Inheritence & Virtual functions with COM/ATL
-
Disclaimer: I am not an ATL guru....want to be but that is another disclaimer! I have not been able to find any samples to follow that use inheritence and virtual functions in com using atl. I guess the first question should be can it be done? Here is what I am trying to accomplish... I have a parent object (PObject) that has a virtual function (ExecuteService), and several pure virtual functions such as Display(). I want to extend that class with several subclasses that would implement the specific service. (We need an insert diagram here button on this forum!) I want a factory which takes a string or int which gives me the correct subclass depending upon the parameter value. This should work: void RunService(CString name) { IServiceFactoryPtr ptrFact(__uuidof(ServiceFactory)); IPObjectPtr ptrObj = ptrFact->GetServiceByName(name.AllocSysString()); ptrObj->ExecuteService(); } I've been able to get the factory working, I actually end up with the correct object when I make the call to GetServiceByName, however when calling ExecuteService I get an exception. So, how do I set up the classes using ATL to get this to work??? It's a basic factory pattern. Good basis for an article if someone could get me past this hump! Thanks, ed
-
Disclaimer: I am not an ATL guru....want to be but that is another disclaimer! I have not been able to find any samples to follow that use inheritence and virtual functions in com using atl. I guess the first question should be can it be done? Here is what I am trying to accomplish... I have a parent object (PObject) that has a virtual function (ExecuteService), and several pure virtual functions such as Display(). I want to extend that class with several subclasses that would implement the specific service. (We need an insert diagram here button on this forum!) I want a factory which takes a string or int which gives me the correct subclass depending upon the parameter value. This should work: void RunService(CString name) { IServiceFactoryPtr ptrFact(__uuidof(ServiceFactory)); IPObjectPtr ptrObj = ptrFact->GetServiceByName(name.AllocSysString()); ptrObj->ExecuteService(); } I've been able to get the factory working, I actually end up with the correct object when I make the call to GetServiceByName, however when calling ExecuteService I get an exception. So, how do I set up the classes using ATL to get this to work??? It's a basic factory pattern. Good basis for an article if someone could get me past this hump! Thanks, ed
Take a look through the MS documentation (or other sources) for topics pertaining to Aggregation and/or containment. It is possible mimic inheritence in ATL by having one COM component aggregate the implementation of another COM component, expose these interfaces, and extend upon them by exposing additional interfaces. This is how inheritence in practiced in ATL and more specifically COM.
-
Disclaimer: I am not an ATL guru....want to be but that is another disclaimer! I have not been able to find any samples to follow that use inheritence and virtual functions in com using atl. I guess the first question should be can it be done? Here is what I am trying to accomplish... I have a parent object (PObject) that has a virtual function (ExecuteService), and several pure virtual functions such as Display(). I want to extend that class with several subclasses that would implement the specific service. (We need an insert diagram here button on this forum!) I want a factory which takes a string or int which gives me the correct subclass depending upon the parameter value. This should work: void RunService(CString name) { IServiceFactoryPtr ptrFact(__uuidof(ServiceFactory)); IPObjectPtr ptrObj = ptrFact->GetServiceByName(name.AllocSysString()); ptrObj->ExecuteService(); } I've been able to get the factory working, I actually end up with the correct object when I make the call to GetServiceByName, however when calling ExecuteService I get an exception. So, how do I set up the classes using ATL to get this to work??? It's a basic factory pattern. Good basis for an article if someone could get me past this hump! Thanks, ed
Component Categories
As this chapter has emphasized, the basic COM activation primitives require the caller to know the precise class name in order to create new instances. However, it is sometimes useful simply to request that any class that adheres to some semantic constraint is suitable. In addition, it could be useful to know what services a class requires from its clients prior to issuing an activation request to avoid creating objects that the client is not prepared to support properly. These problems motivate the concept of component categories.
COM allows implementors to group related COM classes into logical groups or component categories. Often, all classes within a category will implement the same set of interfaces. However, simply partitioning the class space based on which interfaces each class implements does not provide the proper granularity for many applications. Component categories act as metainformation that indicates which classes are compatible with particular semantic constraints.
A component category is a group of logically related COM classes that share a common category ID or CATID. CATIDs are GUIDs that are stored in the Registry as attributes of a class. Each class can have two subkeys: Implemented Categories and Required Categories. Assume that there are two categories of components: Simians and Mammals. These two categories would each have a unique CATID (CATID_Simians and CATID_Mammals, respectively). Assuming that the class Chimp is a member of each of these categories, the Chimp’s Implemented Categories registry key would contain each GUID as an individual subkey:
[HKCR\CLSID\{CLSID_Chimp}\Implemented Categories\{CATID_Mammals}]
[HKCR\CLSID\{CLSID_Chimp}\Implemented Categories\{CATID_Simians}]
These registry entries are typically added at self-registration time. Each known component category on a system has an entry under
HKEY_CLASSES_ROOT\Component Categories
Each category has its own unique subkey named by its CATID. Underneath its subkey, each category has one or more named values that contain the human-readable description of the category. For example, the two categories shown would require the following registry entries:
[HKCR\Component Categories\{CATID_Mammals}] 409="Bears live young" [HKCR\Component Categories\{CATID_Simians}] 409="Eats Bananas"
Not
-
Disclaimer: I am not an ATL guru....want to be but that is another disclaimer! I have not been able to find any samples to follow that use inheritence and virtual functions in com using atl. I guess the first question should be can it be done? Here is what I am trying to accomplish... I have a parent object (PObject) that has a virtual function (ExecuteService), and several pure virtual functions such as Display(). I want to extend that class with several subclasses that would implement the specific service. (We need an insert diagram here button on this forum!) I want a factory which takes a string or int which gives me the correct subclass depending upon the parameter value. This should work: void RunService(CString name) { IServiceFactoryPtr ptrFact(__uuidof(ServiceFactory)); IPObjectPtr ptrObj = ptrFact->GetServiceByName(name.AllocSysString()); ptrObj->ExecuteService(); } I've been able to get the factory working, I actually end up with the correct object when I make the call to GetServiceByName, however when calling ExecuteService I get an exception. So, how do I set up the classes using ATL to get this to work??? It's a basic factory pattern. Good basis for an article if someone could get me past this hump! Thanks, ed
Ed- Can you post the class signature of the derived class (i.e its inheritance list ) as well as whether or not you have used ATL_NO_VTABLE ? One place to look at is : 1. Do you have ExecuteService defined as one of the methods in the interface definition for IPObject ? HTH - Vivek
-
Ed- Can you post the class signature of the derived class (i.e its inheritance list ) as well as whether or not you have used ATL_NO_VTABLE ? One place to look at is : 1. Do you have ExecuteService defined as one of the methods in the interface definition for IPObject ? HTH - Vivek
Thanks for offering to take a look at it. Rather than posting all the code here I've put it into a zip on my site to download. test.zip The zip contains 2 zip files. One is the test client and the other the servicefactory. I'm using VC6 and WTL7 (I'm in love with CString!). ServiceFactory has a method GetService(BSTR name, IPObject**ppObj) which will get either the FoodService or the BeverageService depending on the string passed in. PObject has a virtual method ExecuteServie(BSTR param) which calls a virtual method Display. ExecuteService is only implemented in PObject. Display is only implemented in the subclasses. If you step through the test client you can actually see that the right object is created however when I called ExecuteService it throws an unhandled exception. Thanks again! :rose: ed
-
Thanks for offering to take a look at it. Rather than posting all the code here I've put it into a zip on my site to download. test.zip The zip contains 2 zip files. One is the test client and the other the servicefactory. I'm using VC6 and WTL7 (I'm in love with CString!). ServiceFactory has a method GetService(BSTR name, IPObject**ppObj) which will get either the FoodService or the BeverageService depending on the string passed in. PObject has a virtual method ExecuteServie(BSTR param) which calls a virtual method Display. ExecuteService is only implemented in PObject. Display is only implemented in the subclasses. If you step through the test client you can actually see that the right object is created however when I called ExecuteService it throws an unhandled exception. Thanks again! :rose: ed
Ed - The problem is you are trying to call a ExecuteService using a IFoodService pointer. This would work if IFoodService were derived from IPObject, but it is not ! This means that the vtable entry for ExecuteService just does not exist for IFoodService. Typecasting it via reinterpret_cast wont fix it either. I made the following change and it worked for me. In File factory.cpp Note that we now QI for IPObject instead of IFoodService
return CComCreator< CComObject >::CreateInstance(NULL, IID_IPObject, reinterpret_cast(ppPObj) );
This would ensure that the interface pointer for IPObject is returned instead of IFoodService.
If you really want IFoodService on the client, you can always QI for it from IPObject. Suggested change Change the interface map for CFood and CBevBEGIN_COM_MAP(CFoodService) COM_INTERFACE_ENTRY(IFoodService) COM_INTERFACE_ENTRY_CHAIN(CPObject) END_COM_MAP()
This is because CFoodService does not directly implement IPObject but rather uses the implementation already defined in a base class. The QI then searches for the implementation in the base class. Hope that helps- Vivek -
Ed - The problem is you are trying to call a ExecuteService using a IFoodService pointer. This would work if IFoodService were derived from IPObject, but it is not ! This means that the vtable entry for ExecuteService just does not exist for IFoodService. Typecasting it via reinterpret_cast wont fix it either. I made the following change and it worked for me. In File factory.cpp Note that we now QI for IPObject instead of IFoodService
return CComCreator< CComObject >::CreateInstance(NULL, IID_IPObject, reinterpret_cast(ppPObj) );
This would ensure that the interface pointer for IPObject is returned instead of IFoodService.
If you really want IFoodService on the client, you can always QI for it from IPObject. Suggested change Change the interface map for CFood and CBevBEGIN_COM_MAP(CFoodService) COM_INTERFACE_ENTRY(IFoodService) COM_INTERFACE_ENTRY_CHAIN(CPObject) END_COM_MAP()
This is because CFoodService does not directly implement IPObject but rather uses the implementation already defined in a base class. The QI then searches for the implementation in the base class. Hope that helps- VivekWhat it comes down to is that I have to implement ExecuteService in each of the base classes to handle the initial request (Command Pattern) then let it call a template method in the parent class to get things moving as I want them to. I really don't want the client to have a clue as to what he is calling. That way I can add other base classes without having to recompile the client. Thanks for checking it out. I really appreciate it! ed The nice thing about egotists is that they don't talk about other people. Lucille Harper