Solved

Connecting to an existing Automation Object

Posted on 1998-10-22
26
440 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
 

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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
IE call stack entries where function call address is not in a module 4 62
convert char array to number in c 5 83
Socket Programming (Unix) 8 118
C++ mouse_event mouse look 7 64
Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
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.

930 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

9 Experts available now in Live!

Get 1:1 Help Now