LoadLibraryW() failing to load DLL in System32

Justin G picture Justin G · Aug 30, 2013 · Viewed 10.7k times · Source

I'm trying to load a DLL that was installed with a printer driver in the C:\Windows\System32\ folder with the following code:

LoadLibraryW(L"C:\\Windows\\System32\\MagAPI.dll");

GetLastError() is reporting that "The specified module could not be found". If I move the DLL outside of the System32 folder (C:\SomeFolder\MagAPI.dll for example) then it will load fine so it doesn't seem like it's a problem with the DLL itself. Is there some weird Windows security feature that might be blocking my application from loading it? That's the only thing I can think of but I can't find any definitive answers.

Here's the debug output from ShowSnaps, which shows where it's failing:

1a8c:1fd4 @ 19006756 - LdrLoadDll - ENTER: DLL name: C:\Windows\system32\MagAPI.dll DLL path: C:\Windows\system32;C:\Windows\system;C:\Windows;.;<otherstuff>
1a8c:1fd4 @ 19006756 - LdrpLoadDll - ENTER: DLL name: C:\Windows\system32\MagAPI.dll DLL path: C:\Windows\system32;C:\Windows\system;C:\Windows;.;<otherstuff>
1a8c:1fd4 @ 19006756 - LdrpLoadDll - INFO: Loading DLL C:\Windows\system32\MagAPI.dll from path C:\Windows\system32;C:\Windows\system;C:\Windows;.;<otherstuff>
1a8c:1fd4 @ 19006756 - LdrpFindOrMapDll - ENTER: DLL name: C:\Windows\system32\MagAPI.dll DLL path: C:\Windows\system32;C:\Windows\system;C:\Windows;.;<otherstuff>
1a8c:1fd4 @ 19006756 - LdrpSearchPath - ENTER: DLL name: C:\Windows\system32\MagAPI.dll DLL path: C:\Windows\system32;C:\Windows\system;C:\Windows;.;<otherstuff>
1a8c:1fd4 @ 19006756 - LdrpResolveFileName - ENTER: DLL name: C:\Windows\system32\MagAPI.dll
1a8c:1fd4 @ 19006756 - LdrpResolveFileName - RETURN: Status: 0xc0000135
1a8c:1fd4 @ 19006756 - LdrpSearchPath - RETURN: Status: 0xc0000135
1a8c:1fd4 @ 19006756 - LdrpFindOrMapDll - RETURN: Status: 0xc0000135
1a8c:1fd4 @ 19006756 - LdrpLoadDll - RETURN: Status: 0xc0000135
1a8c:1fd4 @ 19006756 - LdrLoadDll - RETURN: Status: 0xc0000135

Answer

Spire picture Spire · Sep 1, 2013

Since you mention that your application is 32-bit, the DLL that you're loading must also be 32-bit.

The likely reason LoadLibrary() is failing is that you're running a 64-bit edition of Windows, but your printer driver has erroneously installed its 32-bit DLL in question into the System32 folder instead of the SysWOW64 folder.

Some background: On 64-bit editions of Windows, 64-bit DLLs go in System32 folder, and 32-bit DLLs go in the SysWOW64 folder. I know it sounds like it should be the other way around, but don't let the names confuse you; the folders are named like that are for backward-compatibility reasons. The SysWOW64 folder is supposed to be transparent to applications: Windows has a feature called file-system redirection that makes it possible for 32-bit applications to load 32-bit DLLs by specifying the System32 folder (just as they always have) even though the DLLs are actually inside SysWOW64.

On the other hand, LoadLibrary() will simply refuse to load a DLL that's been placed inside the wrong folder. This is the behavior you're probably seeing.

The real solution to your problem is to contact the printer manufacturer and inform them that their driver installer is putting its DLLs into the wrong folder under 64-bit editions of Windows. If they ever fix this, your application will start working correctly without any changes to your existing code.

In the meantime, you should be able to work around your problem by doing this:

  1. Before you attempt to load the DLL, copy the DLL from %windir%\Sysnative to some known folder that you have control over. (I suggest that you create a uniquely named folder inside %TEMP% and copy the DLL into that folder). Note: Sysnative is a special alias that maps to the native system folder, which is System32 under 64-bit editions of Windows.
  2. Call LoadLibrary() and pass it the path of the copy of the DLL that you made in step 1.
  3. When you're done using the DLL, unload it and delete the copy you made in step 1.

The above workaround might not work if you need to support 64-bit Windows XP, since the Sysnative alias is documented to work only on Windows Vista or later. An alternative workaround is to manually bypass file-system redirection:

  1. Temporarily disable file-system redirection by calling Wow64DisableWow64FsRedirection(). This will allow your application to see what's really in System32.
  2. Copy the DLL from System32 to some known folder that you have control over.
  3. Reenable file-system redirection by calling Wow64RevertWow64FsRedirection().
  4. Call LoadLibrary() and pass it the path of the copy of the DLL that you made in step 2.
  5. When you're done using the DLL, unload it and delete the copy you made in step 2.

Note that if you're thinking of calling Wow64DisableWow64FsRedirection() to get LoadLibrary() to work without having to copy the DLL, don't bother: LoadLibrary() doesn't pay attention to that setting. (I actually tested it.)

More information: File System Redirector