5

I am writing a Windows 32 bit program that can use one of multiple possible dlls. So it tries to load each dll in turn, using SysUtils.SafeLoadLibrary and if loading succeeds, it uses that dll.

Unfortunately some of these dlls are statically linked to other dlls. These dlls may be missing from the computer. In that case I get dialog telling me

[myprogram]: [myprogram.exe] System Error

The program can't start because [some dll name] is missing from your computer. Try reinstalling the program to fix this problem."

After pressing the OK button on that dialog, the program gets the error code and tries one of the other dlls which then works fine.

Rather than showing that dialog to the user I want my program to silently ignore it.

How can I accomplish that?

In case it matters: My program is written in Delphi 2007. The Windows version is Windows 8.1, but the program should also work with other Windows versions >= Windows XP.

I have tried SetErrorMode(SEM_FAILCRITICALERRORS) but it did not make any difference.

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
  • See [this question](https://stackoverflow.com/q/44430822/1889329). – IInspectable Jun 13 '17 at 10:13
  • @IInspectable thanks, but there is no answer only one comment. And if I understand that comment correctly I would have to modify the DLLs in order to accomplish that, which I can't. – dummzeuch Jun 13 '17 at 10:16
  • It wasn't clear from your question, whether or not you control the DLLs you attempt to load. – IInspectable Jun 13 '17 at 10:19
  • The problem is that the exception is raised by the DLL itself, and I therefore kind of beyond your control. My (rather Kludgish) solution would be to remove all the DLLs except the ones I am loading directly in my program and see which ones produce errors. I would then modify my program to attempt to load the dependant DLLs (immediately unloading again) and use the results to determine which DLLs I load. Horrible but possible, unless the DLLs are Windows ones. – Dsm Jun 13 '17 at 10:22
  • 4
    `SetErrorMode(SEM_FAILCRITICALERRORS)` should do the trick. Do some debugging to find out if something else is changing the error mode. For instance, are you calling `SafeLoadLibrary` by any chance? Passing zero as the error mode? `SafeLoadLibrary` is a crock of s**t. – David Heffernan Jun 13 '17 at 10:34
  • @DavidHeffernan you are right, that was the problem. Passing SafeLoadLibrary SEM_FAILCRITICALERRORS as the second parameter did the trick. (I never noticed that it has a second parameter.) I am using SafeLoadLibrary to preserve the FPU control word. And I must admit that I failed to mention that important information in my question. – dummzeuch Jun 13 '17 at 11:37
  • I'd throw SafeLoadLibrary away and implement a proper version yourself. Or use LoadLibrary as I do. Why be so worried about DllMain changing fpu control word. That can happen with any external call. So if that's a bother, you need a more general solution in any case. You need to stop calling SetErrorMode apart from that one call you make at the start of the process. – David Heffernan Jun 13 '17 at 11:42
  • I have had some rather bad experiences with DLLs changing the fpu control word in DllMain. It took me forever to track down that problem so I made it a habit to call SafeLoadLibrary instead. That was way back in the late 90ies though, so maybe it's time to review that habit. – dummzeuch Jun 13 '17 at 12:11
  • 3
    Are you going to summarize your comments into an answer, so I can accept it? – dummzeuch Jun 13 '17 at 12:11
  • 1
    Done, didn't see the comment since there was no notification. Interestingly in the x64 RTL, the only work that `SafeLoadLibrary` does is to screw with the error code. I guess that they couldn't face porting the x86 asm and figured that it didn't matter. – David Heffernan Jun 13 '17 at 14:44

1 Answers1

3

SafeLoadLibrary sets the error mode to the value that you provide as an argument, and then restores it after the call to LoadLibrary returns. Most likely you are not supplying a value for that parameter, in which case a default of SEM_NOOPENFILEERRORBOX is passed. In that case it is probably disabling SEM_FAILCRITICALERRORS which would explain the behaviour that you see.

You could solve the problem by passing SEM_FAILCRITICALERRORS every time you call SafeLoadLibrary. Or, perhaps better would be to pass the current error mode. However this is hard to obtain. In Vista and later you can call GetErrorMode. But in older versions you have to do this:

ErrorMode := SetErrorMode(0);
SetErrorMode(ErrorMode);

Because this is a process wide setting, you have a window of opportunity between the two calls to SetErrorMode, for multi-threaded applications to be caught out.

Frankly, I believe that you should call SetErrorMode exactly once in the lifetime of a process, at startup. With that in mind, I would shun SafeLoadLibrary.

If you wish to take advantage of its other function, namely to protect against changes to floating point control state, then you should implement that functionality yourself, in my opinion.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490