Link to home
Start Free TrialLog in
Avatar of avinash_sahay
avinash_sahay

asked on

Deriving from an interface in another dll

Hi,
I have a COM dll named Integrator.dll. It has an interface named IAsync. I have only the dll and not the source code. I create a new ATL COM dll. In that, using Insert->New ATL Object menu, I add a Simple Object named CPlayer. (I am using Visual Studio 6.0). I want CPlayer to be derived from IAsync so that I can implement the exposed functions of IAsync in CPlayer.

What exactly should I write to do this? I tried using
public IAsync

but it did not work.

Any idea?
Thanks
Avatar of jkr
jkr
Flag of Germany image

If you derive from a pure virtual interface, you'll have to provide your own implementation of it's methods.
Avatar of avinash_sahay
avinash_sahay

ASKER

The problem is not in implementing. I am ready to implement, but the compiler gives error when I try to derive from that interface.

ATL wizard generates my CPlayer class as

class ATL_NO_VTABLE CPlayer :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CPlayer , &CLSID_Player>,
public IDispatchImpl<Player, &IID_IPlayer, &LIBID_EONPLAYERLib>
{
...
...
};

I want CPlayer to be also implement IAsync. I used IAsync as follows:-
(Note 'public IAsync' used)

class ATL_NO_VTABLE CPlayer :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CPlayer , &CLSID_Player>,
public IAsync,
public IDispatchImpl<Player, &IID_IPlayer, &LIBID_EONPLAYERLib>
{
...
...
};


Compiler gives error in the line
COM_INTERFACE_ENTRY(IDispatch)

Error is:-
static_cast' : ambiguous conversions from 'class CPlayer *' to 'struct IDispatch *'

But if I do not try to derive from IAsync, then no error comes.
Do you have the definition of IAsync?  To use a COM interface in C++, you need either to have the header file or you can #import the type library which if it exists will be either embedded in the dll or included as a separate file with a .tlb extension.  If you do a #import, use the no_nameapaces and raw_interfaces_only attributes with the #import statement so the names from the type library will be preserved.  It will look like this:

   #import "<tlb file name>" no_namespace, raw_interfaces_only
I do not have the tlb. So, I imported the dll. The compiler can recognize IAsync after I import the dll but when I use that as parent interface for CPlayer, then I get compiler error as I mentioned in my previous post.
ASKER CERTIFIED SOLUTION
Avatar of _ys_
_ys_

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I did that. But then I got another error. The error is:-

error C2259: 'CComObject<class CSink>' : cannot instantiate abstract class due to following members:
When I double click on this error, AtlCom.h opens. The error is shown in line
ATLTRY(p = new T1(pv))

I also get some warnings.
I commented out all the lines where I am using any instance of CSink. Even then I get the same error.

Do I need to make some other changes also?
And does:
COM_INTERFACE_ENTRY2(IDispatch, IAsync)
give you an error also.

Do you really need to expose IDispatch ? Can you provide a *good* reason why you need to ?
If you don't need it simply comment out the offending COM_INTERFACE_ENTRY. All you're saying by using COM_INTERFACE_ENTRY is that clients are allowed to QI for the listed interfaces.
COM_INTERFACE_ENTRY2(IDispatch, IAsync)
does not give error.

I need to expose IDispatch because a pointer to IPlayer may need to be passed to a VB client. But, just to check, I commented out the COM_INTERFACE_ENTRY entry. I got the same error in AtlCom.h. Do I need to make some change in the line
public IDispatchImpl<Player, &IID_IPlayer, &LIBID_EONPLAYERLib>

also?

In any case, it will be better if I could support IDispatch.
If I replace
COM_INTERFACE_ENTRY(IDispatch)
by
COM_INTERFACE_ENTRY2(IDispatch, IAsync)

and I do not use public IAsync to declare IAsync as a parent interface for CPlayer, then I do not get any error. But I do want CPlayer to support IAsync. Moreover, when I do not use public IAsync, even COM_INTERFACE_ENTRY(IDispatch) does not give error.
The members that the error says are not defined are:-

GetTypeInfoCount
GetTypeInfo
GetIDsOfNames
Invoke
I tried one more thing. I replaced
COM_INTERFACE_ENTRY
by
COM_INTERFACE_ENTRY2

and also
public IAsync by

public IDispatchImpl<IAsync, &__uuidof(IAsync), &LIBID_EONPLAYERLib>

I am creating an instance of CPlayer as follows:-
CComObject<CSink> *pPlayer = NULL;
CComObject<CPlayer>::CreateInstance(&pPlayer);

There is another module that exposes a function to which LPDISPATCH has to be passed. That function converts that LPDISPATCH to IAsync* using QueryInterface. From my dll (the one of which we are discussing here), I pass pPlayer to that function.

But I get an error in the line in which I try to pass pPlayer to that function. The error is
error C2594: 'argument' : ambiguous conversions from 'class ATL::CComObject<class CPlayer> *' to 'struct IDispatch *'

To correct the error I did QueryInterface from pPlayer to get IDispatch* and I passed this pointer to the function.

Now, I do not get any compile time error. But do you think it is fine or will it result in some other problem?
Thanks
So long as you never create a proxy/stub pair to your pPlayer instance, all should be well. If you ever do then multiple IDispatch implementations doesn't work. But since you're using COM_INTERFACE_ENTRY2 this should not be a problem - you only expose a single IDispatch implementation.

I trust you are aware that VB does support early binding, and hence support for IDispatch is irrelevant.

When you QI for IDispatch from pPlayer, it gives you the implementation defined within COM_INTERFACE_ENTRY2. What if you ever wanted access to the *other* IDispatch implementation? Do you ever require this?
A VB module is one of the clients. In fact, depending on some other settings there can be different kinds of clients. One of them is an exe written in MFC. It was written long back. I have not been given the authority to make changes in it unless it becomes necessary. It takes pointer to IDispatch through one of its functions. Through this pointer (after doing other needed conversions) it calls certain functions. These functions are to be implemented in my CPlayer. I think this is one of the reasons I need to support IDispatch.
Here is the actual scenario.

There is a dll named Integrator.dll. It has an interface named IAsync. Inside IAsync there are various functions. Just for example, consider one of its functions named PausedByUser. There is an exe that exposes a class with a function NotifyMe. This function takes dispClient as a parameter. dispClient is a pointer to IDispatch. Inside eonPlayer, there are some classes, one of which is CPlayer. I need to pass an instance of CPlayer to NotifyMe. NotifyMe converts dispClient to IAsync*. The method of conversion depends on the type of exe that contains NotifyMe. As I said there are various kinds of such exes. If it is MFC, then Integrator.dll has been added through class wizard and conversion is not really to IAsync* but to a class derived from COleDispatchDriver and I attach dispClient to an instance of this class using AttachDispatch. Through this instance, NotifyMe calls PausedByUser which is implemented in CPlayer. At this stage, the implementation inside CPlayer should get executed.

Do you think what I have done will help me achieve the above?
>>When you QI for IDispatch from pPlayer, it gives you the implementation defined within COM_INTERFACE_ENTRY2. What if you ever wanted access to the *other* IDispatch implementation? Do you ever require this?<<

Yes, I do because not all the exposed functions of CPlayer are those of IAsync. There are some other functions as well. So, if I can get the IDispatch implementation that I want, then it will be good.
But, even if I cannot do that, it is fine with me. In that case, I will break CPlayer into two classes. One will be used only for implementing the functions of IAsync and the other will do the rest. Will this be fine?
Whilst it is possible to support multiple IDispatch implementations and enable QI support for them, it's not trivial. Furthermore it won't work around the proxy/stub issue I mentioned, unless you're going to write your own - and you don't want to go there.

There are two easy solutions:

1. Break it into 2 classes, providing disparate support for the 2 IDispatch implementations.
2. Create a single huge IDispatch implementation that supports all functions from the other 2 IDispatch implementations.

Solution 1 is easier, and future proof. If you add another function at a later date, minimal (if any) code chages would be necessary to expose it.
Thankyou. I will use the first approach. Meanwhile, I would like to ask one more question related to this. I will break the class into two classes. One of them will implement only IAsync functions. Let us call that class as CAsyncImpl. This will be derived from both IAsync and IAsyncImpl. But IAsyncImpl does not contain any functions of its own. So, does it make sense not to make IAsyncImpl as a parent interface of CAsyncImpl? If yes, how to remove this? By default, ATL wizard will add code to make IAsyncImpl as a parent interface. How to remove that?
I'm not familiar with ATL ... I prefer to do COM raw ...

But, anyhow, borrowing from code samples you've already posted, let's say CAsyncImpl looks like this:

class ATL_NO_VTABLE CAsyncImpl :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CAsyncImpl , &CLSID_AsyncImpl>,
public IAsync,
public IDispatchImpl<IAsyncImpl, &IID_IAsyncImpl, &LIBID_EONPLAYERLib>
{
...
...
};

Simply merging the last two lines should do the trick:

class ATL_NO_VTABLE CAsyncImpl :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CAsyncImpl , &CLSID_AsyncImpl>,
public IDispatchImpl<IAsync, &IID_IAsync, &LIBID_XxxxLib, majorXxxx, minorXxxx>
{
...
...
};

The last three arguments are the LIBId for the source of IAsync, as well as it's major and minor version numbers (version of the typelib, bot the DLL); you did mention it was imported from another DLL, so it's not going to be the local library.

Also, remove COM_INTERFACE_ENTRY(IAsyncImpl).
I am still getting problem. I broke CPlayer into two classes. One of them is CAsyncImpl. This class implements the exposed methods of IAsync.

I create a new instance of CAsyncImpl. Using QI, I get a pointer to IDispatch. QI works properly and I get the LPDISPATCH. I pass this to a function in an MFC exe that takes an LPDISPATCH parameter. MFC exe has added Integrator.dll (the dll that contains IAsync) using class wizard. Classwizard adds wrappers for all the exposed functions of IAsync. Consider one exposed function of IAsync. It is PausedByUser. MFC exe calls this method. Inside the wrapper of PausedByUser, there is call to InvokeHelper. I expect this to call PausedByUser of CAsyncImpl. But it does not. In fact, InvokeHelper hangs. I put one message box  before calling InvokeHelper and another after. The first one if displayed but the second is not.

I put the library that defines IAsync in the library section of the idl of my ATL COM project using importlib. Inside the coclass section for AsyncImpl, I have added IAsync. I have also done all that you have suggested.

To find out where the error is, I made one change in the MFC exe. As I said the MFC exe exposes one function to which my ATL COM module passes an LPDISPATCH parameter (which is actually CAsyncImpl). When I have to call PausedByUser, earlier it was going through the wrapper. I made a change. Now, I directly call Invoke of LPDISPATCH. To do that I have to get disp id PausedByUser. I get that using GetIDsOfNames. The return value of GetIDsOfNames is not S_OK. This means that Invoke is not able to find PausedByUser method.
> QI works properly and I get the LPDISPATCH
Can you confirm this? Try to invoke PausedByUser immediately after obtaining this pointer - before passing it to the MFC exe.

Let me know the result.
I did that. This time also, the value of hr is not S_OK. I checked the meaning of the value in Tools->Error Lookup menu. The error is "Library Not Registered". I even tried to get the disp id of AddRef. Even this fails and I get the same error. Just to check, instead of creating an instance of CAsyncImpl, I create an instance of some other COM class (which does not support IAsync). Using QI, I get LPDISPATCH. From LPDISPATCH, I get the disp id of an exposed method in that class. This time disp id is proper.
I'll assume that the values for LIBID_XxxxLib, majorXxxx and minorXxxx are correct.

You use importlib of the type library for IAsync. So, try registering this type library as the error suggests. There should be a utility called RegTLib.exe on your local machine. Use this.
The dll is already registered. I tried registering once more. Note: I do not have the tlb file, I have only the dll. I have imported that. The dll  has been registered using regsvr32.exe.
Can you take the value of LIBID_XxxxLib in the format {00000000-0000-0000-0000-000000000000} (if you can't find this, the function StringFromCLSID should give you it.) and search the registry for it.

It should reside under the HKEY_CLASS_ROOT/TypeLib key. It's sub-key states the registered version and should again match your majorXxxx and minorXxxx.

Let me know what you find.
Yes, now it works. Thanks. But I do not understand why these were required when I have just one copy of that dll in my system.
Thanks a lot
When the DLL registerd itself with the system, it did enough for itself to work. What it didn't count on was what you were doing - reusing its interfaces.

So it didn't register the embedded typelib properly. But you need it, so these entries make it work for you as well.

At least it's now sorted.