0

I have an out of process server based on the ATL Service VC++2010 Template. Now I wont to extend his COM Interface by dynamically loading additional dlls that contain its own COM Classes. The dll to load is based on ATL dll VC++2010 Template, containing a simple ATL-object "IMModule". I changed the corresponding .rgs file to connect the class from the dll to the EXE server by adding a LocalServer section and the server's AppID as follows:

HKCR
{
  NoRemove CLSID
  {
    ForceRemove {59276614-A811-4D27-B131-514656E643D3} = s 'IMModule Class'
    {
      ForceRemove Programmable
      LocalServer32 = s 'path to the service exe'
      {
    val ServerExecutable = s 'path to the service exe'
      }
      TypeLib = s '{250685C7-CBD3-4FF8-A3A6-2AF668794CFC}'
      Version = s '1.0'
      val AppID = s '{7EFD508A-53C6-4EA0-B21A-D29277B86CBC}'
    }
  }
}

In a dll init() method called by the service after loading the dll I call CoRegisterClassObject to register the IMModule class object. But im not sure how to obtain the IUnknown interface pointer (second parameter to CoRegisterClassObject ). I tried the following:

CIMModule::_ClassFactoryCreatorClass* pClassFak = 
    new CIMModule::_ClassFactoryCreatorClass;
IUnknown* pUnk;
HRESULT hr =
pClassFak->CreateInstance(CIMModule::_ClassFactoryCreatorClass::CreateInstance, 
                            IID_IIMModule, (LPVOID*)&pUnk);

But the call to CreateInstance fails with E_NOINTERFACE. So, how to register my IMModule class implemented inside a dll to be available for COM clients from my out of process server?

  • You implemented some COM class, with a set of interfaces. Do you want this class to somehow implement additional interfaces through dynamically loaded modules? – Roman R. Jul 06 '12 at 10:38
  • Short answer: no. Long answer: The COM server itself will not provide any COM interface. The server will load modules (implemented as DLLs) that provide COM interfaces to clients. The IMModule class is part of such a module DLL. Upon startup, the server loads and initializes the module. From my understanding, the DLL must register the IMModule COM class in the context of the server during its initialization. That's where the call to CreateInstance fails. – user1503961 Jul 06 '12 at 11:53
  • If the server does not expose any interface itself, this retags the quesion into loading modules and using their interfaces. You don't need any special registration for this, just regular registration for COM server, for its coclasses and type library. – Roman R. Jul 06 '12 at 12:00
  • I changed the tite, I hope it is clearer now. – user1503961 Jul 06 '12 at 12:41
  • Your main "umbrella" server should be exposed as out of process server and in its ineterface there will be a method `FindMeAnInterface(, IDispatch** ppDispatch)` through which umbrella server would return module's interface. Modules will have regular interface, they just need their interface marshalable (i.e. with a typelibrary or proxy/stub classes) to that they could be given out of process boundaries. – Roman R. Jul 06 '12 at 12:44
  • But how do I obtain the IDispatch interface? Maybe this sounds like a newbie question, but i'm new to COM. – user1503961 Jul 06 '12 at 13:04
  • You get it somehow from the module you want to load. The easiest is to `CoCreateInstance` a class hosted by external module. You have one "free" interface returned back from the succeeded call. You can pass it then through umbrella service and that way make it available to public. `CoCreateInstance(CLSID_MyModuleExtension, ..., __uuidof(IDispatch), (VOID**) &pDispatchFromLoadableModule)`. – Roman R. Jul 06 '12 at 13:07
  • If I understand right, you want to create a COM executable, or migrate to that, from your COM DLL. It is better if you simply start a new application as a COM exe instead of changing the rgs file because it's too complicated. If you're using ATL, which is a good decision, you should not have to deal with IUnknown object type. I will try to find my sample code that I have used, please stay tune for it. – The Original Android Jul 06 '12 at 18:45
  • Just to be clear. The COM exposed interface would be through the EXE, not the DLL. You may have to structure your code to deal with that. It should not be difficult. – The Original Android Jul 06 '12 at 18:48
  • A question...Did you already register the EXE? If the registration fails, then that's a structural problem, not COM compliant. – The Original Android Jul 06 '12 at 18:49
  • @roman-r I did it as you mentioned (please see my answer), thank you so mutch. – user1503961 Jul 10 '12 at 11:20

2 Answers2

1

With the help from Roman.R I get the behavior that I need. I can't say thank you enough, @roman-r. I will precisely describe what I did, so maybe someone can retrace the steps and give me some response.

First I created an ATL based Windows service (named UmbrellaService). Inside UmbrellaService I added a simple ATL-Object named Control and added the method:

 FindMeAnInterface(BSTR moduleName, IDispatch** ppDispach);

Thats all with the VC++ Wizard. Then I fixed the Control.rgs file by adding:

 val AppID = s '%APPID%'

Why has VC++ still such bugs after 17 years of evolution? (See CoCreateInstance does not start or connect to ATL COM service) Then I created an ATL-dll Project, named MyModule, with a "Module" simple ATL-Object inside. The Module class has a method

testMethod (LONG a, LONG b, LONG* sum)"

The MyModule dll is registered as a in-proc server. Furthermore the dll has some classes that makes the dll a plugin as I need it.

In the PreMessageLoop method of the UmbrellaService the MyModule dll will be loaded with LoadLibrary and through GetProcAddress the address of a factory creation method is obtained. The factory creation method returns a plugin-dependent FactoryClass that acts as a plugin entry point. This is my COM-independent plugin mechanism.

Now to export the module interface from the plugin dll through the UmbrellaService interface I did the following: On the FactoryClass I add the method:

IDispatch* getInterface();

In getInterface method I call

CoCreateInstance(__uuidof(Module), NULL , CLSCTX_INPROC_SERVER , __uuidof(IDispatch), (VOID**) &pDispatch); 

and return the obtained IDispatch interface. The FactoryClass::getInterface method is called inside the Control::FindMeAnInterface method of the UmbrellaService after comparing the name passed to FindMeAnInterface with the name provided by the FactoryClass. FindMeAnInterface returns the then obtained IDispatch pointer to the client.

On the client side I import the tlb file from the UmbrellaService and the tlb from the apropriate plugin dll. I call testMethod as follows:

IControlPtr pControl(__uuidof(Control));
_bstr_t moduleName("Module");
IDispatchPtr moduleDisp = pControl->FindMeAnInterface(moduleName);
IModulePtr pModule(moduleDisp );
LONG res = pModule->testMethod(42,23);

This all works indeed, but I am not sure if this is the way to do it. Did I miss something about reference counting? Will the plugin DLL be loaded two times? First time through my plugin mechanism and second time through CoCreateInstance? Something else I should note?

Thanks for your help!

Community
  • 1
  • 1
0

I could not find my code so far. But I did check one of my favorite websites, The Code Project. It used to be popular especially with older technologies like COM (yes, it is). I hope you're already convinced that you have to use COM instead of new WFC or another technology.

Please check good documentation and sample code @ ATL COM EXE doc. I believe I used this web page as a start of my past project.

Good luck and have fun.

The Original Android
  • 6,147
  • 3
  • 26
  • 31