Class Emulation

It is often the case that class implementors wish to deploy new versions of an existing class to repair defects or to add enhanced functionality. It is useful to give these new implementations new CLSIDs to allow clients to indicate explicitly which version is required. For example, consider what happens when a second version of a class is deployed. If a new CLSID is used to identify the new class (e.g., CLSID_Chimp2), clients that explicitly wish to use the new version would use the new CLSID at activation time:

// new client
IApe *pApe = 0;
hr = CoCreateInstance(CLSID_Chimp2, 0, CLSCTX_ALL, 
                     IID_Ape, (void**)&pApe);

The use of a second CLSID ensures that clients do not accidentally get old versions of the Chimp class. However, legacy clients still make activation requests using the old CLSID:

// old client
IApe *pApe = 0;
hr = CoCreateInstance(CLSID_Chimp, 0, CLSCTX_ALL, 
                    IID_Ape, (void**)&pApe);

To continue to support legacy clients, the Chimp implementor needs to keep the original CLSID in the Registry in order to satisfy these activation requests. If the semantics of the class have changed, then the original server would also need to remain available for these clients. However, it is often the case that the semantics are simply extended. In this case, it would be preferable simply to reroute the legacy activation requests to create instances of the new class.

To allow the implementor of the new version of the class to satisfy activation requests for other CLSIDs transparently, COM supports the notion of class emulation. Class emulation allows a component implementor to indicate that an old CLSID has been replaced by a new alternative CLSID that emulates the original class’s semantics. This allows legacy clients that make activation calls using the original CLSID to get instances of the new updated class. To indicate that a class has a new alternative version, COM provides the following API function:

HRESULT CoTreatAsClass([in] REFCLSID rclsidOld,
                     [in] REFCLSID rclsidNew);

Assuming that Chimp2 is a new version of the class Chimp, the following code informs COM to alter activation requests for Chimp to become activation requests for Chimp2:

// cause Chimp activation calls to activate Chimp2
HRESULT hr = CoTreatAsClass(CLSID_Chimp, CLSID_Chimp2);

This API routine inserts the following registry key:

[HKCR\CLSID\{CLSID_Chimp}\TreatAs]17
@={CLSID_Chimp2}

Calling CoTreatAsClass with CLSID_NULL as the second parameter removes the TreatAs setting:

// cause Chimp activation calls to activate Chimps
HRESULT hr = CoTreatAsClass(CLSID_Chimp, CLSID_NULL);

This call restores the original implementation of the class to its preemulation status. Clients can interrogate the emulation setting of a given class using the CoGetTreatAsClass API function:

HRESULT CoGetTreatAsClass([in] REFCLSID rclsidOld,
                        [out] REFCLSID *pclsidNew);

If the requested class is being emulated by another class, the emulating class’s CLSID will be returned through the second parameter and the routine will return S_OK. If the requested class is not being emulated by another class, the original CLSID will be returned through the second parameter and the routine will return S_FALSE. It is worth noting that, at the time of this writing, class emulation does not work as expected for remote activation
requests.

17 Note that CLSID_Chimp and CLSID_Chimp2 are shorthand notation for the actual 32-digit GUIDs in canonical form.

© 1998 by Addison Wesley Longman, Inc. All rights reserved.