You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 18:44:50