怀德维宁

大邦维屏,大宗维翰。怀德维宁,宗子维城。

0%

_access函数32bit和64bit编译差异

前言

最近,在进行软件开发工作中遇到了一个问题,觉得比较有趣,在此进行以下记录。

问题比较简单,需要检查windows系统下的C:\Windows\System32路径下是否存在特定的用户文件(dll格式)。在这里使用_access函数(C运行库函数)对文件进行检查,该函数主要用于检查文件或目录是否存在及其对应的读写权限。但是开发完成后,在自检过程中却发现使用_access函数无法达到预期,主要表现为文件已存在,但是函数返回结果却表明文件不存在。如果换用win32 API函数PathFileExistsA却正常,文件存在于对应目录。本文即对_access函数的异常进行分析。

分析过程

win32函数访问没问题,而使用C语言库函数存在问题,即系统调用没问题,主要问题应该出现在C语言库上。而最简单的修改项就是编译环境修改,之前为了兼容性考虑,代码选择的编译环境为32位,因此将编译环境改为64位。此时,_access函数正常执行,返回了符合预期的结果。

因此,针对这一点进行分析,最终在微软官网上找到相关说明,链接如下:https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?from_wecom=1

微软在64位操作系统上,为64位应用程序保留了%windir%\System32(通常就是C:\Windows\System32)路径。因为通常情况下,32位dll和64位dll的文件名称完全相同,因此64位dll也保存在System32路径下,而32位dll则被保存在其他路径。这就使得64位操作系统下的32位应用程序访问对应dll时,直接访问%windir%\System32路径是错误的,因此微软为64位系统下的32位程序通过访问%windir%\System32路径做了重定向。而32位要想真正访问%windir%\System32路径,就可以使用%windir%\Sysnative路径替代,也可以使用Wow64DisableWow64FsRedirection函数禁用文件重定向功能。

示例程序:

#include  <io.h>
#include  <stdio.h>
#include  <stdlib.h>
#include <Windows.h>
#include <iostream>
#include "Shlwapi.h"
#include <errhandlingapi.h >

int main( void )
{
    char *file_path = "C:\\Windows\\System32\\test.dll";
    char *file_path1 = "C:\\Windows\\Sysnative\\test.dll";
    // Check for existence.
    printf_s( "1.access system32.\n");
    if( (_access( file_path, 0 )) != -1 )
    {
        printf_s( "File %s is exists.\n", file_path);
    }
    else
    {
        printf_s( "File %s isn't exists.\n", file_path);
    }
    printf_s( "2.access sysnative.\n");
    if( (_access( file_path1, 0 )) != -1 )
    {
        printf_s( "File %s is exists.\n", file_path1);
    }
    else
    {
        printf_s( "File %s isn't exists.\n", file_path1);
    }
    PVOID OldValue = NULL;

    //  Disable redirection immediately prior to the native API function call.
    printf_s( "3.Disable Redirection.\n");
    bool flag = Wow64DisableWow64FsRedirection(&OldValue);
    printf("Wow64DisableWow64FsRedirection return value is %d.\n", flag);
    if (!flag)
    {
        DWORD dw = GetLastError();
        printf("Errorcode is %d.\n", dw);
    }
    if( (_access( file_path, 0 )) != -1 )
    {
        printf_s( "File %s is exists.\n", file_path);
    }
    else
    {
        printf_s( "File %s isn't exists.\n", file_path);
    }

    return 0;
}

在32位编译环境下输出如下:

1.access system32.
File C:\Windows\System32\test.dll isn't exists.
2.access sysnative.
File C:\Windows\Sysnative\test.dll is exists.
3.Disable Redirection.
Wow64DisableWow64FsRedirection return value is 1.
File C:\Windows\System32\test.dll is exists.

(1)C:\Windows\System32路径无法直接访问;

(2)C:\Windows\Sysnative路径会被重定位到C:\Windows\System32;

(3)Wow64DisableWow64FsRedirection函数返回1代表函数正常运行,此后再使用C:\Windows\System32路径,则无重定向操作,获取到了正确的结果;

在64位编译环境下输出如下:

1.access system32.
File C:\Windows\System32\test.dll is exists.
2.access sysnative.
File C:\Windows\Sysnative\test.dll isn't exists.
3.Disable Redirection.
Wow64DisableWow64FsRedirection return value is 0.
Errorcode is 1.
File C:\Windows\System32\test.dll is exists.

(1)C:\Windows\System32路径可以直接访问;

(2)C:\Windows\Sysnative路径不会被重定位到C:\Windows\System32;

(3)Wow64DisableWow64FsRedirection函数返回0代表函数异常运行,但不影响后续使用C:\Windows\System32路径获取正确的结果;

结论

_access函数在32bit和64bit编译环境下输出结果存在差异,主要原因即为64位windows操作系统下,%windir%\System32被保留给了对应的64位应用程序,而32位应用程序访问%windir%\System32路径时就会被重定向到系统指定的其他路径上。而使用32bit编译环境最终生成的是32位应用程序,因此对应路径被重定向,直接访问%windir%\System32路径下的文件时就会出现异常。

因此建议在64位操作系统中使用64位的应用程序,而非32位的应用程序,虽然64位操作系统兼容32位应用程序,而32位操作系统不兼容64位应用程序,因此32位具有更广泛的兼容性。

如果不确定目标程序运行环境或程序存在运行于两种系统的使用场景,则在使用文件访问相关函数时:

(1)直接使用win32函数PathFileExistsA;

(2)使用_access函数前,先使用Wow64DisableWow64FsRedirection函数禁用文件重定向功能;

参考链接

(1)https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?from_wecom=1
(2)https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-wow64disablewow64fsredirection?from_wecom=1
(3)https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=msvc-170&from_wecom=1
(4)https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathfileexistsa?from_wecom=1