Windows服务:Go可执行文件能访问指定目录,C可执行文件无法访问
我有一个运行在Local System账户下的Windows服务,该服务用Go语言编写,通过exec.Command()执行两个.exe文件:一个由Go编写,另一个由C编写。两者都需要访问C:\Windows\system32\config\systemprofile\sandbox\目录,Go可执行文件能正常访问该目录,但C可执行文件无法访问,且该目录在访问尝试的前后均存在。
服务代码(myservice.exe)
exec.Command("go_access_dir.exe") // 可正常访问目标目录 exec.Command("c_access_dir.exe") // 无法访问目标目录
Go程序(go_access_dir.go)及运行结果
func main(){ // 创建日志文件 f, err := os.Create("go-main.log") if err != nil { log.Fatal(err) } defer f.Close() fmt.Println("-- begin main") currentUser, err__ := user.Current() if err__ != nil { // log.Fatalf(err__.Error()) } homedir := currentUser.HomeDir sandbox_dir := homedir + sandboxDir _, err = f.WriteString("sandbox_dir ="+sandbox_dir + "\n") if err != nil { log.Fatal(err) } file, err := os.Open(sandbox_dir) if err != nil { _, err = f.WriteString("failed opening directory ="+sandbox_dir + " error "+ err.Error()+"\n") if err != nil { log.Fatal(err) } } defer file.Close() list,_ := file.Readdirnames(0) // 0表示读取所有文件和文件夹 for _, name := range list { fmt.Println(name) _, err = f.WriteString(name + "\n") if err != nil { log.Fatal(err) } } }
运行日志:
sandbox_dir =C:\Windows\system32\config\systemprofile\sandbox\ folder1 folder2 test1.doc test10.doc
C程序(c_access_dir.c)及运行结果
FILE *fdout = fopen("list_dir.log","w"); DIR* directory = NULL; if ((directory = opendir(dir_name)) == NULL) { fprintf(fdout, "Can't open |%s|\n", dir_name); fprintf(fdout, "error: %s\n", strerror(errno)); return; }else{ fprintf(fdout, "|%s| opens fine\n", dir_name); DIR *dp; struct dirent *ep; dp = opendir (dir_name); if (dp != NULL) { while (ep = readdir (dp)){ fprintf(fdout, "|%s| \n", ep->d_name); } (void) closedir (dp); } } fclose(fdout);
运行日志:
Can't open |C:\Windows\system32\config\systemprofile\sandbox\| error: No such file or directory
注:"C:\\Windows\\Temp\\sandbox\\"目录可被opendir正常访问
尝试Windows原生API(FindFirstFile)及结果
HANDLE fileHandle; WIN32_FIND_DATA FindFileData; LARGE_INTEGER szDir; WIN32_FIND_DATA fileData; DWORD dwError=0; char *this_file_name = "C:\\Windows\\system32\\config\\systemprofile\\sandbox\\*"; fprintf(fdout, "1193 working with this_file_name %s \n", this_file_name); fprintf(fdout, "findfirstfile\n"); fileHandle = FindFirstFileA(this_file_name, &FindFileData); if (INVALID_HANDLE_VALUE == fileHandle) { fprintf(fdout, "Invalid File Handle Value \n"); dwError = GetLastError(); fprintf(fdout, "FindFirstFile error %d\n",dwError); } do { fprintf(fdout, "%s\n", FindFileData.cFileName); } while (FindNextFile(fileHandle, &FindFileData) != 0);
运行日志:
. .. folder1
注:目标目录根目录及folder1下存在文件,且Go程序可正常访问
核心原因:WOW64文件系统重定向
Local System账户下运行的服务,若C程序是32位编译的,访问C:\Windows\system32时会被Windows自动重定向到C:\Windows\SysWOW64目录,而SysWOW64\config\systemprofile\sandbox不存在,因此触发“找不到文件或目录”的错误。Go程序默认编译为64位(对应64位Windows主机),不会触发重定向,所以能正常访问目标路径。
验证方法
右键点击c_access_dir.exe → 属性 → 详细信息,查看“目标类型”是否为32 位,以此确认程序位数。
解决方案
方案1:编译64位C程序
编译C代码时指定目标架构为x64:
- 使用MSVC编译器时,在项目设置中选择
x64平台; - 使用GCC编译器时,添加
-m64参数。
64位程序不会触发WOW64重定向,可直接访问C:\Windows\system32下的目录。
方案2:禁用WOW64重定向(适用于必须用32位程序的场景)
在C代码中调用Windows API临时禁用文件系统重定向:
#include <windows.h> int main() { PVOID oldValue = NULL; // 禁用文件系统重定向 if (!Wow64DisableWow64FsRedirection(&oldValue)) { // 处理错误,如记录日志 } // 执行目录访问操作(opendir/FindFirstFile等) // 恢复重定向,避免影响后续操作 Wow64RevertWow64FsRedirection(oldValue); return 0; }
方案3:直接访问真实路径(兼容性较差,不推荐)
将路径改为C:\Windows\Sysnative\config\systemprofile\sandbox\,32位程序访问Sysnative时会直接映射到64位系统的system32目录。
内容的提问来源于stack exchange,提问作者stubble jumper

