Reputation: 475
I have an old 32-bit installer program that installs some 32-bit dependency DLLs into the Windows system folder. I have discovered that it's failing to install some of the 32-bit DLLs on a 64-bit system because the SysWOW redirection is doing something that I don't understand.
The installer program has relied on Windows API function GetFileVersionInfo to indicate if the DLL is already present with a newer version number. However, I am now seeing a case where file MSVCR100.DLL is already present in the System32 folder, but not in the SysWOW64 folder. When GetFileVersionInfo is used to test C:\Windows\System32\MSVCR100.DLL, I expect it to redirect to C:\Windows\SysWOW64\MSVCR100.DLL. It seems that if the file doesn't exist in SysWOW64, then it looks in System32 as a fallback. Thus, the installer thinks MSVCR100.DLL is already there and fails to install it.
I created a C++ Win32 Console app to test this. The entirety of the code is:
int _tmain(int argc, _TCHAR* argv[])
{
char sysDirName[64], sysWow64DirName[64];
char fileName[256];
strcpy_s(fileName, argv[1]);
GetSystemDirectory((LPSTR)sysDirName, 256);
GetSystemWow64Directory((LPSTR)sysWow64DirName, 256);
test_file(sysDirName, fileName);
test_file(sysWow64DirName, fileName);
return 0;
}
void test_file(char *dir, char* fileName)
{
char filePath[256];
DWORD verInfoSize, tempDWORD;
BOOL found;
byte buff[8192];
PathCombine((LPSTR)filePath, (LPSTR)dir, (LPSTR)fileName);
verInfoSize = GetFileVersionInfoSize((LPSTR)filePath, &tempDWORD);
found = GetFileVersionInfo((LPSTR)filePath, 0, verInfoSize, buff);
if (found)
printf("%s --found\n", filePath);
else
printf("%s --NOT found\n", filePath);
}
I tested it on 3 different 64-bit computers, including Win 10, Win 7, and I get the same results.
If MSVCR100.DLL is in SysWOW64 but not in System32, then my test shows redirection working as expected:
>testSysFile msvcr100.dll
C:\WINDOWS\system32\msvcr100.dll --found
C:\WINDOWS\SysWOW64\msvcr100.dll --found
If MSVCR100.DLL is in neither System32 nor SysWOW64, then the result is expected:
>testSysFile msvcr100.dll
C:\WINDOWS\system32\msvcr100.dll --NOT found
C:\WINDOWS\SysWOW64\msvcr100.dll --NOT found
If MSVCR100.DLL is in System32 but not SysWOW64, then the result shows something unexpected and unhelpful:
>testSysFile msvcr100.dll
C:\WINDOWS\system32\msvcr100.dll --found
C:\WINDOWS\SysWOW64\msvcr100.dll --NOT found
Web searching has shown me lots of information about the SysWOW redirection, but I couldn't find any documentation or discussion of this behavior. Is this really what I should expect? My tests are also showing that if I use API function GetSystemWow64Directory I can have a file path that doesn't rely on redirection. Would it be safe to just copy DLLs and register them at that path instead?
Upvotes: 2
Views: 549
Reputation: 101656
GetFileVersionInfo*
uses LoadLibraryEx
to do its work by loading the file as a data file.
For some reason KERNELBASE!BasepLoadLibraryAsDataFile
inside LoadLibraryW
calls ntdll!RtlWow64EnableFsRedirectionEx
to disable the redirection and if the requested file is inside "%WinDir%\System32" then it tries to load the file again, this time from the "real" system32 directory.
This is clearly by design and I can't think of a way around it that is not a giant hack. I assume they do this for compatibility reasons.
You can however detect it with something like this:
bool validFile = !(GetFileAttributes(filePath) & FILE_ATTRIBUTE_DIRECTORY);
bool falsePositive = gotversioninfo && !validFile;
Upvotes: 2