Solved

Deriving from an interface in another dll

Posted on 2004-10-29
1,211 Views
Last Modified: 2013-12-14
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
0
Question by:avinash_sahay
    25 Comments
     
    LVL 86

    Expert Comment

    by:jkr
    If you derive from a pure virtual interface, you'll have to provide your own implementation of it's methods.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 19

    Expert Comment

    by:drichards
    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
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 9

    Accepted Solution

    by:
    Your interface Player inherits from IDispatch. It's also safe to say IAsync does as well.
    Hence the reported error - the compiler doesn't know which IDispatch to choose.

    Using COM_INTERFACE_ENTRY2 forces it to use whichever IDispatch you choose.

    Replace:
    COM_INTERFACE_ENTRY(IDispatch)

    with:
    COM_INTERFACE_ENTRY2(IDispatch, Player)
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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?
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    The members that the error says are not defined are:-

    GetTypeInfoCount
    GetTypeInfo
    GetIDsOfNames
    Invoke
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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?
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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?
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    >>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?
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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?
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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).
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    > 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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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.
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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.
    0
     
    LVL 2

    Author Comment

    by:avinash_sahay
    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
    0
     
    LVL 9

    Expert Comment

    by:_ys_
    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.
    0

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    6 Surprising Benefits of Threat Intelligence

    All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

    Suggested Solutions

    After several hours of googling I could not gather any information on this topic. There are several ways of controlling the USB port connected to any storage device. The best example of that is by changing the registry value of "HKEY_LOCAL_MACHINE\S…
    Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
    The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.
    The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

    884 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    19 Experts available now in Live!

    Get 1:1 Help Now