2014082100p183-1-
2014082100p183-2-
2014082100p183-3-
2014082100p183-4-	
2014082100p183:5:        Some reasons not to do anything scary in your DllMain, part 3          Raymond Chen        
2014082100p183-6-            
2014082100p183-7-            August 21st, 20140 0
2014082100p183-8-		
2014082100p183-9-        In the same week, the shell team was asked to investigate two failures.
2014082100p183-10- The first one was a deadlock in Explorer. The participating threads look like this:
--
2008091000p20933-14- First, they are intercepting only actions that go through the ShellExecuteEx function. If somebody just calls CreateProcess directly, then the shell execute hooks won’t run, and whoever it is just snuck past your “security” and “auditing” system. 
2008091000p20933-15- Second, these hooks both assume that they are the only shell execute hook in the system. (Remember,  there can be more than one.) Suppose there are three hooks installed, the hook installed by Internet Explorer to handle URLs, the “auditing” hook, and the “security” hook, and suppose that the shell decides to invoke them in that order. (The order in which hooks are executed is unspecified because, if you use them as intended, the order doesn’t matter.) If the user types a URL into the Run dialog, the URL hook launches the Web browser and returns S_OK to say, “I took care of this one.” Your “auditing” hook never got to see the URL, and your “security” hook never got a chance to reject the operation. Hooks that sit ahead of the “security” hook not only get to bypass security, but they also elude the “auditing” hook. 
2008091000p20933-16- So much for “auditing” and “security”. 
2008091000p20933-17- Sidebar 
2008091000p20933-18- Since shell execute hooks effectively become part of the ShellExecute function, they are unwittingly subject to all the application compatibility constraints that ShellExecute must follow. Welcome to the operating system. Your world has just gotten a lot more complicated. 
2008091000p20933:19: There is no “list of application compatibility constraints” (oh how life would be simple if there were); basically, anything that ShellExecute did in the past is a de facto compatibility constraint. Some programs crash if you create a worker thread inside ShellExecute; other programs crash if you call PeekMessage inside ShellExecute. Some programs call ShellExecute and  immediately exit, which means that your shell execute hook had better not return before its work is done (or at least before the work has been given to another process to finish). Some programs call ShellExecute on an unusually small stack, so you have to watch your stack usage. And even though it is called out in the documentation that programs should not call shell functions from inside DllMain, that doesn’t stop people from trying, anyway. Your shell execute hook had better behave in a sane manner when called under such “impossible” conditions. 
2008091000p20933-20-
2008091000p20933-21-        
2008091000p20933-22-                
2008091000p20933-23-
2008091000p20933-24-        
--
2005052305p35573-61-     GetSomething && FreeSomething) {
2005052305p35573-62-  g_hSomething = GetSomething();
2005052305p35573-63- }
2005052305p35573-64- return g_hSomething;
2005052305p35573-65-}
2005052305p35573:66:BOOL CALLBACK DllMain(HINSTANCE hinst,
2005052305p35573-67-      DWORD dwReason, LPVOID lpReserved)
2005052305p35573-68-{
2005052305p35573-69- switch (dwReason) {
2005052305p35573-70- ...
2005052305p35573-71- case DLL_PROCESS_DETACH:
--
2006092007p29663-4-	
2006092007p29663-5-        Don’t forget to unregister your window classes when your DLL shuts down dynamically          Raymond Chen        
2006092007p29663-6-            
2006092007p29663-7-            September 20th, 20060 0
2006092007p29663-8-		
2006092007p29663:9:        If your DLL is unloaded dynamically, you need to make sure you have unregistered your window classes. (You can tell whether the DLL_PROCESS_DETACH is due to a dynamic unload or whether it’s due to process termination by checking the lpReserved parameter to your DllMain function.) If you forget to unregister your window classes, all sorts of bad things can happen:
2006092007p29663-10-First, if you registered any of those classes as a CS_GLOBALCLASS, then people will still be able to create a window of that class by passing its class name to the CreateWindowEx function (or any other function that leads to CreateWindowEx). Since your DLL is no longer in memory, the moment it receives a window message (like, say, WM_NCCREATE), the process will crash since the window procedure has been unloaded. This manifests itself in crashes with the instruction pointer in no-man’s land—these are typically not easy to debug, and the Windows error reports that are generated by these crashes won’t even be assigned to your DLL since your DLL is long gone.
2006092007p29663-11-Second, even if you registered the classes as private classes, you are still committing namespace pollution, leaking the class into a namespace that you no longer own. If another DLL gets loaded at the same base address as your DLL (thereby receiving the same HINSTANCE, it inherits this dirty namespace. If that DLL wants to register its own class that happens to have the same name as the class you leaked, its call to RegisterClassEx will fail with ERROR_CLASS_ALREADY_EXISTS. This typically leads to the DLL failing to initialize or (if the problem is not detected) an attempt to create a window of that class creating instead a window of your leaked class, with a window procedure whose address now resides somewhere in the middle of this new DLL. This is even worse than an instruction pointer in no-man’s land; instead, control goes to a random instruction in the new DLL and probably will manage to execute for a little while before finally keeling over. What’s worse, not only does the crash not get reported against your DLL (which is no longer in memory), but it gets erroneously reported against the new DLL since it is the new DLL’s code that was executing when the crash finally occurred. Congratulations, you just created work for somebody you never met. Those poor victims are going to be scratching their heads trying to figure out how control ended up in the middle of a totally random function with completely nonsense values on the stack and in the registers.
2006092007p29663-12-Third, the namespace you pollute can be your own. Suppose you registered a class as a CS_GLOBALCLASS, then your DLL gets unloaded and you forget to unregister the class. Later, your DLL gets reloaded, but due to changes in the virtual address map, it gets loaded at a new address. Now your DLL attempts to re-register its CS_GLOBALCLASS classes, and the call fails with ERROR_CLASS_ALREADY_EXISTS. If you’re lucky, your DLL detects the error and fails to load, resulting in missing functionality. If you’re unlucky, you fail to detect the error and succeed the load anyway. Then the code that did the LoadLibrary will try to create a window with that class, but instead of getting your DLL’s window class (which failed to register), it gets the window class left over by that first copy of your DLL! Since that DLL no longer exists, you get a crash with the instruction pointer off in no-man’s land.
2006092007p29663-13-This is not a purely theoretical problem. The shell common controls library contained this bug of neglecting to unregister all its classes when dynamically unloaded, and we had to issue a hotfix because the crashes caused by it were actually occurring on real users’ machines. Don’t be the one responsible for having to issue a hotfix for your product. Unregister your classes if the process is going to continue running after your DLL unloads. Because it’s the right thing to do.
2006092007p29663-14-(Now, you might notice that this goes against the rule of not calling out to other DLLs during your DLL_PROCESS_ATTACH. The solution for this is to have a “cleanup” function that people must call before calling FreeLibrary on your library to balance the “initialization” function that they had to call to register your control classes. On the other hand, if you failed to plan ahead for this, such as the shell common control did with its InitCommonControlsEx function without a matching UninitCommonControls function, then you have to decide between the lesser of two evils.)
--
2013070500p3883-27-for example, when the user double-clicks the icon in Explorer,
2013070500p3883-28-a new process object is created, and the DLLs are loaded
2013070500p3883-29-by a component known as the Loader.
2013070500p3883-30-The Loader locates the DLLs,
2013070500p3883-31-maps them into memory,
2013070500p3883:32:and then calls the DllMain function for each of the modules.
2013070500p3883-33-It appears that some Microsoft DLLs obtain DLLs from the
2013070500p3883-34-current directory and are therefore susceptible to a current
2013070500p3883-35-directory attack.
2013070500p3883-36-We created a simple Win32 application which demonstrates the issue:
2013070500p3883-37-
--
2010012200p15193-10- What about critical sections? There is no “Uh-oh” return value for critical sections; EnterCriticalSection doesn’t have a return value.  Instead, the kernel just says “Open season on critical sections!” I get the mental image of all the gates in a parking garage just opening up and letting anybody in and out. 
2010012200p15193-11- In Windows Vista, the gates don’t go up. Instead they become electrified!
2010012200p15193-12- If during DLL_PROCESS_DETACH at process termination on Windows Vista you call EnterCriticalSection on a critical section that has been orphaned, the kernel no longer responds by just letting you through. Instead, it says, “Oh dear, things are in unrecoverably bad shape. Best to just terminate the process now.” If you try to enter an orphaned critical section during process shutdown, the kernel simply calls TerminateProcess on the current process!
2010012200p15193-13- It’s sort of like the movie  Speed: If the thread encounters a critical section that causes it to drop below 50 miles per hour, it blows up.
2010012200p15193-14- Fortunately, this error doesn’t change the underlying analysis of  How my lack of understanding of how processes exit on Windows XP forced a security patch to be recalled.
2010012200p15193:15: But it also illustrates how the details of process shutdown are open to changes in the implementation at any time, so you shouldn’t rely on them. Remember  the classical model for how processes exit: You cleanly shut down all your worker threads, and then call ExitProcess. If you don’t follow that model (and given the current programming landscape, you pretty have no choice but to abandon that model, what with DLLs creating worker threads behind your back), it’s even more important that you follow the general guidance of  not doing anything scary in your DllMain function. 
2010012200p15193-16-
2010012200p15193-17-        
2010012200p15193-18-                
2010012200p15193-19-
2010012200p15193-20-        
--
2007081500p25573-7-            August 15th, 20070 0
2007081500p25573-8-		
2007081500p25573-9-        
2007081500p25573-10-One of the differences between C++ and C# is when static constructors run.
2007081500p25573-11-In C++, static constructors are the first thing run in a module,
2007081500p25573:12:even before the DllMain function
2007081500p25573-13-runs.¹
2007081500p25573-14-In C#, however, static constructors
2007081500p25573-15-
2007081500p25573-16-don’t run until you use the class for the first time.
2007081500p25573-17-If your static constructor has side effects,
--
2010011500p15253-6-            
2010011500p15253-7-            January 15th, 20100 0
2010011500p15253-8-		
2010011500p15253-9-        
2010011500p15253-10-As you are I’m sure aware,
2010011500p15253:11:you shouldn’t be doing much of anything in your DllMain
2010011500p15253-12-function,
2010011500p15253-13-but you have to watch out for cases where you end up doing them
2010011500p15253-14-accidentally.
2010011500p15253-15-
2010011500p15253-16-
--
2010011500p15253-24-The following is not the actual code, but it captures the same
2010011500p15253-25-spirit:
2010011500p15253-26-
2010011500p15253-27-
2010011500p15253-28-INFO *CachedInfo;
2010011500p15253:29:BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, void *pvReserved)
2010011500p15253-30-{
2010011500p15253-31-  switch (dwReason) {
2010011500p15253-32-  ...
2010011500p15253-33-  case DLL_PROCESS_DETACH:
2010011500p15253-34-    ...
--
2009071000p17543-28-
2009071000p17543-29-
2009071000p17543-30- A.DLL depends on B.DLL, and both DLLs depend on MIDDLE.DLL. That common DLL really should be called BOTTOM.DLL since everybody depends on it.
2009071000p17543-31- Now you can see why the dependency chain A.DLL → MIDDLE.DLL → B.DLL is horribly wrong. Under the incorrect dependency chain, the DLLs would be uninitialized in the order A.DLL, MIDDLE.DLL, B.DLL, even though B.DLL depends on MIDDLE.DLL. That’s because your “invented” dependency introduces a cycle in the dependency chain, and a bogus one at that. Once you have cycles in the dependency chain, everything falls apart. You took something that might have worked into something that explodes upon impact.
2009071000p17543-32- This situation appears much more often than you think. In fact it happens all the time. Because in real life, the loader is implemented in the internal library NTDLL.DLL, and KERNEL32.DLL is just a wrapper function around the real DLL loader. In other words, if your A.DLL calls LoadLibrary("B.DLL"), you are already using a middle DLL; its name is KERNEL32.DLL. If this “dynamic dependency generation” were followed, then KERNEL32.DLL would be listed as dependent on everything. When it came time to uninitialize, KERNEL32.DLL would uninitialized before all dynamically-loaded DLLs, because it was the one who loaded them, and then all the dynamically-loaded DLLs would find themselves in an interesting world where KERNEL32.DLL no longer existed.
2009071000p17543:33: Besides, the original problem arises when A.DLL calls a function in B.DLL during its DLL_PROCESS_DETACH handler, going against the rule that you shouldn’t call anything outside your DLL from your DllMain function (except perhaps a little bit of KERNEL32 but even then, it’s still not the best idea). It’s one thing to make accommodations so that existing bad programs continue to run, but it’s another to build an entire infrastructure built on unreliable heuristics in order to encourage people to do something they shouldn’t be doing in the first place, and whose guesses end up taking a working situation and breaking it.
2009071000p17543-34- You can’t even write programs to take advantage of this new behavior because walking the stack is itself unreliable. You recompile your program with different optimizations, and all of a sudden the stack walking stops working because you enabled tail call elimination. If somebody told you, “Hey, we added this feature that isn’t reliable,” I suspect your reaction would not be “Awesome, let me start depending on it!” 
2009071000p17543-35-
2009071000p17543-36-        
2009071000p17543-37-                
2009071000p17543-38-
--
2004012700p40873-1-
2004012700p40873-2-
2004012700p40873-3-
2004012700p40873-4-	
2004012700p40873:5:        Some reasons not to do anything scary in your DllMain          Raymond Chen        
2004012700p40873-6-            
2004012700p40873-7-            January 27th, 20040 0
2004012700p40873-8-		
2004012700p40873-9-        As everybody knows by now,
2004012700p40873-10-you’re not supposed to do anything even remotely
2004012700p40873-11-interesting in your
2004012700p40873-12-
2004012700p40873:13:DllMain function.
2004012700p40873-14-Oleg Lvovitch has written two very good
2004012700p40873-15-articles about this,
2004012700p40873-16-
2004012700p40873-17-one about how things work,
2004012700p40873-18-and
2004012700p40873-19-
2004012700p40873-20-one about what goes wrong when they don’t work.
2004012700p40873-21-
2004012700p40873-22-Here’s another reason not to do anything remotely interesting
2004012700p40873:23:in your DllMain:  It’s common to load a library without actual
2004012700p40873-24-intent to invoke its full functionality. For example, somebody
2004012700p40873-25-might load your library like this:
2004012700p40873-26-
2004012700p40873-27-
2004012700p40873-28-// error checking deleted for expository purposes
--
2009111300p16043-14-We have a global unhandled exception filter in our application so we can log all failures. After we finish logging, we call ExitProcess, but we find that the application never actually exits. If we connect a debugger to the stuck application, we see it hung in GetEnvironmentVariable.
2009111300p16043-15-Your gut response should be, “Holy cow, I’m surprised you even got that far!”
2009111300p16043-16-This isn’t one of those global unhandled exception filters that got installed because your program plays some really clever game with exceptions, No, this is an “Oh no, my program just crashed and I want to log it” exception handler. In other words, when this exception handler “handles” an exception, it’s because your program has encountered some sort of serious internal programming error for which the program did not know how to recover. We saw earlier that  you can’t do much in a signal handler because you might have interrupted a block of code which was in the middle of updating some data structures, leaving them momentarily inconsistent. But this exception filter is in an even worse state: Not only is there a good chance that the program is in the middle of updating something and left it in an inconsistent state, you are in fact guaranteed that the system is in a corrupted state.
2009111300p16043-17-Why is this a guarantee? Because if the system were in a consistent state, you wouldn’t have crashed!
2009111300p16043-18-Programming is about establishing invariants, perturbing them, and then re-establishing them. It is a game of stepping-stone from one island of consistency to another. But the code that does the perturbing and the re-establishing assumes that it’s starting from a consistent state to begin with. For example, a function that removes a node from a doubly-linked list manipulates some backward and forward link pointers (temporarily violating the linked list invariant), and then when it’s finished, the linked list is back to a consistent state. But this code assumes that the linked list is not corrupted to begin with!
2009111300p16043:19:Let’s look again at that call to ExitProcess. That’s going to detach all the DLLs, calling each DLL’s DllMain with the DLL_PROCESS_DETACH notification. But of course, those DllMain are going to assume that the data structures are intact and nothing is corrupted. On the other hand, you know for a fact that these prerequisites are not met—the program crashed precisely because something is corrupted. One DLL might walk a linked list—but you might have crashed because that linked list is corrupted. Another DLL might try to delete a critical section—but you might have crashed because the data structure containing the critical section is corrupted.
2009111300p16043-20-Heck, the crash might have been inside somebody’s DLL_PROCESS_DETACH handler to begin with, for all you know.
2009111300p16043-21-“Yeah, but the documentation for TerminateProcess says that it does not clean up shared memory.”
2009111300p16043-22-Well, it depends on what you mean by clean up. The reference count on the shared memory is properly decremented when the handle is automatically closed as part of process cleanup, and the shared memory will be properly freed once there are no more references to it. It is not cleaned up in the sense of “corruption is repaired”—but of course the operating system can’t do that because it doesn’t know what the semantics of your shared memory block are.
2009111300p16043-23-But this is hardly anything to get concerned about because your program doesn’t know how to un-corrupt the data either.
2009111300p16043-24-“It also says that DLLs don’t receive their DLL_PROCESS_DETACH notification.”
--
2009062600p17733-6-            
2009062600p17733-7-            June 26th, 20090 0
2009062600p17733-8-		
2009062600p17733-9-        The thread that gets the DLL_PROCESS_DETACH notification is not necessarily the one that got the DLL_PROCESS_ATTACH notification. This is obvious if you think about it, because the thread that got the DLL_PROCESS_ATTACH notification might not even exist any longer when the DLL is unloaded. How can something that doesn’t exist send a notification?
2009062600p17733-10- Even so, many people fail to realize this. You can’t do anything with thread affinity in your DLL_PROCESS_ATTACH or DLL_PROCESS_DETACH handler since you have no guarantee about which thread will be called upon to handle these process notifications. Of course, you’re not supposed to be doing anything particularly interesting in your DLL_PROCESS_ATTACH handler anyway, but things with thread affinity are doubly bad. 
2009062600p17733:11: The classic example of this, which I’m told the Developer Support team run into with alarming frequency, is a DLL that creates a window in its DLL_PROCESS_ATTACH handler and destroys it in its DLL_PROCESS_DETACH handler. Now, creating a window in DllMain is already a horrifically bad idea since arbitrary code can run during the creation of a window (for example, there may be a global hook), but the lack of a thread guarantee makes it downright insane. The DLL calls DestroyWindow in its DLL_PROCESS_DETACH handler, but since that notification comes in on a thread different from the one that received the DLL_PROCESS_ATTACH notification, the attempt to destroy the window fails since you must call DestroyWindow from the same thread that created it. 
2009062600p17733-12- Result: The DLL’s attempt to destroy its window fails, a message comes in, and the process crashes since the window procedure no longer exists. 
2009062600p17733-13-
2009062600p17733-14-        
2009062600p17733-15-                
2009062600p17733-16-
--
2005021400p36463-17-
2005021400p36463-18-
2005021400p36463-19-If this value is used, and the executable module is a DLL,
2005021400p36463-20-the system does not call
2005021400p36463-21-
2005021400p36463:22:DllMain for process and thread initialization and termination.
2005021400p36463-23-Also, the system does not load additional executable modules
2005021400p36463-24-that are referenced by the specified module.
2005021400p36463-25-
2005021400p36463-26-
2005021400p36463-27-If you are planning only to access data or resources in the DLL,
--
2007121300p24183-4-	
2007121300p24183-5-        AppInit_DLLs should be renamed Deadlock_Or_Crash_Randomly_DLLs          Raymond Chen        
2007121300p24183-6-            
2007121300p24183-7-            December 13th, 20070 0
2007121300p24183-8-		
2007121300p24183:9:        I have no idea why the window manager team added  this feature to Windows NT. It basically says, “Hi, use this key to violate all the rules known to mankind about what can legitimately be done in a DllMain function. Oh, and be  an attractive malware attack vector, too.”
2007121300p24183:10: I’ve debugged a few crashes that were traced back to the AppInit_DLLs key. What makes them particularly fun is that the offending DLL is usually not on the stack. Rather, the fact that a foreign DLL is being loaded inside USER32‘s initialization code means that you’re violating the rule against calling LoadLibrary inside a DllMain function. The result of this madness is that DLLs get initialized out of order, and typically manifests itself in some DLL crashing trying to use an object (often a critical section) that it is supposed to have initialized in its DLL_PROCESS_ATTACH handler. It crashed because the loader got tricked into initializing DLLs out of order. The dependent DLL received its DLL_PROCESS_ATTACH before the prerequisite DLL.
2007121300p24183-11- I end up looking at these failures because the victim DLL is often a DLL that my group is responsible for.
2007121300p24183-12- The window manager folks came to the same conclusion about AppInit_DLLs, and it doesn’t work any more in Windows Vista by default. (Nick Kramer describes how to re-enable it.) 
2007121300p24183-13-
2007121300p24183-14-        
2007121300p24183-15-                
--
2012042700p7763-24-ntdll!RtlRaiseException+0x223
2012042700p7763-25-ntdll!TppRaiseInvalidParameter+0x48
2012042700p7763-26-ntdll!TpReleaseCleanupGroupMembers+0x246
2012042700p7763-27-litware!CThreadPool::UnInitialize+0x22
2012042700p7763-28-litware!_CRT_INIT+0xbf
2012042700p7763:29:litware!__DllMainCRTStartup+0x18b
2012042700p7763-30-ntdll!LdrpCallInitRoutine+0x3f
2012042700p7763-31-ntdll!LdrShutdownProcess+0x205
2012042700p7763-32-ntdll!RtlExitUserProcess+0x90
2012042700p7763-33-kernel32!ExitProcessImplementation+0xa
2012042700p7763-34-contoso!wmain+0x193
--
2012042700p7763-56-Do you see it?
2012042700p7763-57-
2012042700p7763-58-
2012042700p7763-59-The code is calling
2012042700p7763-60-Close­Thread­pool­Cleanup­Group­Members
2012042700p7763:61:from inside DllMain
2012042700p7763-62-while handling the
2012042700p7763-63-DLL_PROCESS_DETACH notification.
2012042700p7763-64-Looking further up the stack, you can see this was triggered by
2012042700p7763-65-a call to ExitProcess,
2012042700p7763-66-and now all the stuff you know about
--
2010011300p15293-7-            January 13th, 20100 0
2010011300p15293-8-		
2010011300p15293-9-        A customer had the following problem:
2010011300p15293-10- We’re calling GetCommandLine to retrieve the command line, and the documentation says that it returns a single null-terminated string. However, when we call it in our application, we find that it is actually a double-null-terminated string. The buffer returned consists of a series of null-terminated strings, one string per word on the command line, all stored one after the other, and with two null terminators at the end. How do I get the original string? 
2010011300p15293-11- Recall that  the command line is just a conveniently-initialized variable in a process and once it’s set up, the kernel doesn’t really care about it any more.
2010011300p15293:12: What is most likely happening is that somebody is taking the raw command line returned by GetCommandLine and  writing to it. The customer can confirm this by dumping the command line just as the process starts, even before any DLLs get to run their DllMains, and then setting a write breakpoint on the command line to see who is writing to it.
2010011300p15293-13- And in fact, the customer did find the culprit. 
2010011300p15293-14- It turns out it was some other part of the code (not written by me!) which was parsing the command line and writing into it in the process. 
2010011300p15293-15-
2010011300p15293-16-        
2010011300p15293-17-                
--
2004012800p40853-1-
2004012800p40853-2-
2004012800p40853-3-
2004012800p40853-4-	
2004012800p40853:5:        Another reason not to do anything scary in your DllMain: Inadvertent deadlock          Raymond Chen        
2004012800p40853-6-            
2004012800p40853-7-            January 28th, 20040 0
2004012800p40853-8-		
2004012800p40853-9-        
2004012800p40853:10:Your DllMain function runs inside the loader lock,
2004012800p40853-11-one of the few times the OS lets you run code while one
2004012800p40853-12-of its internal locks is held.
2004012800p40853-13-This means that you must be extra careful not to violate
2004012800p40853:14:a lock hierarchy in your DllMain; otherwise, you
2004012800p40853-15-are asking for a deadlock.
2004012800p40853-16-
2004012800p40853-17-
2004012800p40853-18-(You do have a
2004012800p40853-19-
--
2004012800p40853-22-
2004012800p40853-23-The loader lock is taken by any function that needs to
2004012800p40853-24-access the list of DLLs loaded into the process.
2004012800p40853-25-This includes functions like GetModuleHandle
2004012800p40853-26-and GetModuleFileName.
2004012800p40853:27:If your DllMain enters a critical section or waits on
2004012800p40853-28-a synchronization object, and that critical section or
2004012800p40853-29-synchronization object is owned by some code that is
2004012800p40853-30-in turn waiting for the loader lock, you just created a deadlock:
2004012800p40853-31-
2004012800p40853-32-
--
2004012800p40853-35-// some code somewhere
2004012800p40853-36-EnterCriticalSection(&g_csGlobal);
2004012800p40853-37-... GetModuleFileName(MyInstance, ..);
2004012800p40853-38-LeaveCriticalSection(&g_csGlobal);
2004012800p40853-39-BOOL WINAPI
2004012800p40853:40:DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
2004012800p40853-41-        LPVOID lpvReserved)
2004012800p40853-42-{
2004012800p40853-43-  switch (fdwReason) {
2004012800p40853-44-  ...
2004012800p40853-45-  case DLL_THREAD_DETACH:
--
2004012800p40853-75-
2004012800p40853-76-I have seen this happen. It’s not pretty.
2004012800p40853-77-
2004012800p40853-78-Moral of the story: Respect the loader lock.
2004012800p40853-79-Include it in your lock hierarchy rules if you take
2004012800p40853:80:any locks in your DllMain.
2004012800p40853-81-
2004012800p40853-82-        
2004012800p40853-83-                
2004012800p40853-84-
2004012800p40853-85-        
--
2014101600p43833-1-
2014101600p43833-2-
2014101600p43833-3-
2014101600p43833-4-	
2014101600p43833:5:        If only DLLs can get DllMain notifications, how can an EXE receive a notification when a thread is created (for example)?          Raymond Chen        
2014101600p43833-6-            
2014101600p43833-7-            October 16th, 20140 0
2014101600p43833-8-		
2014101600p43833-9-        
2014101600p43833-10-When a DLL is loaded, it receives a
--
2014101600p43833-40-LACKEYNOTIFICATION g_lackeyNotification;
2014101600p43833-41-void RegisterLackeyCallback(LACKEYNOTIFICATION lackeyNotification)
2014101600p43833-42-{
2014101600p43833-43- g_lackeyNotification = lackeyNotification;
2014101600p43833-44-}
2014101600p43833:45:BOOL WINAPI DllMain(
2014101600p43833-46-    HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
2014101600p43833-47-{
2014101600p43833-48- if (g_lackeyNotification) g_lackeyNotification(dwReason);
2014101600p43833-49- return TRUE;
2014101600p43833-50-}
--
2013110700p2713-17-says
2013110700p2713-18-
2013110700p2713-19-
2013110700p2713-20-
2013110700p2713-21-This prevents a deadlock from occurring when one thread in
2013110700p2713:22:DllMain is waiting for the callback to end,
2013110700p2713-23-and another thread that is executing the callback
2013110700p2713-24-attempts to acquire the loader lock.
2013110700p2713-25-
2013110700p2713-26-
2013110700p2713-27-If the DLL containing the callback might be unloaded,
2013110700p2713:28:the cleanup code in DllMain must cancel outstanding
2013110700p2713-29-callbacks before releasing the object.
2013110700p2713-30-
2013110700p2713-31-
2013110700p2713-32-Managing callbacks created with a
2013110700p2713-33-TP_CALLBACK_ENVIRON
--
2013110700p2713-37-or to guarantee that callbacks which may be executing
2013110700p2713-38-do not acquire the loader lock.
2013110700p2713-39-
2013110700p2713-40-
2013110700p2713-41-
2013110700p2713:42:I’m not going to help you with the DllMain cleanup issues.
2013110700p2713-43-(My plan is to simply avoid the issue by preventing the DLL
2013110700p2713-44-from unloading while a callback is still pending.
2013110700p2713:45:That way, you never have to cancel the callback from DllMain.)
2013110700p2713-46-But I am going to help with the
2013110700p2713-47-“consider other options for ensuring that the library is not
2013110700p2713-48-unloaded while callbacks are executing.”
2013110700p2713-49-
2013110700p2713-50-
--
2017020300p95345-30-00a5f754 69699832 WINHTTP!WinHttpWebSocketClose+0x9c
2017020300p95345-31-...
2017020300p95345-32- global atexit call being made here
2017020300p95345-33-...
2017020300p95345-34-00a5f814 696d1f7d XXXXXX!_CRT_INIT+0xaa
2017020300p95345:35:00a5f874 7753cd4e XXXXXX!__DllMainCRTStartup+0x1ee
2017020300p95345-36-00a5f894 77505525 ntdll!LdrxCallInitRoutine+0x16
2017020300p95345-37-00a5f8e4 775057cb ntdll!LdrpCallInitRoutine+0x43
2017020300p95345-38-00a5f97c 77518e3f ntdll!LdrShutdownProcess+0x101
2017020300p95345-39-00a5f990 77065736 ntdll!RtlExitUserProcess+0x63
2017020300p95345-40-00a5f99c 77065471 msvcrt!__crtExitProcess+0x17
--
2017020300p95345-89-contoso!`dynamic atexit destructor for 'Extension::s_extension''+0x23
2017020300p95345-90-ucrtbase!<lambda_275893d493268fdec8709772e3fcec0e>::operator()+0xb7
2017020300p95345-91-ucrtbase!__crt_seh_guarded_call<int>::operator()<...>+0x3b
2017020300p95345-92-ucrtbase!__acrt_lock_and_call+0x1e
2017020300p95345-93-ucrtbase!_execute_onexit_table+0x31
2017020300p95345:94:contoso!dllmain_crt_process_detach+0x4e
2017020300p95345:95:contoso!dllmain_dispatch+0xd3
2017020300p95345-96-ntdll!LdrpCallInitRoutine+0x4c
2017020300p95345-97-ntdll!LdrShutdownProcess+0x142
2017020300p95345-98-ntdll!RtlExitUserProcess+0x98
2017020300p95345-99-kernel32!ExitProcessImplementation+0xa
2017020300p95345-100-contososerver!ControlSignalHandler::HandleControlSignal+0x68
--
2008080800p21313-11- No. 
2008080800p21313-12- … 
2008080800p21313-13- Yes. 
2008080800p21313-14- All three answers are correct, for different formulations of the question. 
2008080800p21313-15- From the kernel’s point of view, the answer is a simple Yes. If a DLL’s entry point returns FALSE to the DLL_PROCESS_ATTACH notification, it will receive a DLL_PROCESS_DETACH notification. 
2008080800p21313:16: However, most C and C++ programs do not use the raw DLL entry point. Instead, they use the C runtime entry point, which will have a name something like DllMainCRTStartup. That entry point function does work to manage the C runtime library and calls your entry point (which you’ve probably called DllMain) to see what you think. 
2008080800p21313:17: If you compiled your program prior to around 2002 and your DllMain function returns FALSE in response to the DLL_PROCESS_ATTACH notification, then the C runtime code says, “Oh, well, I guess I’m not running after all” and shuts itself down. When the kernel calls the C runtime entry point with the DLL_PROCESS_DETACH notification, the C runtime says, “Oh, I’m already shut down, thanks for asking” and returns immediately, which means that your entry point is not called with the DLL_PROCESS_DETACH notification. In other words, if you wrote your program prior to around 2002, the answer is No. 
2008080800p21313:18: Sometime in 2002 or maybe 2003, the C runtime folks changed the behavior. If your DllMain function returns FALSE in response to the DLL_PROCESS_ATTACH notification, you will nevertheless get the DLL_PROCESS_DETACH notification. In other words, if you wrote your program after around 2002 or maybe 2003, then the answer is Yes. Why change? Maybe they wanted to match the kernel behavior more closely, maybe they considered their previous behavior a bug. You’ll have to ask them. 
2008080800p21313:19: What does this mean for you, the programmer? Some people may look at this and conclude, “Well, now that I know how each of the specific scenarios works, I can rely on knowing the behavior that results from the scenario I’m in. For example, since I’m using Visual Studio 2008, the answer is Yes.” But I think that’s the wrong conclusion, because you usually do not have total control over how your program is compiled and linked. You may share your code with another project, and that other project may not know that you are relying on the behavior of a specific version of Visual Studio 2008; they will compile your program with Borland C++ version 5.5,¹ and now your program is subtly broken. My recommendation is to write your DllMain function so that it works correctly regardless of which scenario it ends up used in. (And since you shouldn’t be doing much in your DllMain function anyway, this shouldn’t be too much of a burden.) 
2008080800p21313-20- Footnote 
2008080800p21313:21: ¹I do not know what the behavior of Borland C++ version 5.5 is with respect to returning FALSE from DllMain. I didn’t feel like doing the research to find a compiler whose behavior is different from Visual Studio 2008, so I just picked one at random. I have a 50/50 chance of being right. 
2008080800p21313-22-
2008080800p21313-23-        
2008080800p21313-24-                
2008080800p21313-25-
2008080800p21313-26-        
--
2007090400p25283-1-
2007090400p25283-2-
2007090400p25283-3-
2007090400p25283-4-	
2007090400p25283:5:        Does creating a thread from DllMain deadlock or doesn't it?          Raymond Chen        
2007090400p25283-6-            
2007090400p25283-7-            September 4th, 20070 1
2007090400p25283-8-		
2007090400p25283:9:        Let me get this out of the way up front: Creating a thread from DllMain is  not recommended. The discussion here has to do with explaining the behavior you may observe if you violate this advice.
2007090400p25283:10:  Commenter Pete points out that “according to Usenet” creating a thread in DllMain is supposed to deadlock, but that’s not what he saw. All he saw was that the thread entry procedure was not called.
2007090400p25283-11- I’m going to set aside what “according to Usenet” means.
2007090400p25283:12: Recall how a thread starts up. When you call CreateThread, a kernel thread object is created and scheduled. Once the thread gets a chance to run, the kernel calls all the DllMain functions with the DLL_THREAD_ATTACH code. Once that’s done, the thread’s entry point is called.
2007090400p25283:13: The issue with deadlocks is that all DllMain functions are serialized. At most one DllMain can be running at a time. Suppose a DllMain function is running and it creates a thread. As we noted above, a kernel thread object is created and scheduled, and the first thing the thread does is notify all the DLLs with DLL_THREAD_ATTACH. Since DllMain functions are serialized, the attempt to send out the DLL_THREAD_ATTACH notifications must wait until the current DllMain function returns.
2007090400p25283:14: That’s why you observe that the new thread’s entry point doesn’t get called until after you return from DllMain. The new thread hasn’t even made it that far; it’s still working on the DLL_THREAD_ATTACH notifications. On the other hand, there is no actual deadlock here. The new thread will get itself off the ground once everybody else has finished doing their DllMain work.
2007090400p25283-15- So what is this deadlock that Usenet talks about? If you’ve been following along, you should spot it easily enough.
2007090400p25283:16: If your DllMain function creates a thread and then waits for the thread to do something (e.g., waits for the thread to signal an event that says that it has finished initializing, then you’ve created a deadlock. The DLL_PROCESS_ATTACH notification handler inside DllMain is waiting for the new thread to run, but the new thread can’t run until the DllMain function returns so that it can send a new DLL_THREAD_ATTACH notification.
2007090400p25283-17- This deadlock is much more commonly seen in DLL_PROCESS_DETACH, where a DLL wants to shut down its worker threads and wait for them to clean up before it unloads itself. You can’t wait for a thread inside DLL_PROCESS_DETACH because that thread needs to send out the DLL_THREAD_DETACH notifications before it exits, which it can’t do until your DLL_PROCESS_DETACH handler returns.
2007090400p25283-18- (It is for this thread cleanup case that the function FreeLibraryAndExitThread was created.) 
2007090400p25283-19-
2007090400p25283-20-        
2007090400p25283-21-                
--
2014102400p43773-127-        litware!std::tr1::shared_ptr<CApiInstance>::reset+0x3c
2014102400p43773-128-        litware!CSingleton<CApiInstance>::ReleaseRef+0x97
2014102400p43773-129-        litware!LitWareUninitialize+0xed
2014102400p43773-130-        fabrikam!CDoodadHelper::~CDoodadHelper+0x67
2014102400p43773-131-        fabrikam!_CRT_INIT+0xda
2014102400p43773:132:        fabrikam!__DllMainCRTStartup+0x1e5
2014102400p43773-133-        ntdll!LdrpCallInitRoutine+0x57
2014102400p43773-134-        ntdll!LdrpProcessDetachNode+0xfe
2014102400p43773-135-        ntdll!LdrpUnloadNode+0x77
2014102400p43773-136-        ntdll!LdrpDecrementNodeLoadCount+0xd0
2014102400p43773-137-        ntdll!LdrUnloadDll+0x34
--
2014102400p43773-346-        litware!std::tr1::shared_ptr<CApiInstance>::reset+0x3c
2014102400p43773-347-        litware!CSingleton<CApiInstance>::ReleaseRef+0x97
2014102400p43773-348-        litware!LitWareUninitialize+0xed
2014102400p43773-349-        fabrikam!CDoodadHelper::~CDoodadHelper+0x67
2014102400p43773-350-        fabrikam!_CRT_INIT+0xda
2014102400p43773:351:        fabrikam!__DllMainCRTStartup+0x1e5
2014102400p43773-352-        ntdll!LdrpCallInitRoutine+0x57
2014102400p43773-353-        ntdll!LdrpProcessDetachNode+0xfe
2014102400p43773-354-        ntdll!LdrpUnloadNode+0x77
2014102400p43773-355-        ntdll!LdrpDecrementNodeLoadCount+0xd0
2014102400p43773-356-        ntdll!LdrUnloadDll+0x34
--
2016080500p94035-68-Just at this moment, the folder tree was populating itself on the first thread, and it found a third party shell extension. It dutifully loaded the third party shell extension (because that’s what shell extensions are for), and that shell extension, as part of its DLL_PROCESS_ATTACH tried to initialize GDI+. 
2016080500p94035-69-Here comes the deadlock. 
2016080500p94035-70-GDI+ was prepared for this possibility that somebody would try to initialize GDI+ while GDI+ was already in the process of shutting itself down. It solves this problem by making the shutdown run to completion (seeing as it already started), and then starting a new initialization pass. 
2016080500p94035-71-That shutdown is waiting for a worker thread to finish up and exit. But the thread cannot exit until it sends out its DLL_THREAD_DETACH notifications. And since DLL notifications are serialized, the DLL_THREAD_DETACH cannot be sent until the DLL_PROCESS_ATTACH completes. But the DLL_PROCESS_ATTACH for the third party shell extension is waiting for GDI+. There’s our deadlock. 
2016080500p94035-72-The root cause for this is that the third party shell extension is initializing GDI+ inside its DLL_PROCESS_ATTACH. This is already highly suspect even without any special insight into GDI+, and the suspicious are confirmed in the documentation for GdiplusStartup: 
2016080500p94035:73:Do not call GdiplusStartup or GdiplusShutdown in DllMain or in any function that is called by DllMain. 
2016080500p94035-74-My guess is that the vendor who wrote this shell extension thinks that the rule doesn’t apply to them because they passed SuppressBackgroundThread = true, thinking that by removing the background thread, they successfully avoided any deadlocks with another thread. It didn’t occur to them that the other thread might not be the GDI+ background thread. 
2016080500p94035-75-It also didn’t occur to them that GDI+ might already be initialized with a background thread. Furthermore, suppose the component that initialized GDI+ first (with a background thread) uninitialized GDI+ first. That call to GdiplusShutdown will not shut down GDI+ because there is still an outstanding client. And then when their DLL unloads, they call GdiplusShutdown, and that will cause a true shutdown of GDI+, which includes shutting down that background thread that they thought they had suppressed.¹ 
2016080500p94035-76-So basically it was a bad idea all around. 
2016080500p94035-77-I transferred this issue to the application compatibility team for outreach to the vendor, who happens to be a major corporation, so hopefully they can spare some developers to fix the deadlock. 
2016080500p94035-78-Bonus chatter: Identifying the vendor was a bit tricky because of the extremely vague DLL name. 
--
2021010800p104684-120-Unfortunately, windows.service­framework.widget­service.dll is too long to fit in the buffer, so its name gets truncated, and the debugger can’t recover it.
2021010800p104684-121-We’ll have to resolve the symbol manually¹ using  a technique I discussed some time ago: Loading the module as if were a dump file and fixing up the addresses.
2021010800p104684-122-C:\> ntsd -z windows.serviceframework.widgetservice.dll
2021010800p104684-123-...
2021010800p104684-124-ModLoad: 00000001`80000000 00000001`8001f000 windows.serviceframework.widgetservice.dll
2021010800p104684:125:windows_serviceframework_widgetservice!_DllMainCRTStartup:
2021010800p104684-126-00000001`80010c70 48895c2408      mov     qword ptr [rsp+8],rbx ss:00000000`00000008=????????????????
2021010800p104684-127-0:000> ln 00000001`80000000+14480
2021010800p104684-128-(00000001`80014480)   windows_serviceframework_widgetservice!winrt::impl::produce
2021010800p104684-129-                         <winrt::Windows::ServiceFramework::WidgetService::implementation::ColorChangedEventArgs,
2021010800p104684-130-                          winrt::Windows::ServiceFramework::WidgetService::IColorChangedEventArgs>::`vftable'
--
2014101700p43823-84-and to destruct them in
2014101700p43823-85-DLL_PROCESS_DETACH.
2014101700p43823-86-In other words, the code conceptually goes like this:
2014101700p43823-87-
2014101700p43823-88-
2014101700p43823:89:BOOL CALLBACK RealDllMain(
2014101700p43823-90-    HINSTANCE hinst, DWORD dwReason, void *pvReserved)
2014101700p43823-91-{
2014101700p43823-92-  ...
2014101700p43823-93-  case DLL_PROCESS_ATTACH:
2014101700p43823-94-   Initialize_C_Runtime_Library();
2014101700p43823-95-   Construct_DLL_Global_Objects();
2014101700p43823:96:   DllMain(hinst, dwReason, pvReserved);
2014101700p43823-97-   ...
2014101700p43823-98- case DLL_PROCESS_DETACH:
2014101700p43823:99:   DllMain(hinst, dwReason, pvReserved);
2014101700p43823-100-   Destruct_DLL_Global_Objects();
2014101700p43823-101-   Uninitialize_C_Runtime_Library();
2014101700p43823-102-   break;
2014101700p43823-103- ...
2014101700p43823-104-}
--
2020073100p104024-7-            July 31st, 20207 0
2020073100p104024-8-		
2020073100p104024-9-        There’s an undocumented switch for the Microsoft Visual Studio linker known as /ALTERNATENAME. Despite being undocumented, people use it a lot. So what is it?
2020073100p104024-10-This is effectively a command line switch version of  the OLDNAMES.LIB library. When you say /ALTERNATENAME:X=Y, then this tells the linker that if it is looking for a symbol named X and can’t find it, then before giving up, it should redirect it to the symbol Y and try again.
2020073100p104024-11-The C runtime library uses this mechanism for various sneaky purposes. For example, there’s a part that goes
2020073100p104024:12:BOOL (WINAPI * const _pDefaultRawDllMain)(HANDLE, DWORD, LPVOID) = NULL;
2020073100p104024-13-#if defined (_M_IX86)   
2020073100p104024:14:#pragma comment(linker, "/alternatename:__pRawDllMain=__pDefaultRawDllMain")   
2020073100p104024-15-#elif defined (_M_IA64) || defined (_M_AMD64)   
2020073100p104024:16:#pragma comment(linker, "/alternatename:_pRawDllMain=_pDefaultRawDllMain")   
2020073100p104024-17-#else  /* defined (_M_IA64) || defined (_M_AMD64) */   
2020073100p104024-18-#error Unsupported platform   
2020073100p104024-19-#endif  /* defined (_M_IA64) || defined (_M_AMD64) */  
2020073100p104024-20-
2020073100p104024:21:What this does is say, “If you need a symbol called _pRawDllMain, but you can’t find it, then try again with _pDefaultRawDllMain.” If an object file defines _pRawDllMain, then that definition will be used. Otherwise _pDefaultRawDllMain will be used.
2020073100p104024-22-Note that /ALTERNATENAME is a linker feature and consequently operates on decorated names, since the linker doesn’t understand compiler-specific name-decoration algorithms. This means that you typically have to use different versions of the /ALTERNATENAME switch, depending on what architecture you are targeting. In the above example, the C runtime library knows that __cdecl decoration prepends an underscore on x86, but not on any other platform.
2020073100p104024:23:This use of /ALTERNATENAME here is a way for the compiler to generate hooks into the DLL startup process based on the code being compiled. If there is no _pRawDllMain defined by an object file, then _pDefaultRawDllMain will be used instead, and that version is just a null pointer, which means, “Don’t do anything special.”
2020073100p104024-24-This pattern of using the /ALTERNATENAME switch lets you provide a default value for a function or variable, which others can override if they choose. For example, you might do something like this:
2020073100p104024-25-void default_error_log() { /* do nothing */ }
2020073100p104024-26-// For expository simplification: assume x86 cdecl
2020073100p104024-27-#pragma comment(linker, "/alternatename:_error_log=_default_error_log")   
2020073100p104024-28-
--
2020073100p104024-30-The C++/WinRT library uses /ALTERNATENAME for a different purpose. The C++/WinRT library wants to support being used both with and without windows.h, so it contains its own declarations for the Windows functions and structures that it needs.
2020073100p104024-31-But now there’s a problem: If it is used with windows.h, then there are structure definition errors. Therefore, C++/WinRT needs to give its equivalent declarations of Windows structures some other name, to avoid redefinition errors.
2020073100p104024-32-But this in turn means that the function prototypes in the C++/WinRT library need to use the renamed structures, rather than the original Windows structures, in case the C++/WinRT library is used without windows.h. This declaration will in turn create a conflict if the C++/WinRT library is used with windows.h when the real declarations are encountered in windows.h.
2020073100p104024-33-The solution is to rename the C++/WinRT version of Windows functions, too. C++/WinRT gives them a WINRT_IMPL_ prefix, so that there is no function declaration collision.
2020073100p104024-34-We now have two parallel universes. There’s the windows.h universe, and the C++/WinRT universe, each with their own structures and functions. The two parallel universes are unified by the /ALTERNATENAME directive, which tells the linker, “If you find yourself looking for the function WINRT_IMPL_GetLastError, try again with GetLastError.” Since nobody defines WINRT_IMPL_GetLastError, the “try again” kicks in, and all of the calls to WINRT_GetLastError end up redirected to the operating system GetLastError function, which is what we wanted in the first place.
2020073100p104024:35:¹ The more traditional way of doing this (that doesn’t rely on undocumented vendor-specific linker features) is to take advantage of  the classical model for linking, specifically the part where you can let  an OBJ override a LIB: What you do is define _pRawDllMain in a separate OBJ file that defines nothing except that one variable, and put that OBJ in the C runtime LIB. If the module provides its own definition of _pRawDllMain in an OBJ file, then that definition is used. Otherwise, the linker will search through the LIBs, and eventually it will find the one in the C runtime LIB and use that one.
2020073100p104024-36-So why does /ALTERNATENAME exist if you could already get this effect via LIBs, and in way that all linkers support, not just the Microsoft C linker?
2020073100p104024-37-C++/WinRT is a header-only library. It has no LIB in which to put these default definitions. It therefore has to use the “command line switch version of a LIB”.
2020073100p104024-38-
2020073100p104024-39-        
2020073100p104024-40-                
--
2007042500p27123-77-...
2007042500p27123-78-ModLoad: 15800000 15dc1000   C:\Program Files\ABC\GHI.DLL
2007042500p27123-79-eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
2007042500p27123-80-eip=15807366 esp=00000000 ebp=00000000 iopl=0         nv up di pl nz na pe nc
2007042500p27123-81-cs=0000  ss=0000  ds=0000  es=0000  fs=0000  gs=0000             efl=00000000
2007042500p27123:82:GHI!_DllMainCRTStartup:
2007042500p27123-83-15807366 8bff             mov     edi,edi
2007042500p27123-84-0:000>
2007042500p27123-85-
2007042500p27123-86-
2007042500p27123-87-That module-load notification tells you where the DLL got
--
2022092200p107206-14-#define WrlCreatorMapIncludePragma(className)
2022092200p107206-15-#define WrlCreatorMapIncludePragmaex(className, serverName)
2022092200p107206-16-#define CoCreatableClassWrlCreatorMapInclude(className)
2022092200p107206-17-#define CoCreatableClassWrlCreatorMapIncludeEx(className, serverName)
2022092200p107206-18-
2022092200p107206:19:Invoke those macros somewhere in the main project (next to your DllMain is a fairly obvious place), and they will pull in the object registration information.
2022092200p107206-20-
2022092200p107206-21-        
2022092200p107206-22-                
2022092200p107206-23-
2022092200p107206-24-        
