DllCanUnloadNow function isn't invoked after using classic COM components in .NET applications

 

COM Interop is a technic for calling an unmanaged COM component from a .NET application, please refer to https://msdn.microsoft.com/en-us/magazine/dvdarchive/cc163494.aspx to learn how to implement it. DllCanUnloadNow is an important function which should be implemented and exported by the COM component.

However, you may be aware that the DllCanUnloadNow function isn't invoked with the managed client if we don't explicitly call CoUninitialize function. In this article, I'd like to explain why the DllCanUnloadNow function isn't invokved.

How DllCanUnloadNow is invoked in native client

At first, let's have a look at how the native client is implemented in C++ code.

void main(void)

{

     // Declare and HRESULT and a pointer to the Simple_ATL interface

     HRESULT hr;

     IFirst_ATL *IFirstATL = NULL;

     // Now we will intilize COM

     hr = CoInitialize(0);

     // Use the SUCCEDED macro and see if we can get a pointer to

     // the interface

     if(SUCCEEDED(hr))

     {

           hr = CoCreateInstance( CLSID_First_ATL, NULL, CLSCTX_INPROC_SERVER,

                IID_IFirst_ATL, (void**) &IFirstATL);

          

           // If we succeeded then call the AddNumbers method, if it failed

           // then display an appropriate message to the user.

           if(SUCCEEDED(hr))

           {

                long ReturnValue;

                hr = IFirstATL->AddNumbers(5, 7, &ReturnValue);

                cout << "The answer for 5 + 7 is: " << ReturnValue << endl;

                hr = IFirstATL->Release();

           }

           else

           {

                cout << "CoCreateInstance Failed." << endl;

           }

     }

     // Uninitialize COM

     CoUninitialize();

}

Please note, we never forget to invoke CoUninitialize function after we finish using the COM object. The DllCanUnloadNow function will be triggered when calling CoUninitialize function, please refer to the following callstack.

0:000> k

ChildEBP RetAddr

0012fe94 776bb927 simple_atl!DllCanUnloadNow [C:\comtest\COM_ATL_Object_Src\Simple_ATL.cpp @ 43]

0012fea8 77691b90 ole32!CClassCache::CDllPathEntry::CanUnload_rl+0x3b 

0012fef4 7769182a ole32!CClassCache::CleanUpDllsForApartment+0x12e

0012ff20 7769174c ole32!FinishShutdown+0xd7

0012ff40 776bce20 ole32!ApartmentUninitialize+0x94 

0012ff58 776bcdd2 ole32!wCoUninitialize+0x7d 

0012ff74 00401087 ole32!CoUninitialize+0x65 

 

How to make the DllCanUnloadNow be invoked in managed client

 

At first, let's have a look at the common managed client code.

class Test

{

    static void Main()

    {

        MyCOMServerClass comServer = new MyCOMServerClass();

        comServer.MyCOMServerMethod();

        System.Runtime.InteropServices.Marshal.ReleaseComObject(comServer);

    }

}

In COM Interop, we don't need to call CoInitialize function as the .NET framework will call it automatically. But .NET framework won't call CoUninitialize automatically; this is why the DllCanUnloadNow function will never be triggered.

So, we need to call CoUninitialize explicitly by using P/Invoke of ole32.dll. Please refer to the following codes.

class Test

{

    [DllImport("ole32.dll")]

    extern static void CoUninitialize();

    static void Main()

    {

        MyCOMServerClass comServer = new MyCOMServerClass();

        comServer.MyCOMServerMethod();

        System.Runtime.InteropServices.Marshal.ReleaseComObject(comServer);

        CoUninitialize();

    }

}

 

Note: In this scenario, we need to call the CoUninitialize function when the application is closed. Although .NET will delay COM dll unloading, with CoUninitialize we can ensure the DllCanUnloadNow will be hit before process exits. To unload COM dll without such delay, please consider using CoFreeUnusedLibrariesEx(0,0).

References

DllCanUnloadNow Function: https://msdn.microsoft.com/en-us/library/ms690368(VS.85).aspx

CoUninitialize Function: https://msdn.microsoft.com/en-us/library/ms688715(VS.85).aspx

How to use COM components in Visual Studio .NET: https://support.microsoft.com/kb/816152

Beginner's Tutorial: COM/ATL Simple Project: https://www.codeproject.com/KB/atl/com_atl.aspx

Improving Interop Performance: https://msdn.microsoft.com/en-us/library/ms998551.aspx

Regards,

Zhixing Lv