IT-Consultant: José Roca (PBWIN 10+/PBCC 6+) (Archive only) > Discussion

IRunningObjectTable Interface

(1/2) > >>

Frederick J. Harris:
Hi Jose!

Hope you are doing well!  I haven't been doing much coding for the past year since I retired, but I'm trying to find some time to get back to it.  I'm trying to help a C++ coder with the IRunningObjectTable interface.  I've only ever used the Running Object Table one time I can remember, when I developed that Out Of Process COM Server in PowerBASIC years ago (posted here).  What I'm wondering is how best to obtain an IDispatch pointer to an already running instance of Excel.  Of course, in PowerBASIC that is easily done for us by the GetCom call, which yields an object reference to an already running instance.  I guess what I'm asking is what do you think Bob Z's internal implementation for that was?

I looked at your IRunningObjectTable example and that seemed to provide a good start.  In studying up on the interfaces I noted the IMoniker interface has a IMoniker::BindToObject() method that allows passing in an IID and yields upon success an [OUT]  interface pointer in the normal fashion.  In the C/C++ code below I used your IRunningObjectTable example and in the loop which iterates through the ROT I call IMoniker::BindToObject for each entry in the table.  If Excel is running it yields S_OK when it hits "Book1".  I'm not sure if this is the best way to go.  I was hoping for your input on this. 

It seems to me that if I were to continue in this fashion I would need some other tests to make sure it was an object reference to Excel I got.  Afterall, if both Excel and Word were running, I'd get an S_OK return for IDispatch on both.  Can you shed some light on this for me?

Fred


--- Code: ---// cl Test7.cpp /O1 /Os /GS- TCLib.lib kernel32.lib ole32.lib oleaut32.lib
#include <windows.h>
#include "stdio.h"

int main()
{
 const IID            IID_IDispatch     = {0x00020400,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};   // IID_IDispatch
 IBindCtx*            pBindCtx          = NULL;
 IRunningObjectTable* pROT              = NULL;
 IEnumMoniker*        pEnumMoniker      = NULL;
 IMoniker*            pMoniker          = NULL;
 IDispatch*           pXLApp            = NULL;
 ULONG                iFetched          = 0;
 LPOLESTR             pDisplayName      = NULL;
 HRESULT              hr;
 int                  iResult           = 0;
 
 CoInitialize(NULL);
 hr = CreateBindCtx(0, &pBindCtx);
 if(hr!=S_OK)
    goto Bad_Ending;
 hr = pBindCtx->GetRunningObjectTable(&pROT);
 if(hr!=S_OK)
    goto Bad_Ending;
 hr = pROT->EnumRunning(&pEnumMoniker);
 if(hr!=S_OK)
    goto Bad_Ending;
   
 while(true)
 { 
    hr = pEnumMoniker->Next(1, &pMoniker, &iFetched);   
    if(hr!=S_OK)
       break;
    hr = pMoniker->GetDisplayName(pBindCtx, 0, &pDisplayName);
    if(hr!=S_OK)
       break;
    wprintf(L"pDisplayName = %s\n",pDisplayName);
         
    hr=pMoniker->BindToObject(pBindCtx,NULL,IID_IDispatch,(void**)&pXLApp); 
    if(hr==S_OK)
    {
       printf("pMoniker->BindToObject() Succeeded!\n");
       iResult=wcscmp(pDisplayName,L"Book1");
       if(iResult==0)
       {
          printf("Found Excel App!\n");       
          printf("pXLApp = %u\n",pXLApp);
          pXLApp->Release();
       }   
    }
    else
       printf("pMoniker->BindToObject() Failed!\n");     
    if(pDisplayName)
       CoTaskMemFree(pDisplayName);   
    pMoniker->Release();
    pMoniker=NULL;   
 }; 
   
 Bad_Ending:
 if(pMoniker)
    pMoniker->Release();
 if(pEnumMoniker)
    pEnumMoniker->Release();
 if(pROT)
    pROT->Release();
 if(pBindCtx)
    pBindCtx->Release(); 
 CoUninitialize();
 
 return 0;
}

--- End code ---

José Roca:
Hi, Fred,

Welcome back!

The function you're looking for is GetActiveObject:
https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-getactiveobject

Currently, I'm collaborating with Paul Squires in a framework and a visual designer for FreeBasic:
http://www.planetsquires.com/protect/forum/index.php

For this compiler I wrote the functions below, which remain untested because I don't have Office installed.


--- Code: ---' ========================================================================================
' If the requested object is in an EXE (out-of-process server), such Office applications,
' and it is running and registered in the Running Object Table (ROT), AfxGetCom will
' return a pointer to its interface. AfxAnyCom will first try to use an existing, running
' application if available, or it will create a new instance if not.
' Be aware that AfxGetCom can fail under if Office is running but not registered in the ROT.
' When an Office application starts, it does not immediately register its running objects.
' This optimizes the application's startup process. Instead of registering at startup, an
' Office application registers its running objects in the ROT once it loses focus. Therefore,
' if you attempt to use GetObject or GetActiveObject to attach to a running instance of an
' Office application before the application has lost focus, you might receive an error.
' See: https://support.microsoft.com/en-us/help/238610/getobject-or-getactiveobject-cannot-find-a-running-office-application
' ========================================================================================
PRIVATE FUNCTION AfxGetCom OVERLOAD (BYREF wszProgID AS CONST WSTRING) AS IDispatch PTR
   DIM classID AS CLSID, pUnk AS ANY PTR
   CLSIDFromProgID(wszProgID, @classID)
   IF IsEqualGuid(@classID, @IID_NULL) THEN RETURN NULL
   GetActiveObject(@classID, NULL, @pUnk)
   RETURN pUnk
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxGetCom OVERLOAD (BYREF classID AS CONST CLSID) AS IDispatch PTR
   DIM pUnk AS ANY PTR
   GetActiveObject(@classID, NULL, @pUnk)
   RETURN pUnk
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxAnyCom OVERLOAD (BYREF wszProgID AS CONST WSTRING) AS IDispatch PTR
   DIM classID AS CLSID, pUnk AS ANY PTR
   pUnk = AfxGetCom(wszProgID)
   IF pUnk THEN RETURN pUnk
   CLSIDFromProgID(wszProgID, @classID)
   IF IsEqualGuid(@classID, @IID_NULL) THEN RETURN NULL
   IF GetActiveObject(@classID, NULL, @pUnk) <> S_OK THEN
      CoCreateInstance(@classID, NULL, CLSCTX_INPROC_SERVER, @IID_IUnknown, @pUnk)
   END IF
   RETURN pUnk
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxAnyCom OVERLOAD (BYREF classID AS CONST CLSID) AS IDispatch PTR
   DIM pUnk AS ANY PTR
   IF GetActiveObject(@classID, NULL, @pUnk) <> S_OK THEN
      CoCreateInstance(@classID, NULL, CLSCTX_INPROC_SERVER, @IID_IUnknown, @pUnk)
   END IF
   RETURN pUnk
END FUNCTION
' ========================================================================================

--- End code ---

Hope it helps!

Frederick J. Harris:
Thanks very much for the help Jose.  GetActiveObject() did it.  The thread at www.cplusplus.com involved was....

http://www.cplusplus.com/forum/windows/249570/

C++ coders for some reason don't fool with this OLE/COM stuff much it seems.  I referenced your help and site at about the 15th post in that thread. 

That's Freebasic code you posted above, right?

I'm still struggling to find time to code, but when my life settles down a bit I'll give Freebasic a look. 

Odd thing about that GetActiveObject() call that threw me for a bit was the last [out] pointer to pointer parameter was typed as returning an IUnknown pointer rather than a (**void) which I'm more used to.  But since dual interface objects still have IUnknown as the first three methods, followed by the last four of IDispatch, I figurred I could just go and use it instead of a QueryInterface cast to IDispatch.  Didn't work!  It crashed!  I had to do a QueryInterface to IDispatch, and the address returned was different from the IUnknown pointer returned by GetActiveObject.  I'm not  sure what's going on there.  It seems to me they should have the same address.  It must be a conceptual misunderstanding on my part, or its just the total wierdness of Excel.

José Roca:
I hate to write code that I can't test because there is always something missing. I will modify the posted functions to do a call to QueryInterface and return a true IDispatch pointer.

GetActiveObject is a wrapper function that uses the IMoniker and IRunningObjectTable interfaces. This is what it does:


--- Code: ---/***********************************************************************
  *      GetActiveObject (OLEAUT32.35)
  *
  * Gets an object from the global item table.
  *
  * PARAMS
  *  rcid        [I] CLSID of the object.
  *  preserved   [I] Reserved. Set to NULL.
  *  ppunk       [O] Address to store object into.
  *
  * RETURNS
  *  Success: S_OK.
  *  Failure: HRESULT code.
  */
 HRESULT WINAPI GetActiveObject(REFCLSID rcid,LPVOID preserved,LPUNKNOWN *ppunk)
 {
     WCHAR           guidbuf[80];
     HRESULT         ret;
     LPRUNNINGOBJECTTABLE    runobtable;
     LPMONIKER       moniker;
 
     StringFromGUID2(rcid,guidbuf,39);
     ret = CreateItemMoniker(pdelimiter,guidbuf,&moniker);
     if (FAILED(ret))
         return ret;
     ret = GetRunningObjectTable(0,&runobtable);
     if (FAILED(ret)) {
         IMoniker_Release(moniker);
         return ret;
     }
     ret = IRunningObjectTable_GetObject(runobtable,moniker,ppunk);
     IRunningObjectTable_Release(runobtable);
     IMoniker_Release(moniker);
     return ret;
 }

--- End code ---

The pdelimiter parameter in the call to CreateIMoniker


--- Code: ---static const WCHAR  _delimiter[] = {'!',0}; /* default delimiter apparently */
static const WCHAR  *pdelimiter = &_delimiter[0];

--- End code ---

José Roca:
> That's Freebasic code you posted above, right?

Yes, it is. I wanted to play with 64-bit code and started using this compiler. There were several problems, mainly the lack of native support for dynamic unicode strings, but I have written a framework that provides all that I missed from PowerBasic and much more.

Since you're a C programmer, you will be at home with FreeBasic, as it is a C compiler with a basic syntax, with many of the C nuisances like strict type checking (having to use casting frequently), all these C aliases (HANDLE, HWND, WPARAM, LPARAM, etc.), parameters passed by value by default, etc.

My framework is available in GitHub:

https://github.com/JoseRoca/WinFBX

Afx framework: https://github.com/JoseRoca/WinFBX/tree/master/Afx

Documentation: https://github.com/JoseRoca/WinFBX/tree/master/docs

The easiest way to get started is to install Paul Squire's WinFBE editor, that comes with everything, including my framework and the FreeBasic compilers and headers.

Navigation

[0] Message Index

[#] Next page

Go to full version