Application Startup and Shutdown events are now available in COM+ (303890)



The information in this article applies to:

  • Microsoft COM+ 1.0

This article was previously published under Q303890

SUMMARY

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:
    pISTWrite->DeleteRow();
    					
  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.

Modification Type:MajorLast Reviewed:4/17/2006
Keywords:kbinfo kbWin2000sp3fix KB303890 kbAudDeveloper