Why does Win32 fail a module load if an import could not be resolved?

Date:September 16, 2003 / year-entry #65
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20030916-00/?p=42473
Comments:    6
Summary:Because we tried it the other way and it was much worse. In 16-bit Windows, a module that didn't satisfy all its imports would still load. As long as you didn't call a missing import, you were fine. If you did try to call a missing import, you crashed pretty spectacularly with the dreaded Unrecoverable...

Because we tried it the other way and it was much worse.

In 16-bit Windows, a module that didn't satisfy all its imports would still load. As long as you didn't call a missing import, you were fine. If you did try to call a missing import, you crashed pretty spectacularly with the dreaded Unrecoverable Application Error dialog.

The Win32 folks decided that this was a bad design, because often people would take Fred App, designed for Windows 3.1, and run it on Windows 3.0, and it would run great for about an hour, at which point Fred App would call a function that was available only in Windows 3.1 (like, say, GetSaveFileName) and crash as a result.

So the Win32 folks decided that if an import could not be resolved, the app should fail loading. If the makers of Fred App wanted to run on Windows 3.0 after all, they could indicate this by using GetProcAddress explicitly. Because if you have to call GetProcAddress explicitly, it'll probably occur to you to check the return value.

This issue comes up occasionally when people wish out loud, "Gosh, there should be a way I could mark an import as 'optional' - if it couldn't bind, the load should not fail. It would be the app's responsibility to verify that the bind succeeded before calling it." These people are unwittingly asking for history to repeat itself.

Comments (6)
  1. C-J Berg says:

    A good answer to those asking the question could be "use delay loaded imports" (http://msdn.microsoft.com/library/en-us/vccore/html/vcconlinkersupportfordelayedloadingofdlls.asp)? The programmer must of course still add code for handling loader errors, but the framework for loading the imports on demand is already there, and it’s a smooth one.

  2. Mike says:

    You can do this on Linux and MacOS X (iirc) using weak refs. It’s a useful technique, but requires support from the actual libraries you’re linking against (the symbols must be marked as weak). You can check if the symbol is null before calling it.

    Typically, dlopen/GetProcAddress is a better way to proceed though, even if it is less convenient.

  3. Raymond Chen says:

    I’m not saying that it can’t be done. I was trying to explain why allowing it to be done by default was a bad idea. For example, how many people check for a null before using a weak ref or a delayloaded import? Do they even know or care that the function they are calling is weak/delayloaded? Apparently not, because I’ve seen apps crash due to a failed delayloaded import – we’ve reinvented the Windows 3.1 Unrecoverable Application Error dialog.

  4. C-J Berg says:

    Of course you did not say anything like it couldn’t be done, and I did not for once think that you did not know about it. However, I thought it would be a good thing letting people know about delay-loaded imports, in case they did not already know.

    When it comes to handling loader failures for delay-loaded imports, the linker documentation is very clear stating that your code "must handle errors robustly" and elaborates on how it can be done through exceptions (SEH) or hooks. If used correctly, I even think it’s less error-prone than having a bunch of GetProcAddress-calls scattered throughout your code. After all, many crashes I see on my computers are due to failure of checking return values and dereferencing null pointers. And you wouldn’t use delay-loaded imports indeliberately (using a weak reference is another thing, even another OS).

    By the way, I love reading your retrospects on things like the system tr… uhm, notification area. Please keep them coming! :)

  5. I see the point here, but what about a more pressing issue (at least for me):-

    Why didn’t CoCreateInstance() load a library using LoadLibraryEx() and not LoadLibrary() … it makes it significantly harder to chain DLL loading in a COM object?

  6. If you forward to a function, it still has to exist.

Comments are closed.

*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index