Catching LoadLibrary() errors gracefully - c++

I'm working on a piece of C++ software which runs on all Windows versions between Windows XP and Windows Vista. In my code, I developed a DLL which links against a standard library (the Qt library). Once my software is deployed, it's not unusual that the user doesn't have the exact same Qt build on his system but a slightly different configuration. There might be features disabled (so their Qt build doesn't export the same set of symbols) or the library might even be changed in ways which make the library binary incompatible to the original.
At some point, I'm loading my DLL via a LoadLibrary() call. This pulls in whatever Qt library is on the user's system. If I'm lucky, their Qt build is compatible with what I used while developing my DLLs, so LoadLibrary() succeeds. However, depending on the changes they did to their Qt build, the LoadLibrary() call sometimes fails with
"The specified Module could not be found."; this usually happens if their Qt build consists of less DLLs than my Qt build. So my DLL attempts to load e.g. QtFoo.dll but since this dll is not part of their Qt build, loading my DLL fails.
"The specified Procedure could not be found."; this usually happens if they changes their Qt build so that certain features are disabled, which results in less exported symbols.
My question is: how can I catch these errors gracefully? Right I'm simply using GetLastError() and then print either of the above two messages. However, it would be much more useful if I knew which module could not be found, or which procedure is missing. I noticed that when running an application in the explorer which links against a missing DLL, explorer manages to yield a nice 'The application foo could not be loaded since the required library blah.dll is missing'. Is there maybe some API available to get more information about why a LoadLibrary() call failed exactly?

Short of attaching a debugger to your process, I don't think you can. The message that typically pops up when this happens is generated internally by LoadLibrary. SetErrorMode is used in a lot of apps to inhibit messages of this form, I'm gessing that somewhere in your apps framework, its calling SetErrorMode to inhibit the OS message.
The only app ive seen generate its own verbose messages about dll load failures is MS DevStudio - which is attached as a debugger and so has access to a special stream of debug events.

At some point, I'm loading my DLL via
a LoadLibrary() call. This pulls in
whatever Qt library is on the user's
system.
Don't do this! The kind of errors you have are the lucky kind, just as easily it could corrupt memory and crash.
The canonical way of shipping a Qt application is either shipping the DLLs or linking statically. Check out the Qt deployment guide in the help files.
Later edit:
After reading your comments, I still do not recommend that you use this approach, since you can't be sure that the DLLs are binary compatible even if they load, which could lead to hard to track errors.
Nevertheless, I believe that you could intercept the LoadLibrary calls and see which ones fail. The MS Detours library can be used for this. Also see this Stackoverflow question.

To expand jeffamaphone's answer, you can try retrieving the file version details before calling LoadLibrary. You can do this using the following function:
BOOL GetFileDetails(LPCTSTR lpszPath, LPDWORD lpMajorVersion,
LPDWORD lpMinorVersion)
{
DWORD dwVersionHandle;
DWORD dwVersionSize = GetFileVersionInfoSize((LPTSTR)lpszPath,
&dwVersionHandle);
if (dwVersionSize == 0)
return FALSE;
LPBYTE lpVersion = new BYTE[dwVersionSize];
if (!GetFileVersionInfo((LPTSTR)lpszPath, dwVersionHandle,
dwVersionSize, lpVersion))
{
delete [] lpVersion;
return FALSE;
}
VS_FIXEDFILEINFO *pVersionInfo = NULL;
UINT nLength;
if (!VerQueryValue(lpVersion, _T("\\"), (LPVOID *)&pVersionInfo, &nLength))
{
delete [] lpVersion;
return FALSE;
}
*lpMajorVersion = pVersionInfo->dwFileVersionMS;
*lpMinorVersion = pVersionInfo->dwFileVersionLS;
return TRUE;
}
You can then check the major/minor version numbers against ones you're expecting.

Can MapAndLoad from ImageHLP.DLL may help. It returns a LOADED_IMAGE structure.

Can you be more proactive and check the version of the QT binaries you need before you call LoadLibrary()? Then you can just warn your user they don't have the version your app needs, and maybe even provide a link to the install point for them.

You can also have windows check for this by using a manifest file. This file contains the information on the requirements on the used libraries' versions. More accurate and complete info is on the msdn site.
Take a look at the answer to this question about how to use LoadLibrary with a manifest file.
The Qt documentation briefly mentions the usage of a manifest file for VS2005; for earlier versions you would have to create it for your own.

Related

How can I control search order for DLLs to avoid hijacking?

As a background: my application requires:
admin privileges
access to WinAPI DLLs
be able to run on all OSs: Win7-Win10
Normally, to use API, I can just link required *.lib files. However it uses default search order, that means (according to https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order) it firstly loads DLLs from "The directory from which the application loaded."
As a result, if the DLL exists in the same directory, running my app by double-click loads also that DLL.
I want to look for DLLs only in system directories (similarly to https://stackoverflow.com/a/46182665/9015013 ).
I know I can try to create some kind of proxy, like
BOOL WinAPIFunction(WinAPIType param) {
return reinterpret_cast<decltype(&WinAPIFunction)>(
reinterpret_cast<void*>(GetProcAddress(manually_loaded_module, "WinAPIFunction")))(param);
}
But it is hard to maintain all these functions. Is there any better method to force windows to look only in system32? I thought about manifest file but it requires version for each DLL that can break "capability" requirement (DLLs have different version for Win7 and Win10)
The solution is posted by #Eryk Sun in comment above.
It is sufficient to add all DLLs not listed in known dlls to delayed loaded libraries and call SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32); at the beginning of the WinMain.
Thanks
You can try to use "Known DLLs" feature: If Windows "knows" that DLL, Windows doesn't search dll file. Known feature is described into link, you written in question.

Is it possible on windows to prevent other applications hooking in system DLLs

I am desperately looking for a cause of crashes in my Qt-based Application.
After some observation I've detected, that alone opening a QFileDialog, which is standard windows file dialog, even without selecting any file, causes the application to crash after some minutes. It doesn't happen on all machines.
I've opened my application in dependency walker and the profiling revealed, that opening of file dialog loads tons of DLLs, which I don't need in my application - all the tools which hooked in windows shell. Among the others - TortoiseSVN, which even makes depends to freeze.
Is it possible in an application context to prevent other DLLs like codecs or shell-hooks to be loaded?
Is it at least possible to create a QFileDialog without loading all the tool hooked in windows?
This is definitely possible, but it's not trivial. What you have to do is insert an API hook on LoadLibrary (and/or the Native API equivalent.) When your hook is called, you can examine the DLL filename and decide whether you want to pass it along to the real LoadLibrary or return an error.
A couple places to find more info on API hooks:
A tutorial on CodeProject
Microsoft Detours is Microsoft's library for API hooking.
Now all of that said, for your specific situation you may be better off just changing your TortoiseSVN settings. If you set the include/exclude paths in Tortoise to only look at directories on your computer that contain SVN repos, I bet this freeze will go away as long as you avoid those directories.

Show an (custom) error when "required" runtime libraries are not present?

I've been making a program in Visual Studio 2012, what comes with it is that when I send my application to someone, they need the VS2012 Runtime, which sometimes they don't know where to download or what they need (for normal users "xxx.dll is missing" is very misleading).
I know exactly which dependencies my application requires (fantom.dll [Lego Mindstorms stuff] and the VC++ 2012 Redist).
I would like to show a dialog when these libraries are missing on application startup and provide the user with download links for these libraries.
Is this possible to accomplish?
Yeah you could do something like:
Move all of the code in your binary into a DLL.
Create an EXE which dynamically loads the DLL using LoadLibrary and unloads it with FreeLibrary.
If LoadLibrary fails, check if its due to missing DLLs, if so then display a MessageBox/your custom message and exit.
Of course this means your EXE project must NOT depend on the runtime itself - this shouldn't be an issue since you'll only need to call 3 win32 API's.
No it's not possible but you can create an installer for your program. The error is thrown during the loading of your program, before your code execution...
You can try with that : http://www.codeproject.com/Articles/24187/Creating-an-Installer
I can't speak for testing the VS2012 Runtime dynamically, but you can certainly validate fantom.dll dynamically. Instead of static-linking to the DLL directly, you can dynamically load it instead. You can configure your project to delay-load the DLL at run-time, and then provide a delay-load callback handler that the RTL will call if the delay-load fails. Or you can simply skip the delay-load feature and load the DLL yourself manually by calling LoadLibrary() and GetProcAddress() directly.
Sure, you can verify if a dependency exists on the deployed system. A few things come to mind...
You can see if the assembly is recognized on the running system by calling AppDomain.AssemblyResolve() . Further reading here
Another more primitive option is to call a File.Exists(your assembly path here) test, but I would advise against this as it's a bad practice to require hard-pathed installation locations.
That said, and as others have stated, it's still by far the best approach to create yourself an installation distribution.

can not load a DLL file

Every time I try to load DLL (COM) I get the following error:
LDR: LdrpWalkImportDescriptor() failed
to probe for its manifest,
ntstatus 0xc0150002
I searched a lot but found nothing.
Please I really believe in this site experts and I hope to get a solution to this problem.
I know that the problem is from manifest file but I really don't know what is it and how to fix this
I use VS2010 C++ and LoadLibrary
This is the description of the error code from ntstatus.h:
//
// MessageId: STATUS_SXS_CANT_GEN_ACTCTX
//
// MessageText:
//
// Windows was not able to process the application binding information.
// Please refer to your System Event Log for further information.
//
#define STATUS_SXS_CANT_GEN_ACTCTX ((NTSTATUS)0xC0150002L)
Look in the Windows event log for further information. A very common mishap is that the CRT runtime library that the DLL needs is not installed in your machine. Post what you see there in your question if that doesn't help. Or contact the COM component vendor or author for support, best way.
Com dlls are not loaded with loadlibrary. The system loads them itself when you do CoCreateInstance for object implemented in library.
The problem seems to be that dlls on what the com dll depends are missing.
The first and second links that Google produces for this error both suggest it is commonly caused by runtime library versioning. Both predate VS2010, but I would start by looking for a similar problem. Have you tried duplicating the analysis steps described in that first post?
(Is the DLL something that you have created? If not, maybe it actually was built with VS2008 and corresponds exactly to that post? If so, maybe you can try the compilation tweaks recommended there.)

Visual Studio Debuging Errors in C++

For some reason the integrated debugger is causing an error as soon as I make reference to a third party vendor's dll class. This same code runs when it is built and ran as a release, stand alone. The two properties for debug and release should be the same as I have not really altered them. I added the lib file to the path for both builds. I simply have:
ClassNameFromDll blah;
When it gets to here, I get this exception:
Unhandled exception at 0x78a3f623 (mfc90ud.dll) in MTGO SO Bot.exe:
0xC0000005: Access violation reading location 0xf78e4568.
It occurs in: afxtls.cpp, line 252.
This is an MFC app, but I am not really using any MFC other than a very simple gui which fires off an event that is all win32. I am using Visual Studio 2008 Express.
Looking at the atltls.cpp file from my VC9 install, the crash is occurring here:
inline void* CThreadSlotData::GetThreadValue(int nSlot)
{
EnterCriticalSection(&m_sect);
ASSERT(nSlot != 0 && nSlot < m_nMax);
ASSERT(m_pSlotData != NULL);
ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED); // <== crash
// ...
}
So the reason the crash doesn't occur in release build is because the ASSERT() is a no-op in that build. I'm not familiar with ATL's use of thread local storage, but this assertion indicates that something is asking for a value in a slot where nothing has been stored yet.
Whether the initialization of that TLS slot is your responsibility or the 3rd party DLL's responsibility, I don't know.
It looks like GetThreadValue() has some additional protections such that it'll return a NULL pointer in the release build for an uninitialized slot (though I'm not sure that this would be guaranteed) - I'd bet that the 3rd party DLL relies on that behavior (ie., it checks for a NULL return) so no crash occurs in release builds. Note that the vendor might be using the CThreadSlotData class indirectly (the stack trace would give a clue about this), so they might not be aware of its expectations.
engaging psychic debugging
The fact that it runs in release mode fine and crashes in debug mode leads me to believe that you've somehow managed to reference, specifically, the release version of that DLL (mfc90u.dll), rather than referencing the library itself and allowing the linker to decide which version to import.
You may not be using MFC for anything in this app, but if it's building as an MFC application, you will get all of the MFC stuff whether you want it or not (which means you also have to solve the MFC dependency problem and ship the MFC DLLs with your app).
Do you have a stack trace you can post? It might have some helpful information.
If the 3rd party DLL is still actively supported by the vendor, then the first thing you should do is see if you can have the same problem occur with a very simple program that you can send to the vendor and ask them to fix it.
If the vendor is not available or responsive enough:
If you have source of the 3rd party DLL and can easily build your own version, you have probably the best way to debug this (short of getting the vendor to support you). Even if you cannot easily build a source-debuggable DLL, you can trace into the constructor's assembly instructions and use the source as a map to help you understand what's going on.
Even if you don't have source for the 3rd party DLL then I think the best course of action is to trace through the constructor for ClassNameFromDll to try to figure out whats going wrong. It might help to compare the instructions path in the Debug build vs. the Release build.
MFC source is distributed with MSVC (probably not with the Express version, but I think with all other versions) so when you get in to the MFC DLL's code you might find the source to be useful in helping to figure out what's going on.