我们很久以前编译过一个使用GetPrivateProfileString的c ++程序。 lpFileName
arg只是ini文件的名称,因此根据文档的预期行为是它应该在Windows目录中查找该文件:
lpFileName [in] 初始化文件的名称。如果是这个参数 不包含文件的完整路径,系统搜索 Windows目录中的文件。
最近程序被重新编译(没有代码更改)并重新部署,导致程序无法再读取ini文件。
使用Process Monitor对两个程序进行比较表明旧程序试图在%HOMEPATH%\ Windows而不是C:\ Windows中查找该文件。
作为测试,我使用Visual Studio 2012编译了以下代码,并在2003服务器上运行它,在命令行上传入一些随机文件名:
void wmain(int argc, wchar_t* argv[])
{
wchar_t *appName = L"xxx";
wchar_t *keyName = L"yyy";
wchar_t *defValue = L"default value";
wchar_t buffer[1024];
DWORD bufferSize = sizeof(buffer);
wchar_t *fileName = argv[1];
DWORD result = GetPrivateProfileString(
appName,
keyName,
defValue,
buffer,
bufferSize,
fileName
);
wprintf(L"result: %d\n", result);
}
使用Process Monitor监视该文件名,我可以看到它在C:\ Windows中查找文件 - 正如预期的那样。但是,当在VS 2003中编译完全相同的代码并在2003服务器上运行时,它会在%HOMEPATH%\ Windows中查找该文件。
返回原始的非重新编译程序。我注意到在Windows 2008服务器上,如果我只是双击exe,它会在%HOMEPATH%\ Windows中查找该文件。但是,如果我右键单击exe并以管理员身份运行,它将在C:\ Windows中查找该文件!但在Windows Server 2003上,它总是在%HOMEPATH%\ Windows中查找。
这与文档不一致!
答案 0 :(得分:2)
这种行为几乎肯定与终端服务器有关。如果安装了终端服务器(稍后重命名为Server 2003及更高版本的SKU中的远程桌面服务),则任何访问系统(Windows)目录的尝试都将重定向到用户本地Windows目录(%HOMEPATH%\Windows
)。
这是在GetWindowsDirectory()文档的“备注”部分中记录的,当您没有指定完整路径并且它回退到16时,可能是GetPrivateProfileString()在引擎盖下使用的部分。 -bit Windows查看系统目录的行为。
如果要将二进制文件与IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
标记链接(在链接器→系统→终端服务器→/TSAWARE:YES
下的项目属性页中设置),则假定您已了解此行为并了解终端服务器与普通客户端计算机不同。因此,它为您提供了实际的Windows目录,而不是私有Windows目录。
否则,如果您与/TSAWARE:NO
链接,则会获得向后兼容性行为,将系统Windows目录的请求重定向到私有用户本地Windows目录。
大概这个标志的默认设置在VS 2003和VS 2012之间改变了一段时间。我不知道具体到什么时候,但现代版本的Visual Studio确实假设你是终端服务器感知的。
Raymond Chen在博客中写过一次:When will GetSystemWindowsDirectory return something different from GetWindowsDirectory?
无论如何,这只是学术上的好奇心。你的程序是错误的,很久你就发现了这种好奇心。应用程序永远不应将数据写入Windows目录,无论是系统Windows目录还是伪用户本地Windows目录。有两种解决方法:
完全停止调用GetPrivateProfileString(),正如David Heffernan建议的那样,使用第三方INI文件解析器。谷歌将出现几个结果。 simpleini看起来是个不错的选择。
如果您尚未准备好进行更改,则至少应指定INI文件的完整路径。这样可以避免在系统目录中转储文件的16位Windows行为,但是你仍然可以接受这个古老API的其他硬度。