Edit
Attach
Printable
topic end
<!-- * Set TOPICTITLE = Claus Brod: Doppelgänger modules, or The Curse of <nop>EightPlusThree (7 Jan 2006) --> <style type="text/css"> pre {background-color:#ffeecc;} </style> %STARTINCLUDE% #DoppelgaengerBug <a name="7"></a> ---+++ [[BlogOnSoftware20060107][Doppelgänger modules, or The Curse of <nop>EightPlusThree]] (7.1.2006) Windows, even in its latest incarnations, still exhibits quite a bit of quirky behavior which is due to its DOS roots, or at least due to the attempt to remain compatible with code which was created for DOS. Most of the time, I am not even surprised anymore when I come across 16-bit limitations or similar reminiscences of the past. But sometimes, I only become aware of them when my code crashes. This happened some time ago with an application I am working on. When I started the app in a certain way, it would simply crash very early during startup. It took a while to break this down into the following trivial code example which consists of a main executable and a DLL which is loaded into the executable via [[http://msdn.microsoft.com/library/en-us/dllproc/base/loadlibrary.asp][LoadLibrary]], i.e. dynamically. Here is the code for the main executable, =SampleApp.cpp=: <verbatim> #include <stdio.h> #include <conio.h> #include <windows.h> #include <psapi.h> static void EnumModules(const char *msg) { printf("\n==========================================================\n"); printf("List of modules in the current process %s:\n", msg); HMODULE hMods[1024]; DWORD cbNeeded; HANDLE hProcess = GetCurrentProcess(); // inquire modules loaded into process if( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { // print name and handle for each module for ( unsigned int i = 0; i < (cbNeeded/sizeof(HMODULE)); i++ ) { char szModName[MAX_PATH]; if ( GetModuleFileNameEx( hProcess, hMods[i], szModName, sizeof(szModName))) { printf(" %s (0x%08X)\n", szModName, hMods[i] ); } } } CloseHandle( hProcess ); } extern "C" __declspec(dllexport) int functionInExe(void) { printf("Now in functionInExe()\n"); return 42; } int main(void) { EnumModules("before loading DLL"); HMODULE hmod = LoadLibrary("SampleDLL.dll"); EnumModules("after loading DLL"); printf("\nPress key to exit.\n"); _getch(); return 0; } </verbatim> This code loads a DLL called =SampleDLL.dll=. Before and after loading the DLL, it enumerates the modules which are currently loaded into the process; this is only to demonstrate the effect which led to the crash in the other app I was working on. =SampleDLL.dll= is built from this code (=SampleDLL.cpp=): <pre> extern "C" __declspec(dllimport) int functionInExe(void); extern "C" __declspec(dllexport) void gazonk(void) { int i = functionInExe(); } </pre> The main executable exports a function called =functionInExe=, and the DLL calls this function, and so it has an explicit reference to the main executable which the linker needs to resolve. This is an important piece of the puzzle. And here is a simple =makefile= which shows how to build the two modules: <pre> all: SampleApplication.exe SampleDLL.dll clean: del *.obj *.exe *.dll SampleApplication.exe: SampleApp.obj link /debug /out:SampleApplication.exe SampleApp.obj psapi.lib SampleApp.obj: SampleApp.cpp cl /Zi /c SampleApp.cpp SampleDLL.dll: SampleDLL.obj link /debug /dll /out:SampleDLL.dll SampleDLL.obj SampleApplication.lib SampleDLL.obj: SampleDLL.cpp cl /Zi /c SampleDLL.cpp </pre> Let's assume that the above files (=SampleApp.cpp=, =SampleDLL.cpp= and =makefile=) are all in a directory =c:\temp\dupemod=, and that we built the code by running =nmake= in that directory. Now let's run the code as shown in the screenshots below. <table border="0"> <tr> <th>Long filename</th> <th>Short filename</th> </tr> <tr> <td valign="top"> <img src="%ATTACHURL%/startdupemod.jpg" /> <br> <img src="%ATTACHURL%/dupemod_ok.png" /> </td> <td valign="top"> <img src="%ATTACHURL%/startdupemod_short.jpg" /> <br> <img src="%ATTACHURL%/dupemod.png" /> </td> </tr> </table> Take a close look at the command shell window on the right: After loading the DLL, the process maps both =c:\temp\dupemod\Sample~1.exe= and =c:\temp\dupemod\SampleApplication.exe= into its address space. Both refer, of course, to the same file, which means that we have loaded the executable twice! This happens only if we run the executable using its 8+3 DOS name, i.e. =Sample~1.exe=. When run with a long filename, everything works as expected. So when we load =SampleDLL.dll=, the OS loader tries to resolve the references which this DLL makes to other modules. One of those modules is =SampleApplication.exe=. The OS loader _should_ be able to map this reference to the instance of the executable which is already mapped into the address space. However, it seems that the OS loader cannot figure out that =Sample~1.exe= and =SampleApplication.exe= are actually the same file, and therefore loads another instance of the executable! BTW, this happens both on Windows 2000 and Windows XP systems. In this trivial example, the only damage done is probably just that the main executable consumes twice the virtual address space. In a large application, the consequences can be more severe, and in our case they were. Microsoft also documents some effects of this issue in Knowledge Base articles, for example <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;218475">KB218475</a> and <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;193513">KB193513</a>. The only workarounds I see are: * Rename the executable to that it uses a name which fits into the 8+3 format. * Make sure that nobody ever runs the executable using its short name. We basically went with the latter approach - by making sure that end users always run the application by double-clicking shortcuts which contain the full executable name, and by fixing an interesting related bug in ATL which, hopefully, I may have the time and the nerves to describe in more detail one fine day... %COMMENT{type="below" nonotify="on"}% --- %STOPINCLUDE%
to top
End of topic
Skip to action links
|
Back to top
Edit
|
Attach image or document
|
Printable version
|
Raw text
|
Refresh
|
More topic actions
Revisions: | r1.2 |
>
|
r1.1
|
Total page history
|
Backlinks
You are here:
Blog
>
BlogOnSoftware20060107
r1.2 - 29 Jan 2007 - 18:58 -
ClausBrod
to top
Blog
This site
2017
:
12
-
11
-
10
2016
:
10
-
7
-
3
2015
:
11
-
10
-
9
-
4
-
1
2014
:
5
2013
:
9
-
8
-
7
-
6
-
5
2012
:
2
-
10
2011
:
1
-
8
-
9
-
10
-
12
2010
:
11
-
10
-
9
-
4
2009
:
11
-
9
-
8
-
7
-
6
-
5
-
4
-
3
2008
:
5
-
4
-
3
-
1
2007:
12
-
8
-
7
-
6
-
5
-
4
-
3
-
1
2006:
4
-
3
-
2
-
1
2005:
12
-
6
-
5
-
4
2004:
12
-
11
-
10
C++
CoCreate Modeling
COM & .NET
Java
Mac
Lisp
OpenSource
Scripting
Windows
Stuff
Changes
Index
Search
Maintenance
Impressum
Datenschutzerklärung
Home
Webs
Atari
Blog
Claus
CoCreateModeling
Klassentreffen
Main
Sandbox
Sommelier
TWiki
Xplm
Jump:
Copyright © 1999-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki?
Send feedback