• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 478
  • Last Modified:

Connecting to an existing Automation Object

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
RichardParkinson
Asked:
RichardParkinson
  • 13
  • 13
1 Solution
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
OK this is returning somthing sensible now, I'll try and actually use the object!!  I'll let you know...

Thanks.
0
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
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
 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
So what is the RefIID parameter to QueryInterface?
0
 
jkrCommented:
Hmm - usually the one from 'CLSIDFromProgID()'
0
 
RichardParkinsonAuthor Commented:
Thats what I thought, but it returns E_NOINTERFACE (0x80004002).
0
 
jkrCommented:
Try a 'hard coded' IID for testing purposes using 'DEFINE_GUID()'
0
 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
Ooops, i'm an idiot ;-)

You can of course use the CLSID returned by
'pmon->GetClassID    (   &clsid);
0
 
RichardParkinsonAuthor Commented:
Nope, that return E_NOINTERFACE too.
0
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
Do you access the object via the 'AttachDispatch()' method of the 'COleDispatchDriver'-derived wrapper class? (i hate debugging through oledisp2.cpp ;-)
0
 
RichardParkinsonAuthor Commented:
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
 
jkrCommented:
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
 
RichardParkinsonAuthor Commented:
YES !!

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

Answer this and get the points!
0
 
jkrCommented:
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

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 13
  • 13
Tackle projects and never again get stuck behind a technical roadblock.
Join Now