Solved

Connecting to an existing Automation Object

Posted on 1998-10-22
26
431 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
So what is the RefIID parameter to QueryInterface?
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Hmm - usually the one from 'CLSIDFromProgID()'
0
 

Author Comment

by:RichardParkinson
Comment Utility
Thats what I thought, but it returns E_NOINTERFACE (0x80004002).
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 86

Expert Comment

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

Author Comment

by:RichardParkinson
Comment Utility
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
Comment Utility
Ooops, i'm an idiot ;-)

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

Author Comment

by:RichardParkinson
Comment Utility
Nope, that return E_NOINTERFACE too.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
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 clear a vector as well as how to detect empty vectors in C++.

771 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

16 Experts available now in Live!

Get 1:1 Help Now