How do i know when it’s safe to shut-down my out-of-process COM automation server?

Short Version

As soon as my COM automation server starts up, and registers its class objects, i check if there are any references that need to keep me alive. There aren’t. So i shutdown – before anything gets a chance to happen.

What is the pattern to stick around long enough without any references to any of my objects, but then not stick around after there are no longer any references to any of my objects?

Long Version

If you are an out-of-process COM automation server, you have a number of goals. Assume we are implementing a Single-Threaded Apartment (STA) server; meaning we will be pumping a message loop.

My sample implemenatation i think follows the standard route from our WinMain:

Note: I will use a C#-like syntax, but i am not referring to managed code. I’m showing you code that looks like C# so you don’t have to see my native Delphi. Additionally I am asking a language-agnostic question; just like COM is desgined to be language-agnostic – that’s its entire reason for living – binary interoperability

  1. Use CoRegisterClassObject to register my “class object”; which is an object that implements IClassFactory:

    // Create an instance of our class factory
    IClassFactory classFactory = new ApeFactory();
    
    // Initialize the COM infrastructure
    CoInitialize(null);
    
    // Register our class factory, and keep the cookie to unregister it later
    DWORD dwCookie;
    HRESULT hr = CoRegisterClassObject(classFactory, out dwCookie);
    

    This class object will then be called upon to create our Ape object.

    Our IClassFactory does not implement reference counting with its AddRef/Release methods. This is because for the duration that the COM Service Manager (CSM) is holding onto a reference to our ApeFactory our application can’t shutdown (because someone’s using one of our objects). But the CSM won’t release the references to our class objects until we call CoRevokeClassObject. This catch-22 is why IExternalConnection was created:

    IExternalConnection = interface
    {
       // Increments the count of an object's strong external connections.
       DWORD AddConnection(DWORD extconn, DWORD reserved);
    
       // Decrements the count of an object's strong external connections.
       DWORD ReleaseConnection(DWORD extconn, DWORD reserved,BOOL fLastReleaseCloses);
    }
    

    This interface lets us:

    • differentiate between “external” people referencing our objects
    • as opposed to just COM holding onto our objects

    We only really need to keep our automation server alive if there is some external client somewhere who got ahold of our IClassFactory. If it’s just COM holding onto our object, then we can shutdown fine.

    So COM will tell us when some “external” user is referencing our class factory. This means we can then maintain a global lock count:

    Int64 g_ModuleReferenceCount;     
    

    Then perform an atomic increment during AddConnection, and an atomic decrement during ReleaseConnection.

  2. Start our message loop

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
    {
       TranslateMessage(ref msg);
       DispatchMessage(ref msg);
    
       // check if our external reference counts have gone to zero; meaning we can shutdown
       if (g_ExternalReferenceCount <= 0)
          PostQuitMesage(msg.wParam);
    }
    
  3. Unregister our class objects during our orderly shutdown

    // Unregister our ApeFactory IClassFactory
    CoRevokeClassObject(dwCookie);
    
  4. And our automation server gracefully shuts down.

Except for the race condition

If you look at the code together you can see the race condition:

void WinMain()
{
   // Register our class factory, and keep the cookie to unregister it later
   DWORD dwCookie;
   HRESULT hr = CoRegisterClassObject(classFactory, out dwCookie);

   // Setup our count of external references
   Int64 g_ModuleReferenceCount = 0; 

   MSG msg;
   while (GetMessage(&msg, 0, 0, 0))
   {
      TranslateMessage(ref msg);
      DispatchMessage(ref msg);

      // check if our external reference counts have gone to zero; meaning we can shutdown
      if (g_ExternalReferenceCount <= 0)
         PostQuitMesage(msg.wParam);
   }

   // Unregister our ApeFactory IClassFactory
   CoRevokeClassObject(dwCookie);
}

Our module reference count starts at zero, and so before COM has a chance to even create our ApeFactory class object, we’re unregistering the class factory and shutting down.

What is the pattern to employ here to stick around at least long enough to server some COM classes and COM objects?

“What about LockServer?”

I’m not muddying the waters with LockServer. As Don Box noted:

Although the technique of using IExternalConnection on class objects has worked since the early days of COM, few implementors actually used this technique. Instead, most servers would typically ignore outstanding external references to class objects and prematurely terminate their server processes. This situation was encouraged by the presence of the method LockServer on the IClassFactory interface, which lulled developers into thinking that clients could actually ensure that a server would remain running. While most server implementors would properly lock their module in their LockServer methods, there was no reliable way for a client to call this method.

Acknowledging that many server implementations were not using IExternalConnection to manage server lifetime properly, the Windows NT 4.0 release of COM introduced the following enhancement to compensate for these naive implementations:

When marshaling a reference to a class object in response to a CoGetClassObject call, the SCM will call the class object’s IClassFactory::LockServer method. Since the vast majority of servers implement IClassFactory on their class objects, this enhancement to the COM runtimes repairs most defects. However, if a class object does not export the IClassFactory interface or if the server must also execute on pre-Window NT 4.0 releases of COM, the IExternalConnection technique must be used.

I want to pretend that LockServer is not an option because:

  • a) i want to underststand the correct way to do it
  • b) i don’t understand how LockServer doesn’t have the same race condition

https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coregisterclassobject

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật