概述
这两天遇到一个奇怪的问题:在一个进程中,通过GetFileVersionInfo去获取一个绝对路径文件的版本号时, 实际路径对应文件根本不存在,却获取到了版本号信息。在仔细分析GetFileVersionInfo内部实现后,真相终于大白,为了以后能更好的使用这个API,故把分析过程记录下来。
问题背景
一进程在判断是否需要打补丁时,需要去读取文件的版本号,读取路径是从一个库中配置的。在测试的库中,配置的条件是判断C:\Windows\system32\gdiplus.dll的版本号小于某个补丁,就说明用户机器上面需要安装该补丁。
该进程运行后,分析到库的条件时,就会通过GetFileVersionIno获取C:\Windows\system32\gdiplus.dll模块的版本号来进行判断, 实际上在C:\Windows\system32\目录下面是不存在gdiplus.dll文件,但是GetFileVersionInfo这个函数却返回了成功,而且可以通过VerQueryValue能够查询出版本号, 从而导致扫描出的结果不符合预期。
为什么GetFileVersionInfo获取不存在路径的版本号信息会出错呢?
查找原因
查看msdn,GetFileVersionInfo:
GetFileVersionInfo function
Retrieves version information for the specified file.
Syntax
BOOL WINAPI GetFileVersionInfo(
_In_ LPCTSTR lptstrFilename,
_Reserved_ DWORD dwHandle,
_In_ DWORD dwLen,
_Out_ LPVOID lpData
);
Parameters
lptstrFilename [in]
Type: LPCTSTR
The name of the file. If a full path is not specified, the function uses the search sequence specified by the LoadLibrary function.
从msdn 来看,如果输入不是全路径,则会通过LoadLibrary去搜索标识的文件名。
进一步查看GetFileVersionInfo内部实现:
BOOL
APIENTRY
GetFileVersionInfoA(
LPSTR lpstrFilename,
DWORD dwHandle,
DWORD dwLen,
LPVOID lpData
)
{
UNICODE_STRING FileName;
ANSI_STRING AnsiString;
NTSTATUS Status;
BOOL bStatus;
RtlInitAnsiString(&AnsiString, lpstrFilename);
Status = RtlAnsiStringToUnicodeString(&FileName, &AnsiString, TRUE);
if (!NT_SUCCESS(Status)) {
SetLastError(Status);
return FALSE;
}
<span style