none
No DLL_PROCESS_DETACH after calling PlaySound() on Windows 7 64-bit

    General discussion

  • Probably this is caused by the driver of my audio card (Which is Creative X-Fi Xtreme Gamer PCI, SBXF_PCDRV_LB_2_18_0013.exe).

    Nevertheless, I wrote a simple app which implicitly loads DLL with a global object with a destructor. After that the app plays wav file from harddrive using PlaySound from mmsystem.h (winmm.ib).

    When I exit this application, the destructor of the object in the DLL is not called. This happens because DLL's DllMain is not called because __DllMainCRTStartup is not called with DLL_PROCESS_DETACH.

    If I comment out PlaySound call, destructors are called ok.

    Probably, one of the 3 extra threads (created by Microsoft/Creative and still running by the time crtl calls ExitProcess) does something wrong.

    Will need to experiment with sound card from other vendor.

     

    Monday, March 29, 2010 12:36 PM

All replies

  • The story continues as it's actually looks like a bug in Windows 7 itself.

    I unstalled all Creative Drivers (even from Device Manager) and replaced Creative PCI sound card with ASUS Xonar STX. The bug is still there: implicitly loaded DLL will not get DLL_PROCESS_DETACH upon app exit.

    I will test on another Windows 7 64-bit system with original Creative card to see if it's a problem of only one system.

     

    Monday, April 12, 2010 11:10 AM
  • >Probably, one of the 3 extra threads (created by Microsoft/Creative and still running by the time crtl calls ExitProcess) does something wrong.

    Also from the DllMain documentation in the MSDN

    >Note that the thread that receives the DLL_PROCESS_DETACH notification is not necessarily the same thread that received the DLL_PROCESS_ATTACH notification.

    Putting these two together could mean something.

    Is there some real pressing need to have the detach sent? You said you have an object that gets cleaned up by the detach, but do you need to destroy it because of something that it has a hold of which affects the whole system? Memory and handles related to the process are cleaned up regardless when a process exits. Part of the process cleanup is to kill all of the heaps which the process created and reclaim everything allocated by that and it then goes through the handle list looking for handles created by the recently killed process and closes them.

    If you need to destroy the object then moving it out of DllMain is your best choice. It is questionable putting it in this function in the first place. But if you move it out and then require a call in your main function just before it returns to clean things up will solve this issue too.


    Visit my (not very good) blog at http://c2kblog.blogspot.com/
    Monday, April 12, 2010 11:42 AM
  • If you need to destroy the object then moving it out of DllMain is your best choice. It is questionable putting it in this function in the first place. But if you move it out and then require a call in your main function just before it returns to clean things up will solve this issue too.

    Yes, that's what I'm going to do in our application. Still, atexit() functionality does not work. Global C++ object destructors are not called. My real concern is that all other developers might not expect that Windows does not behave as described in MSDN anymore.

    It turns out that the last DLL that recieves DLL_PROCESS_DETACH is Microsoft's MMDEVAPI.DLL which calls Microsoft's SetupDiDestroyDeviceInfoList() from SETUPAPI.DLL which then simply calls NtTerminateProcess().

    Monday, April 12, 2010 3:36 PM
  • It's not that Windows doesn't behave that way, it is something else misbehaving. As you said, without the call to PlaySound Windows does work as expected. This means that the core functionality is working correctly. With this kind of behaviour though, you would need to take it up with Windows support since there is something going wrong in the operating system itself which is blocking the proper sending of the detach reason to all DLLs loaded.
    Visit my (not very good) blog at http://c2kblog.blogspot.com/
    Monday, April 12, 2010 5:43 PM
  • You should post your PlaySound code.  I know the behavior has changed on Windows 7.  Search Larry Osterman's weblog for PlaySound to see if you have one of the things that can happen, like async calls piling up.
    Phil Wilson
    Monday, April 12, 2010 8:56 PM
  • You should post your PlaySound code.  I know the behavior has changed on Windows 7.  Search Larry Osterman's weblog for PlaySound to see if you have one of the things that can happen, like async calls piling up.
    Phil Wilson


    I could reproduce the problem on my home PC running same type of OS (Win7 64-bit) with Creative X-Fi XtremeGamer card. As I can repro the problem in a very simple setup with small exe and small dll, my PlaySound code is very simple:

    PlaySound(L"Default.wav", 0, 0);
    

    After this line is executed, DLLs won't get DLL_PROCESS_DETACH because MMDEVAPI.DLL seems to end game early by calling NtTerminateProcess from its own DLL_PROCESS_DETACH handler.

    Whoever wants to give it a try, hear are full sources http://cid-426a28134ae1a56c.skydrive.live.com/embedicon.aspx/XShare/sndtst.zip It also includes compiled binary which should produce a message box if DLL_PROCESS_DETACH is received.

     

    Monday, April 12, 2010 11:46 PM
  • Well, as I said, this is something which you would have to take up with Windows product support since this seems to be an issue with Windows itself. I've managed to reproduce it myself with just the following code on Windows 7.

    //dll.cpp
    #include <Windows.h>
    
    char* mem;
    
    BOOL WINAPI DllMain(HINSTANCE hmod, DWORD reason, void* reserved)
    {
    	switch(reason)
    	{
    	case DLL_PROCESS_ATTACH:
    		mem = (char*)malloc(500);
    		break;
    	case DLL_PROCESS_DETACH:
    		free(mem);
    		break;
    	}
    	return TRUE;
    }
    
    //used from the client side just to show the dll has loaded properly
    __declspec(dllexport) void myfun()
    {
    	mem[0] = 0;
    }
    
    //exe.cpp
    #include <Windows.h>
    #include <tchar.h>
    
    __declspec(dllimport) void myfun();
    
    int main()
    {
    	myfun(); //make this call just to make sure the dll is in
    	PlaySound(_T("C:\\windows\\media\\ding.wav"), NULL, SND_FILENAME);
    	//Sleep(200);
    
    	return 0;
    }
    I have the extra things in there to be sure everything has loaded correctly etc, and the commented Sleep was being used to see if a delay after the play sound would change anything. So far from experimentation the loading of the multimedia component into a process will terminate the process before it has a chance to finish off unloading all DLLs properly.
    Visit my (not very good) blog at http://c2kblog.blogspot.com/
    Tuesday, April 13, 2010 12:17 PM
  • This is an instance of the following.

    http://blogs.msdn.com/b/oldnewthing/archive/2005/05/23/421024.aspx

    In particular, MMDevAPI.dll is loading SetupAPI.dll manually, and then trying to use a SetupAPI function after SetupAPI has been unloaded.

    A workaround is to statically link to SetupAPI.dll from your own .dll, which will force it to be around when MMDevAPI needs it.

    #include <setupapi.h>

    //used from the client side just to show the dll has loaded properly
    __declspec(dllexport) void myfun()
    {
        // call a SetupAPI function to make sure SetupAPI is loaded
        printf("DLL: SetupGetNonInteractiveMode returns %u\n", SetupGetNonInteractiveMode());
        mem[0] = 0;
    }

    Link to setupapi.lib


    Matthew van Eerde
    Thursday, August 26, 2010 5:51 PM
  • Actually just loading setupapi.dll without subsequent call to FreeLibrary fixes the problem 

    LoadLibrary(_T("setupapi.dll"));

     

     

    Monday, April 18, 2011 8:30 AM
  • It's incredible to have this Microsoft bug not corrected in Win7 SP1, but Xentrax's work around is fine.

     


    Thursday, June 23, 2011 11:41 AM
  • Update: Windows 8 x64 does not have this bug.
    Thursday, November 01, 2012 7:55 PM
  • Yeah, we fixed it in Windows 8.

    Matthew van Eerde

    Thursday, November 01, 2012 8:16 PM