Solved

Connecting to an existing Automation Object

Posted on 1998-10-22
26
465 Views
Last Modified: 2012-05-04
I am trying to connect to an existing Automation opbject in C/C++ (MS Visual C Version 4 or 5).  I can do this easily in VB 4 or 5 using the GetObject routine and just passing the program id in the second parameter.

I can create all the prototypes for the objects methods etc by using the Class Wizard and importing the Type Library, I can create a new object using the ClassFactory. What I can't figure out is how to connect to an existing object (i.e. one that was created by another aplication that is still running).  There is a routine called GetAciveObject which I can use after calling CLSIDFromProgID but I can't get the calls to the methods of the object to work, it just causes an Aplication Error (GPF) when I run them.  Does anyone know how to do this, it's so simple in VB!!!
0
Comment
Question by:RichardParkinson
  • 13
  • 13
26 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 1175774
To connect to an active automation object, it must have been registered in the running object table (using the 'IRunningObjectTable' interface). You'll then be able to receive an interface pointer via 'IRunningObjectTable::GetObject()'
0
 

Author Comment

by:RichardParkinson
ID: 1175775
Having looked at the documentation for IRunningObjectTable::GetObject() I don't know if the object has been registered or not (how do I tell) and I don't know how to get the moniker of the object that I want to access.  Could you give me an example of how to use such a call.  For example if in VB you used :

Set xl = GetObject(, "Excel.Application")

to get a running Excel Application object what would you do in C/C++?
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175776
Here's a little example on how to enumerate the ROT:
HRESULT Enumerate   ()
{
    HRESULT              hres   =   S_OK;
    IRunningObjectTable *prot   =   NULL;
    IEnumMoniker        *pem    =   NULL;
    IMoniker            *pmon   =   NULL;
    IBindCtx            *pbc    =   NULL;
   
    LPOLESTR             lpolestr;
    char                 acCLSID    [   1024];
    char                 acDispName [   1024];
    char                *pszMkType;
    DWORD                dwMkSys;


    CLSID                clsid;

    PVOID                pv;

    hres    =   OleInitialize   (   NULL);

    // get the ROT interface pointer
    if  (   !   (   S_OK    ==  (   hres    =   GetRunningObjectTable   (    0,
                                                                            &prot
                                                                        )
                                )
                )
        )   return  (   hres);

    // get moniker enumerator
    if  (   !   (   S_OK    ==  (   hres    =   prot->EnumRunning   (   &pem))))
            return  (   hres);

    // make sure that it is an IEnumMoniker
    if  (   !   (   S_OK    ==  (   hres    =   pem->QueryInterface (   IID_IEnumMoniker,
                                                                        &pv
                                                                    )
                                )
                )
        )   return  (   hres);
     else   pem->Release    ();

    _ASSERT (   pv  ==  ( PVOID)    pem);

    // create binding context
    if  (   !   (   S_OK    ==  (   hres    =   CreateBindCtx   (    0,
                                                                    &pbc
                                                                )
                                )
                )
        )   return  (   hres);

    printf  (   "\nenumerated ROT contents:\n");
    printf  (   "\nCLSID\t\t\t\t\tmoniker type\t\tdisplay name\n");

    // iterate...
    while   (   S_OK    ==  (   hres    =   pem->Next   (   1,  &pmon,  NULL)))
            {
                _ASSERT (   S_OK    ==  pmon->GetClassID    (   &clsid));

                if  (   S_OK    ==  pmon->IsSystemMoniker   (   &dwMkSys))
                    {
                        switch  (   dwMkSys)
                                {
                                    case    MKSYS_NONE              :   pszMkType   =   "MKSYS_NONE";
                                                                        break;
                                    case    MKSYS_GENERICCOMPOSITE  :   pszMkType   =   "MKSYS_GENERICCOMPOSITE";
                                                                        break;
                                    case    MKSYS_FILEMONIKER       :   pszMkType   =   "MKSYS_FILEMONIKER";
                                                                        break;
                                    case    MKSYS_ANTIMONIKER       :   pszMkType   =   "MKSYS_ANTIMONIKER";
                                                                        break;
                                    case    MKSYS_ITEMMONIKER       :   pszMkType   =   "MKSYS_ITEMMONIKER";
                                                                        break;
                                    case    MKSYS_POINTERMONIKER    :   pszMkType   =   "MKSYS_POINTERMONIKER";
                                                                        break;
   
                                    default:                            pszMkType   =   "none";
                                }
                    }
                 else   pszMkType   =   "none";

                // get CLSID as string
                StringFromCLSID (   clsid,  &lpolestr);
                W2A (   lpolestr, acCLSID);

                // get the moniker's display name
                _ASSERT (   S_OK    ==  pmon->GetDisplayName(   pbc, NULL, &lpolestr));
                W2A (   lpolestr, acDispName);

                // print output
                printf  (   "\n%s\t%s\t%s", acCLSID,    pszMkType,  acDispName);
            }

    printf  (   "\n");

    if  (   pem)    hres    =   pem->Release    ();
    if  (   pbc)    hres    =   pbc->Release    ();
    if  (   prot)   hres    =   prot->Release   ();

    OleUninitialize();

    return( S_OK);
}

using this you should be able to receive the monikers associated to a display name
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:RichardParkinson
ID: 1175777
I'm sure that all this is very clever... but I can't compile it!  What is the W2A routine?  The only place that I can find any reference to it is in atlconv.h but it only has one parameter where it is defined in that file.  

Also as this is adding code to an existing project I can't use the ATL COM Appwizard.
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175778
Oooops - sorry ... it's a simple macro (fast hack) to convert UNICODE strings that hold ASCII symbols back to ASCII:
#define W2A( a, b)\
{\
char *in = ( char *) a;\
char *out = ( char *) b;\
for ( int i = 0; *( in + ( 2 * i)); *( out + i) = *( in + ( 2 * i)), i ++, *( out + i) = 0);\
}

0
 

Author Comment

by:RichardParkinson
ID: 1175779
OK this is returning somthing sensible now, I'll try and actually use the object!!  I'll let you know...

Thanks.
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175780
Ooops, a correction:
All lines like
               // get the moniker's display name
                _ASSERT (   S_OK    ==  pmon->GetDisplayName(   pbc, NULL, &lpolestr));

should be changed to
               // get the moniker's display name
  hres    ==  pmon->GetDisplayName(   pbc, NULL, &lpolestr);
                _ASSERT (   S_OK    ==  hres);

Sorry ;-)

0
 

Author Comment

by:RichardParkinson
ID: 1175781
I still can't use the Object that GetObject returns, it just creates a "Unhandled Exception" error when I try to use any of the methods in the object.  (In fact it is at the same address as the Object returned by GetActiveObject failed at!).
0
 

Author Comment

by:RichardParkinson
ID: 1175782
HAving looked at this a little more, could the problem be in how I access the Object.  Both the GetActiveObject and RunningObjectTable::GetObject return IUnknown objects, how do you convert this into a class object?
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175783
You'll have to call 'QueryInterface()' supplying the appropriate interface ID prior to using it - like always when using COM objects in C/C++...
0
 

Author Comment

by:RichardParkinson
ID: 1175784
So what is the RefIID parameter to QueryInterface?
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175785
Hmm - usually the one from 'CLSIDFromProgID()'
0
 

Author Comment

by:RichardParkinson
ID: 1175786
Thats what I thought, but it returns E_NOINTERFACE (0x80004002).
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175787
Try a 'hard coded' IID for testing purposes using 'DEFINE_GUID()'
0
 

Author Comment

by:RichardParkinson
ID: 1175788
I'm getting a bit further, but not much! (Thank you very much for your help so far by the way :-) )

In the registry there are two CLSID type numbers, the first is in the registry in HKEY_CLASSES_ROOT CLSID with a Key name that starts {2974E401-... which has a value of the ProgId that I want. The next is in HKEY_CLASSES_ROOT Interface with a key name that starts {2974E400-... and it has a value of the Object Name that I want. If I define (with DEFINE_GUID) the first number (first part ending in 01) then QuerryInterface fails, if I use the second (first part ending in 00) then QuerryInterface returns S_OK, but using the Object returned causes a GPF.
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175789
Ooops, i'm an idiot ;-)

You can of course use the CLSID returned by
'pmon->GetClassID    (   &clsid);
0
 

Author Comment

by:RichardParkinson
ID: 1175790
Nope, that return E_NOINTERFACE too.
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175791
Well, sorry, i got a bit confused adn mixed it up - refIID is a reference to an _interface_ id (not a _class_ id). To get the correct interface ID, you'll have to generate the appropriate header file using ClassWizard (Add class -> from TypeLib) or have a look at the .idl files yourself.
0
 

Author Comment

by:RichardParkinson
ID: 1175792
Yes, I did that to start with, I imported the TLB file to create a .h and a CPP file with what are described as "wrapper classes", but there is nothing that looks even remotly like an ID in either of these files.  Do you think its just time to give up?!!
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175793
Nope ;-)
You mentioned earlier that you wanted to use the ->automation<-object in a 'VB'ish style - so the only interface that can be used is an IDispatch, therefore the IID _must_ be IID_IDispatch (defined in 'comdef.h') - i should have thought about that earlier....
0
 

Author Comment

by:RichardParkinson
ID: 1175794
Using IID_IDISPATCH returns S_OK from QueryInterface, but accessing the returned object still causes a crash, the line it crashes on is :

SCODE sc = m_lpDispatch->Invoke(dwDispID, IID_NULL, 0, wFlags,
            &dispparams, pvarResult, &excepInfo, &nArgErr);

In Oledisp2.cpp.
0
 
LVL 86

Expert Comment

by:jkr
ID: 1175795
Do you access the object via the 'AttachDispatch()' method of the 'COleDispatchDriver'-derived wrapper class? (i hate debugging through oledisp2.cpp ;-)
0
 

Author Comment

by:RichardParkinson
ID: 1175796
No, should I?

What I am doing at the moment is passing a pointer to one of the objects imported from the ClassWizard to the QueryInterface routine, like this :

IMyObject *Obj;
IUnknown *Unknown;

 //Get Object from the code you supplied
 hRet=GetObj(ProgID,&Unknown);
 if(hRet==S_OK)
 {
   hRet=Unknown->QueryInterface(IID_IDispatch ,(void **)&Obj);
   if(hRet==S_OK)
   {
      Obj->Test();
   }
 }

0
 
LVL 86

Expert Comment

by:jkr
ID: 1175797
Ooops, there we have the problem: An IDispatch certainly has no method 'Test' in it's vtable. You'll have to use an instance of the wrapper class in order to use the methods, e.g.

CMyWrapper cObj;
hRet=GetObj(ProgID,&Unknown);
 if(hRet==S_OK)
 {
   hRet=Unknown->QueryInterface(IID_IDispatch ,(void **)&Obj);
   if(hRet==S_OK)
   {
      cObj.AttachDispatch ( Obj);

      cObj.Test();
   }

0
 

Author Comment

by:RichardParkinson
ID: 1175798
YES !!

One last question, how should I release the object(s) again after all this lot.

Answer this and get the points!
0
 
LVL 86

Accepted Solution

by:
jkr earned 300 total points
ID: 1175799
There are two possibilities: Using the above code, you'll have to call 'Obj->Release()' manually. But 'AttachDispatch()' also has an optional parameter 'bAutoRealease' which (when set to 'TRUE') can be used to make the instance of 'COleDispatchDriver' call 'IUnknown::Release()' automatically.
0

Featured Post

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

685 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