none
COM Apartments: STA object corrupting an MTA object

    Question

  • Hi all

    I couldn't find a COM forum, so I am posting here instead.

    I have a demo app that uses COM objects from Microsoft's IMAPI2 and XAudio2 COM libraries.

    My demo app Solution (VS2008) is available for download here: http://www.sendspace.com/file/zwzy2t 

    My app creates an IMAPI2 object in a single-threaded apartment and two XAudio2 objects in the multi-threaded apartment. After 15 - 20 minutes, the IMAPI2 object appears to corrupt the heap such that the XAudio2 object is damaged and the app crashes.

    My app's structure purposefully reflects the identical, though much bigger structure, of our production app:

            WPF UI --> C++/CLI interop layer --> Native C++ DLLs.

    • The XAudio2 COM objects are created in a native C++ class, on a dedicated lifecycle thread that is CoInitialize'd as COINIT_MULTITHREADED.
    • The IMAPI2 COM object is created (via C# COM Interop) in a button-click handler in a WPF window class. Thread.GetApartmentState() reports STA on the IMAPI2 creation thread.

    In summary:

    1. Initialize XAudio2 and create an XAudio2 Master Voice on a dedicated thread, initialized as MTA. This thread blocks, waiting for app shutdown.
    2. Create an IMAPI2 MsftDiscRecorder2 object on a WPF UI thread (STA). Object reference is not released until app shutdown.
    3. Wait 15 to 20 minutes.
    4. Observe Unhandled exception (Access violation) and then XAudio2_6.dll unloading.

    Presumably the 15 - 20 minute delay is due to COM's DllCanUnloadNow unloading strategy. But neither of my COM references are released until the app quits, so I am surprised that DllCanUnloadNow ever returns TRUE for either COM DLL.

    Either I am creating my XAudio2 objects incorrectly in the MTA, or there is some subtle WPF/STA gotcha that I am not aware of, or there is a bug in one of those COM libraries.

    The app only crashes when both IMAP2 and XAudio2 objects are created.

    This behaviour occurs on Windows 7 x86 and x64. I haven't tested it on Vista or XP.

    So I am open to suggestions because I am out of ideas.

    Thanks!

    Thursday, January 20, 2011 4:18 PM

Answers

  • Sure @g012.

    There are two solutions, each pretty straightforward. I have tried both in production code with no ill effects. We chose Solution #2.

    Solution #1      

    Keep the XAudio2 DLL in memory to thwart COM's auto-unload function.

    1. Find the XAudio2 DLL on disk, the same physical DLL containing the registered XAudio COM classes.
    2. Explicitly load the DLL using LoadLibrary() when the XAudio2 COM objects are created, and only unload the DLL when those COM objects are released.

    Solution #2

    Keep a reference to the XAudio2 COM class object in memory to thwart COM's auto-unload function. In other words,

    1. Call CoGetClassObject with the uuid of the XAudio lib's IClassFactory.
    2. Release the class object when all XAudio2 API references are released.

    Here are the .H and .CPP files. You will probably have to remove my logging statements to get it to compile.

    #pragma once
    
    //////////////////////////////////////////////////////////////////////////
    //
    // This class provides a workaround for the incredibly damaging Windows
    // bug whereby an IMAPI2 COM object can cause the unintentional unloading
    // of the XAudio2 DLL whilst there are still XAudio2 COM objects in use.
    // 
    // Strategy:
    // Keep the XAudio2 DLL in memory to thwart COM's auto-unload function.
    // 1. Find the XAudio2 DLL on disk, the same physical DLL containing the
    //    registered XAudio COM classes.
    // 2. Explicitly load the DLL using LoadLibrary() when the XAudio2 COM
    //    objects are created, and only unload the DLL when those COM objects
    //    are released.
    //    
    // Simple, crude but effective. No observed ill effects.
    // 
    //////////////////////////////////////////////////////////////////////////
    class Xaudio2DllUnloadWorkaround_LoadLibraryVersion
    {
    public:
        Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void);
        virtual ~Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void);
    
        // Apply this workaround.
        void Invoke(bool isDebugMode);
    
    private:
        // Handle to loaded XAudio2 DLL.
        HMODULE m_hLib;
    
        // Full path to theXAudio2 DLL.
        std::basic_string<TCHAR> m_dllFullPath;
    
        bool RegistryFindDllPath(REFCLSID clsidXaudio2Dll);
        void ExplicitLoad(void);
        void ExplicitUnload(void);
    };
    
    
    
    //////////////////////////////////////////////////////////////////////////
    //
    // This class provides a workaround for the incredibly damaging Windows
    // bug whereby an IMAPI2 COM object can cause the unintentional unloading
    // of the XAudio2 DLL whilst there are still XAudio2 COM objects in use.
    //
    // This is the recommended workaround by Microsoft's XNA team.
    //
    // Keep a reference to the XAudio2 COM class object in memory to thwart
    // COM's auto-unload function. In other words,
    // 1. Call CoGetClassObject with the uuid of the XAudio lib's IClassFactory.
    // 2. Release the class object when all XAudio2 API references are released.
    //
    // Simple, crude but effective. No observed ill effects.
    // 
    //////////////////////////////////////////////////////////////////////////
    class Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion
    {
    public:
        Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void);
        virtual ~Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void);
    
        // Apply this workaround.
        void Invoke(bool isDebugMode);
    
    private:
        IUnknown* m_pClassObject;
    };
    

    //////////////////////////////////////////////////////////////////////////
    //
    // Xaudio2DllUnloadWorkaround_LoadLibraryVersion implementation
    //
    //////////////////////////////////////////////////////////////////////////
    
    Xaudio2DllUnloadWorkaround_LoadLibraryVersion::Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void)
        :m_hLib(NULL)
    {
        LOG_DEBUG(_T("<<Created>>"));
    }
    
    
    Xaudio2DllUnloadWorkaround_LoadLibraryVersion::~Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void)
    {
        ExplicitUnload();
        LOG_DEBUG(_T("<<Destroyed>>"));
    }
    
    
    void Xaudio2DllUnloadWorkaround_LoadLibraryVersion::Invoke(bool isDebugMode)
    {
        // Choose the debug or release version of the XAudio2 DLL.
        REFCLSID clsid = isDebugMode ? __uuidof(XAudio2_Debug) : __uuidof(XAudio2);
    
        if (RegistryFindDllPath(clsid))
        {
            ExplicitLoad();
        }
        else
        {
            // Nasty. There will be consequences.
            LOG_ERROR(m_pLogger,
                _T("Could not apply the workaround. ")
                _T("Application may crash if CD-RW features are used!"));
        }
    }
    
    
    //////////////////////////////////////////////////////////////////////////
    //
    // Use the XAudio2 CLSID to find the XAudio2 DLL path in the registry.
    // 
    //////////////////////////////////////////////////////////////////////////
    bool Xaudio2DllUnloadWorkaround_LoadLibraryVersion::RegistryFindDllPath(REFCLSID clsidXaudio2Dll)
    {
        // Construct a string representation of the registry key in which the
        // path to the desired XAudio2 COM DLL should be located.
    
        LPOLESTR szClsid;
        StringFromCLSID(clsidXaudio2Dll, &szClsid);
    
        std::basic_stringstream<TCHAR> subKey;
        subKey << _T("\\CLSID\\") << szClsid << _T("\\InProcServer32");
        CoTaskMemFree(szClsid);
    
        bool isDllFound = false; // assume the worst
    
    
        // Open the XAudio2 DLL's COM registry key.
    
        HKEY hKey;
        LONG ret = RegOpenKeyEx(HKEY_CLASSES_ROOT, subKey.str().c_str(), 0, KEY_READ, &hKey);
        if (ret != ERROR_SUCCESS)
        {
            DWORD err = GetLastError();
            LOG_WARNING(_T("XAudio2 DLL: find by CLSID: FAILED: GetLastError()=") << err);
            
            // The key must exist if DirectX is installed!
            assert(ret != ERROR_SUCCESS);
        }
        else
        {
            // Found the registry key. Now read its value: it should be the full
            // path the the XAudio2 COM DLL.
    
            TCHAR buf[_MAX_PATH];
            DWORD bufSize = sizeof(buf);
    
            ret = RegQueryValueEx(hKey, NULL, NULL, NULL, (LPBYTE)buf, &bufSize);
            if (ret != ERROR_SUCCESS)
            {
                DWORD err = GetLastError();
                LOG_WARNING(_T("XAudio2 DLL: path retrieval: FAILED: GetLastError()=") << err);
    
                // The DLL path must exist if DirectX is installed!
                assert(ret != ERROR_SUCCESS);
            }
            else
            {
                isDllFound = true;
                m_dllFullPath = buf; // Save the path for logging.
                LOG_DEBUG(_T("XAudio2 DLL: path found: \"") << m_dllFullPath << _T("\""));
            }
    
            RegCloseKey(hKey);
        }
    
        return isDllFound;
    }
    
    
    //////////////////////////////////////////////////////////////////////////
    //
    // Forcibly increment the internal reference count in the XAudio2 DLL by
    // explicitly loading it. This is a crude but effective way to prevent
    // it being unloading by the COM run time.
    // 
    //////////////////////////////////////////////////////////////////////////
    void Xaudio2DllUnloadWorkaround_LoadLibraryVersion::ExplicitLoad(void)
    {
        StringStreamType logMsg;
    
        if (m_pLogger->IsDebugEnabled())
        {
            logMsg << _T("LoadLibrary: \"") << m_dllFullPath << _T("\": ");
        }
    
        // Load the XAudio2 DLL
        m_hLib = LoadLibrary(m_dllFullPath.c_str());
        if (m_hLib != NULL)
        {
            logMsg << _T("SUCCEEDED");
            LOG_DEBUG(logMsg);
        }
        else
        {
            DWORD err = GetLastError();
            logMsg << _T("FAILED: GetLastError()=") << err;
            LOG_WARNING(logMsg);
        }
    }
    
    
    void Xaudio2DllUnloadWorkaround_LoadLibraryVersion::ExplicitUnload(void)
    {
        if (m_hLib != NULL)
        {
            StringStreamType logMsg;
    
            if (m_pLogger->IsDebugEnabled())
            {
                logMsg << _T("FreeLibrary: \"") << m_dllFullPath << _T("\": ");
            }
    
            // Unload the XAudio2 DLL
            if (FreeLibrary(m_hLib))
            {
                logMsg << _T("SUCCEEDED");
                LOG_DEBUG(logMsg);
            }
            else
            {
                DWORD err = GetLastError();
                logMsg << _T("FAILED: GetLastError()=") << err;
                LOG_WARNING(logMsg);
            }
        }
    }
    
    
    //////////////////////////////////////////////////////////////////////////
    
    
    
    Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion::Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void)
        :m_pClassObject(NULL)
    {
        LOG_DEBUG(_T("<<Created>>"));
    }
    
    
    Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion::~Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void)
    {
        SAFE_RELEASE(m_pClassObject);
        LOG_DEBUG(_T("<<Destroyed>>"));
    }
    
    
    void Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion::Invoke(bool isDebugMode)
    {
        StringStreamType logMsg;
        logMsg << _T("CoGetClassObject: ");
    
        // Choose the debug or release version of XAudio2.
        REFCLSID clsid = isDebugMode ? __uuidof(XAudio2_Debug) : __uuidof(XAudio2);
    
        LPVOID ppv;
        HRESULT hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, __uuidof(IClassFactory), &ppv);
        if (SUCCEEDED(hr))
        {
            m_pClassObject = static_cast<IUnknown*>(ppv);
            logMsg << _T("SUCCEEDED");
            LOG_DEBUG(logMsg);
        }
        else
        {
            DWORD err = GetLastError();
            logMsg << _T("FAILED: GetLastError()=") << err;
            LOG_ERROR(logMsg);
        }
    }
    

    • Proposed as answer by g012 Wednesday, April 04, 2012 9:31 AM
    • Marked as answer by dripfeed Wednesday, April 04, 2012 10:15 AM
    Monday, April 02, 2012 4:26 PM

All replies

  • Hi Dripfeed,

     

    Thank you for your posting.

     

    To troubleshoot this issue, we really need the source code and the detailed steps to reproduce the problem, so that we can investigate the issue locally. The linker you posted is blocked by our Corporation network by some security reason. Could you upload to Windows Live SkyDrive which can easily share the document with us.

     

    If we create an IMAPI2 MsftDiscRecord2 object on a MTA, which means all objects run on MTA, is the problem remaining?  

     

    Thank you for understanding.

     

    Regards,

    Yi


    Yi Feng Li [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, January 21, 2011 9:01 AM
    Moderator
  • Hi Yi

    I have uploaded the project to my SkyDrive: http://cid-2840b69141dc1012.skydrive.live.com/redir.aspx?resid=2840B69141DC1012!127

    If I create the IMAPI2 MsftDiscRecord2 object in an MTA there is no crash.

    As a short-term solution, I could ensure that the IMAPI2 objects in our production app are always created in the MTA. But, from a long-term maintenance standpoint, developers should be able to introduce COM objects without worrying about STA/MTA clashes. I would like to get to the root cause of the problem. I suspect it is my coding approach but I cannot see why.

    Thanks.

     

    Friday, January 21, 2011 10:35 AM
  • Hi Dripfeed,

     

    Thank you for the linker of the test sample.

     

    I believe the root cause of this issue is form IMAPI2. The good news is we know using MsftDiscRecord2 in MTA is the workaround so far. Based on some internal discussion, we also recommend you use IMAPI2 Objects in the MTA. I can send you the discussion via Email, so that please leave your private email here, or send an email to me (V-YIFL [AT] MICROSOFT [DOT] COM).

     

    I don’t know much about IMAPI2, we do have a forum which mainly discuss this API library. For your future concern about this API library, please visit our Optical Platform Discussion Forum.

     

    Hope the information helps.

     

    Regards,

    Yi Feng Li

     

     


    Yi Feng Li [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, January 24, 2011 3:40 AM
    Moderator
  • Hi Yi

    Thank you very much for your reply.

    Yes, please email me your internal discussion about IMPAI2 objects. We will have to move all our IMAPI2 objects into the MTA as a work-around for this problem - fair enough.

    My private email address is dripfeed [at] bigfoot [dot] com

    Thanks.

    Monday, January 24, 2011 11:54 AM
  • Hi Dripfeed,

    The email is sent.

    Have a nice day.

    Cheers,

    Yi


    Yi Feng Li [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, January 25, 2011 3:01 AM
    Moderator
  • Hi Yi.

    Thanks for your email. Unfortunately I don't think it will help me much.

    There is definitely something nasty going on with IMAPI2 in an STA and XAudio2 in the MTA because I can replicate the problem with a simple, native C++ console app.  Here is the project if you want to have a look: http://cid-2840b69141dc1012.office.live.com/self.aspx/XAudio2%20IMAPI2%20COM%20problem/Xaudio2Imapi2.zip

    As a workaround I can find all the explicit uses of IMAPI2 objects in our codebase and ensure they are also created in the MTA. But there is one more place where IMAPI2 objects are created in an STA that I cannot control:

    1. Place a blank CD-R (or erased CD-RW) in the CD-RW drive.

    2. In a WPF app, open a FolderBrowserDialog and select the CD-RW drive. At that instant, the FolderBrowserDialog WinForm control loads some IMAPI2 objects, and that is all it takes to kill my XAudio2 objects after 15 - 20 minutes.

    I cannot call FolderBrowserDialog.ShowDialog() from an MTA, only a WPF GUI thread in an STA. So my app is doomed.

    Here is a link to that project, if you want to verify it: http://cid-2840b69141dc1012.office.live.com/self.aspx/XAudio2%20IMAPI2%20COM%20problem/XAudioUnloadProblem2.zip

    Thanks.

    Tuesday, January 25, 2011 6:29 PM
  • Hi,

     

    In checking with colleagues. The recommendation is to not use the XAudio2 api’s for audio. You  should not be using XAudio2 for Windows applications. This is documented in the DirectX SDK as such.

    For audio applications, developers should be using WASAPI's for Windows 7/Vista.

    If you are facing more problems with this and it is urgent. My suggestion is to see about what options to check out for more in depth level into the problems through support.

     

    There are various support options such as advisory and per issue. Please visit the below link to see the various support options that are available to better meet your needs.

     

    http://support.microsoft.com/default.aspx?id=fh;en-us;offerprophone


    bill boyce
    • Marked as answer by dripfeed Thursday, January 27, 2011 9:58 AM
    • Unmarked as answer by dripfeed Wednesday, April 04, 2012 10:16 AM
    Wednesday, January 26, 2011 10:42 PM
    Moderator
  • Thanks Bob

    I have an open support case with Microsoft now. I guess that's the best way to pursue this.

    FYI: I have heard several times now that XAudio is a gaming API and not suitable for Windows apps. Unfortunately our desktop app (a security surveillance system) has been using XAudio (and Direct3D) for over a year now, and re-implementing the audio pipeline in DirectSound for XP and again in WASAPI for Vista/7 is not something we could justify at the moment.

    The real issue is that IMAPI2 and XAudio2 don't play well together. Hopefully my support case will get to the bottom of why.

    Thanks.

    Thursday, January 27, 2011 9:58 AM
  • A colleague of mine and me spend the whole day on a very similar (maybe even the same) problem: XAudio2_7.dll was unloaded after some idle time.

    We came to the conclusions that the root cause is that DllCanUnloadNow in xaudio2_7 always return S_OK. Which we think is a bug.

    In our application the unloading is triggered from the MFC framework. (see m_lpfnOleTermOrFreeLib). It is "activated" by AfxOleInit, which was surprisingly called when instantiating the WindowBlinds OCX control.
    Our solution/workaround is to set this function pointer back to NULL to prevent the unloading on idle.

    Harald

    PS: Xaudio2 is a system component of windows8. So I strongly doubt that it should not be used.

    Thursday, March 15, 2012 4:03 PM
  • Hi Harald

    My problem is caused by a bug in IMAP2: it causes XAudio2 to be unloaded after 15-20 minutes. I'm pretty sure that DllCanUnloadNow always returning S_OK is actually caused by the bug, not the other way around. Use Process Explorer to look at the DLLs loaded by your XAudio2 app. As soon as IMAP2.DLL shows up in that list your app has no more than 20 minutes left to live.

    I had to implement a workaround suggested by my Microsoft Support engineer. I have no idea when, if ever, the IMAP2 bug will be fixed by Microsoft, but the workaround works.

    The bug will affect anyone using XAudio2 and IMAPI2 in the same application.

    Thursday, March 15, 2012 4:35 PM
  • Hi dripfeed,

    I'm facing the same issue. Could you share the workaround you were suggested ?

    Thank you.

    Monday, April 02, 2012 12:07 PM
  • Sure @g012.

    There are two solutions, each pretty straightforward. I have tried both in production code with no ill effects. We chose Solution #2.

    Solution #1      

    Keep the XAudio2 DLL in memory to thwart COM's auto-unload function.

    1. Find the XAudio2 DLL on disk, the same physical DLL containing the registered XAudio COM classes.
    2. Explicitly load the DLL using LoadLibrary() when the XAudio2 COM objects are created, and only unload the DLL when those COM objects are released.

    Solution #2

    Keep a reference to the XAudio2 COM class object in memory to thwart COM's auto-unload function. In other words,

    1. Call CoGetClassObject with the uuid of the XAudio lib's IClassFactory.
    2. Release the class object when all XAudio2 API references are released.

    Here are the .H and .CPP files. You will probably have to remove my logging statements to get it to compile.

    #pragma once
    
    //////////////////////////////////////////////////////////////////////////
    //
    // This class provides a workaround for the incredibly damaging Windows
    // bug whereby an IMAPI2 COM object can cause the unintentional unloading
    // of the XAudio2 DLL whilst there are still XAudio2 COM objects in use.
    // 
    // Strategy:
    // Keep the XAudio2 DLL in memory to thwart COM's auto-unload function.
    // 1. Find the XAudio2 DLL on disk, the same physical DLL containing the
    //    registered XAudio COM classes.
    // 2. Explicitly load the DLL using LoadLibrary() when the XAudio2 COM
    //    objects are created, and only unload the DLL when those COM objects
    //    are released.
    //    
    // Simple, crude but effective. No observed ill effects.
    // 
    //////////////////////////////////////////////////////////////////////////
    class Xaudio2DllUnloadWorkaround_LoadLibraryVersion
    {
    public:
        Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void);
        virtual ~Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void);
    
        // Apply this workaround.
        void Invoke(bool isDebugMode);
    
    private:
        // Handle to loaded XAudio2 DLL.
        HMODULE m_hLib;
    
        // Full path to theXAudio2 DLL.
        std::basic_string<TCHAR> m_dllFullPath;
    
        bool RegistryFindDllPath(REFCLSID clsidXaudio2Dll);
        void ExplicitLoad(void);
        void ExplicitUnload(void);
    };
    
    
    
    //////////////////////////////////////////////////////////////////////////
    //
    // This class provides a workaround for the incredibly damaging Windows
    // bug whereby an IMAPI2 COM object can cause the unintentional unloading
    // of the XAudio2 DLL whilst there are still XAudio2 COM objects in use.
    //
    // This is the recommended workaround by Microsoft's XNA team.
    //
    // Keep a reference to the XAudio2 COM class object in memory to thwart
    // COM's auto-unload function. In other words,
    // 1. Call CoGetClassObject with the uuid of the XAudio lib's IClassFactory.
    // 2. Release the class object when all XAudio2 API references are released.
    //
    // Simple, crude but effective. No observed ill effects.
    // 
    //////////////////////////////////////////////////////////////////////////
    class Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion
    {
    public:
        Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void);
        virtual ~Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void);
    
        // Apply this workaround.
        void Invoke(bool isDebugMode);
    
    private:
        IUnknown* m_pClassObject;
    };
    

    //////////////////////////////////////////////////////////////////////////
    //
    // Xaudio2DllUnloadWorkaround_LoadLibraryVersion implementation
    //
    //////////////////////////////////////////////////////////////////////////
    
    Xaudio2DllUnloadWorkaround_LoadLibraryVersion::Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void)
        :m_hLib(NULL)
    {
        LOG_DEBUG(_T("<<Created>>"));
    }
    
    
    Xaudio2DllUnloadWorkaround_LoadLibraryVersion::~Xaudio2DllUnloadWorkaround_LoadLibraryVersion(void)
    {
        ExplicitUnload();
        LOG_DEBUG(_T("<<Destroyed>>"));
    }
    
    
    void Xaudio2DllUnloadWorkaround_LoadLibraryVersion::Invoke(bool isDebugMode)
    {
        // Choose the debug or release version of the XAudio2 DLL.
        REFCLSID clsid = isDebugMode ? __uuidof(XAudio2_Debug) : __uuidof(XAudio2);
    
        if (RegistryFindDllPath(clsid))
        {
            ExplicitLoad();
        }
        else
        {
            // Nasty. There will be consequences.
            LOG_ERROR(m_pLogger,
                _T("Could not apply the workaround. ")
                _T("Application may crash if CD-RW features are used!"));
        }
    }
    
    
    //////////////////////////////////////////////////////////////////////////
    //
    // Use the XAudio2 CLSID to find the XAudio2 DLL path in the registry.
    // 
    //////////////////////////////////////////////////////////////////////////
    bool Xaudio2DllUnloadWorkaround_LoadLibraryVersion::RegistryFindDllPath(REFCLSID clsidXaudio2Dll)
    {
        // Construct a string representation of the registry key in which the
        // path to the desired XAudio2 COM DLL should be located.
    
        LPOLESTR szClsid;
        StringFromCLSID(clsidXaudio2Dll, &szClsid);
    
        std::basic_stringstream<TCHAR> subKey;
        subKey << _T("\\CLSID\\") << szClsid << _T("\\InProcServer32");
        CoTaskMemFree(szClsid);
    
        bool isDllFound = false; // assume the worst
    
    
        // Open the XAudio2 DLL's COM registry key.
    
        HKEY hKey;
        LONG ret = RegOpenKeyEx(HKEY_CLASSES_ROOT, subKey.str().c_str(), 0, KEY_READ, &hKey);
        if (ret != ERROR_SUCCESS)
        {
            DWORD err = GetLastError();
            LOG_WARNING(_T("XAudio2 DLL: find by CLSID: FAILED: GetLastError()=") << err);
            
            // The key must exist if DirectX is installed!
            assert(ret != ERROR_SUCCESS);
        }
        else
        {
            // Found the registry key. Now read its value: it should be the full
            // path the the XAudio2 COM DLL.
    
            TCHAR buf[_MAX_PATH];
            DWORD bufSize = sizeof(buf);
    
            ret = RegQueryValueEx(hKey, NULL, NULL, NULL, (LPBYTE)buf, &bufSize);
            if (ret != ERROR_SUCCESS)
            {
                DWORD err = GetLastError();
                LOG_WARNING(_T("XAudio2 DLL: path retrieval: FAILED: GetLastError()=") << err);
    
                // The DLL path must exist if DirectX is installed!
                assert(ret != ERROR_SUCCESS);
            }
            else
            {
                isDllFound = true;
                m_dllFullPath = buf; // Save the path for logging.
                LOG_DEBUG(_T("XAudio2 DLL: path found: \"") << m_dllFullPath << _T("\""));
            }
    
            RegCloseKey(hKey);
        }
    
        return isDllFound;
    }
    
    
    //////////////////////////////////////////////////////////////////////////
    //
    // Forcibly increment the internal reference count in the XAudio2 DLL by
    // explicitly loading it. This is a crude but effective way to prevent
    // it being unloading by the COM run time.
    // 
    //////////////////////////////////////////////////////////////////////////
    void Xaudio2DllUnloadWorkaround_LoadLibraryVersion::ExplicitLoad(void)
    {
        StringStreamType logMsg;
    
        if (m_pLogger->IsDebugEnabled())
        {
            logMsg << _T("LoadLibrary: \"") << m_dllFullPath << _T("\": ");
        }
    
        // Load the XAudio2 DLL
        m_hLib = LoadLibrary(m_dllFullPath.c_str());
        if (m_hLib != NULL)
        {
            logMsg << _T("SUCCEEDED");
            LOG_DEBUG(logMsg);
        }
        else
        {
            DWORD err = GetLastError();
            logMsg << _T("FAILED: GetLastError()=") << err;
            LOG_WARNING(logMsg);
        }
    }
    
    
    void Xaudio2DllUnloadWorkaround_LoadLibraryVersion::ExplicitUnload(void)
    {
        if (m_hLib != NULL)
        {
            StringStreamType logMsg;
    
            if (m_pLogger->IsDebugEnabled())
            {
                logMsg << _T("FreeLibrary: \"") << m_dllFullPath << _T("\": ");
            }
    
            // Unload the XAudio2 DLL
            if (FreeLibrary(m_hLib))
            {
                logMsg << _T("SUCCEEDED");
                LOG_DEBUG(logMsg);
            }
            else
            {
                DWORD err = GetLastError();
                logMsg << _T("FAILED: GetLastError()=") << err;
                LOG_WARNING(logMsg);
            }
        }
    }
    
    
    //////////////////////////////////////////////////////////////////////////
    
    
    
    Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion::Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void)
        :m_pClassObject(NULL)
    {
        LOG_DEBUG(_T("<<Created>>"));
    }
    
    
    Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion::~Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion(void)
    {
        SAFE_RELEASE(m_pClassObject);
        LOG_DEBUG(_T("<<Destroyed>>"));
    }
    
    
    void Xaudio2DllUnloadWorkaround_CoGetClassObjectVersion::Invoke(bool isDebugMode)
    {
        StringStreamType logMsg;
        logMsg << _T("CoGetClassObject: ");
    
        // Choose the debug or release version of XAudio2.
        REFCLSID clsid = isDebugMode ? __uuidof(XAudio2_Debug) : __uuidof(XAudio2);
    
        LPVOID ppv;
        HRESULT hr = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, __uuidof(IClassFactory), &ppv);
        if (SUCCEEDED(hr))
        {
            m_pClassObject = static_cast<IUnknown*>(ppv);
            logMsg << _T("SUCCEEDED");
            LOG_DEBUG(logMsg);
        }
        else
        {
            DWORD err = GetLastError();
            logMsg << _T("FAILED: GetLastError()=") << err;
            LOG_ERROR(logMsg);
        }
    }
    

    • Proposed as answer by g012 Wednesday, April 04, 2012 9:31 AM
    • Marked as answer by dripfeed Wednesday, April 04, 2012 10:15 AM
    Monday, April 02, 2012 4:26 PM
  • Thanks a lot dripfeed, it looks like it's working fine now. Haven't had the crash come back yet. Many thanks !

    Wednesday, April 04, 2012 9:28 AM
  • Glad I could help!
    Wednesday, April 04, 2012 10:15 AM