Application Startup and Shutdown events are now available in COM+

This article was previously published under Q303890
This article has been archived. It is offered "as is" and will no longer be updated.
Beginning with COM+ hotfix rollup 14, COM+ provides Startup and Shutdown events when a server process (Dllhost.exe) starts and ends. This feature enables any COM+ components that are installed in a COM+ server application (referred to as server components hereafter) to run custom initialization and clean up code.

For more information about the latest service pack for Windows 2000, click the following article number to view the article in the Microsoft Knowledge Base:
260910 How to obtain the latest Windows 2000 service pack
More information
To use this new feature, obtain Microsoft COM+ hotfix rollup 14. For more information, click the following article number to view the article in the Microsoft Knowledge Base:
302845 Availability of Windows 2000 Post-Service Pack 2 COM+ Hotfix Rollup Package 14
Note The COM+ hotfix rollup package does not register any system dynamic-link libraries (DLLs). To use this feature, you must register the Comsvcs.dll file manually (for example, use the regsvr32 C:\winnt\system32\comsvcs.dll command at a command prompt).

Using the Startup and Shutdown events, you have the opportunity to initialize resources, create connections, initialize shared data, and run clean up code. However, you should not access any states that are specific to COM+ because server components have not been instantiated yet.

In addition, the code in the Startup and Shutdown events should return as quickly as possible because the system only waits 90 seconds for Dllhost.exe to prepare to accept activations after the system starts the process. If Dllhost.exe does not signal that it is ready within 90 seconds, the system kills the process; thus, all initialization processing needs to be completed within this time frame.

Each server component that wants to participate in initialization must support the IProcessInitializer additional interface. On DllHost startup, COM+ creates all server components that requested this service and calls QueryInterface for the IProcessIntializer interface and calls the Startup function. Similarly, when the DllHost process is shut down, it calls the Shutdown function on those previously stored interface pointers.

IProcessInitializer has following signature:
[    object,    pointer_default(unique),    uuid(1113f52d-dc7f-4943-aed6-88d04027e32a)]interface IProcessInitializer : IUnknown{    HRESULT Startup([in]IUnknown *punkProcessControl);    HRESULT Shutdown();}				
Currently, the Startup parameter is NULL. In future releases, this parameter may expose more functionality.

By default, this feature is not enabled. To enable it, you must set the InitializesServerApplication property on the intended server component to TRUE. Use the VCExplore.exe or VBExplore.exe file of the COM+ Administration samples from the Platform SDK, or use following script:
' Assume that the script file is named as ProcInit.vbs'   At a command prompt, type the following command:'   ProcInit <appname>, <progid>, <value>" (without the  quotation marks)' where:'  - <appname> is the name of the application.'  - <progid> is the ProgID of the component that you want to change.'  - <value> is either "0" for False or "1" for True.   Set objArgs = WScript.Arguments   If objArgs.Count <> 3 Then       WScript.Echo "ProcInit"       WScript.Echo ""       WScript.Echo "Usage:"       WScript.Echo "ProcInit [appname], [progid], [value]"       WScript.Echo "[appname]: Name of the application"       WScript.Echo "[progid]:  ProgID of the component to change."       WScript.Echo "[value]:   0 for False, 1 for True"       WScript.Quit (0)   End If   applicationName = objArgs(0)   componentProgID = objArgs(1)   ProcInit = objArgs(2)   Set catalog = CreateObject("COMAdmin.COMAdminCatalog.1")   Set applications = catalog.GetCollection("Applications")   applications.Populate   numApplications = applications.Count   For i = numApplications - 1 To 0 Step -1       If applications.Item(i).Value("Name") = applicationName Then           Set application = applications.Item(i)           Exit For       End If   Next   Set components = applications.GetCollection("Components", _   application.Value("ID"))   components.Populate   numComponents = components.Count    For i = numApplications - 1 To 0 Step -1        If LCase(applications.Item(i).Value("Name")) = LCase(applicationName) Then            Set application = applications.Item(i)            Exit For        End If    Next    Set components = applications.GetCollection("Components", application.Value("ID"))    components.Populate    numComponents = components.Count        For i = numComponents - 1 To 0 Step -1        WScript.Echo components.Item(i).Name        If LCase(components.Item(i).Name) = LCase(componentProgID) Then            WScript.Echo "Set InitializesServerApplication to " + ProcInit + " for " _            + components.Item(i).Name            Set component = components.Item(i)            component.Value("InitializesServerApplication") = ProcInit      End If  Next            components.SaveChanges   applications.SaveChanges
You can also accomplish similar but limited functionality if you use the following undocumented and unsupported technique:

Note Microsoft does not intend to support the following technique in the future. This interface will not work in future versions of Windows and may be deprecated.
  1. Write an in-process COM object that supports the following interface:
    [ object, local, uuid(000001d4-0000-0000-C000-000000000046),    pointer_default(unique) ]interface ISurrogateService : IUnknown{        HRESULT Init( [in] REFGUID rguidProcessID,                      [in] LPVOID reserved,                      [out] BOOL* pfApplicationAware );        HRESULT ApplicationLaunch( [in] REFGUID rguidApplID,                                   [in] DWORD appType );        HRESULT ApplicationFree( [in] REFGUID rguidApplID );        HRESULT CatalogRefresh( [in] ULONG ulReserved );        HRESULT ProcessShutdown( [in] DWORD shutdownType );};					
  2. To install this component in the COM+ catalog and enable this functionality, use the following undocumented interfaces:
    [ object, uuid(4915a36f-0138-11d1-8d56-00c04fc2e0c7),local ]interface ISimpleTableDispenser : IUnknown{    void Reserved();HRESULT GetClientTable ([in] REFGUID i_did, [in] REFGUID i_tid, [in] LPVOID  i_QueryData, [in] LPVOID  i_QueryMeta, [in] DWORD  i_eQueryFormat, [in] DWORD  i_fTable, [out] LPVOID* o_ppv);}; [ object, uuid(0032d55a-c320-11d1-8dec-00c04fc2e0c7),local ]interface ISimpleTableWrite : IUnknown{ HRESULT PopulateCache();    void Reserved0(); HRESULT MoveToNextRow();    void Reserved1();    void Reserved2();       void Reserved3();    void Reserved4();    void Reserved5();    void Reserved6();     HRESULT UpdateStore();    void Reserved7();    void Reserved8(); HRESULT AddRowForInsert();    void Reserved9();        HRESULT DeleteRow(); HRESULT SetRow();    void Reserved10();     HRESULT SetWriteColumn([in] ULONG i_iColumn, [in] ULONG i_cb, [in] LPVOID i_pv);}; typedef struct{ const void * pData; DWORD   eOperator; ULONG   iColumn; DWORD   dbType; ULONG   cbSize;} STQueryCell; didCOMSERVICES GUID {6E38D3C4-C2A7-11d1-8DEC-00C04FC2E0C7}tidCOMSERVICES_SERVICES GUID {1DE5A441-CF10-11d1-8B87-00C04FD7A924}STQueryCell aQueryCells[] ={   { (void *)&guidAppID, 0, 0, DBTYPE_GUID, sizeof(GUID) },  { (void *)&guidCLSID, 0, 1, DBTYPE_GUID, sizeof(GUID) }};ULONG cQueryCells = 2					
  3. Create the simple table dispenser, and call the GetClientTable method to get the simple table write interface STDispenser CLSID {15b0bb4c-0f7d-11d1-b21f-00c04fb9473f} as follows:
    pISTDisp->GetClientTable(didCOMSERVICES, tidCOMSERVICES_SERVICES, (LPVOID)aQueryCells, (LPVOID)(ULONG_PTR)cQueryCells, 1, 0, (LPVOID*)&pISTWrite);					
  4. Populate the cache, and move to the next row. To add a new row, use the following code:
    pISTWrite->AddRowForInsert(); pISTWrite->SetWriteColumn(0, 0, (LPVOID) &guidAppID);pISTWrite->SetWriteColumn(1, 0, (LPVOID) &guidCLSID);pISTWrite->SetRow();						
    To remove a row, use the following code:
  5. When you are finished, update the store.
The documentation of this interface and technique is not intended for general independent software vendor (ISV) use. Any use of this documentation is at the developer's own risk. It has not been subjected to Microsoft's full test suite. Microsoft does not intend to support it as a Win32 application programming interface (API) in the future. Microsoft does expose similar and greater functionality with the IProcessInitializer interface that was described earlier. Microsoft suggests that ISVs use the IProcessInitializer interface to take advantage of the process initialization and clean up feature.
kbIISCom kbWin2000preSP3COMRollup14Fix

Article ID: 303890 - Last Review: 10/26/2013 06:58:00 - Revision: 4.0

Microsoft COM+ 1.0

  • kbnosurvey kbarchive kbinfo kbwin2000sp3fix KB303890