如何从独立启动的Office应用程序中侦听COM事件?

时间:2010-10-19 13:39:02

标签: c++ events com ms-office

我想做什么:

编写一个侦听Office事件的应用程序。我想听听机器上打开的任何实例的事件。例如。如果我正在使用Word中的BeforeDocumentSave,那么只要主机上的任何Word实例保存文档,我就希望激活此方法的接收器。

另一个要求是我在没有MFC或ATL的情况下用C ++编写。

我做了什么:

我写了一个应该听Word事件的程序。请参阅下面的代码。

问题:

它不起作用 - 永远不会输入事件处理程序,尽管我打开一个单词应用程序并执行应该触发事件的操作。

我有一些具体问题,当然任何其他意见都非常受欢迎!

问题:

  1. 是否可以从我未启动的应用程序中侦听事件?在我发现的所有示例中,侦听应用程序启动它想要侦听的office应用程序。

  2. 在微软howto(http://support.microsoft.com/kb/183599/EN-US/)中,我发现了以下评论:

  3.   

    然而,大多数事件,如   Microsoft Excel的工作簿事件,做   不要以DISPID 1开头。就这样   在这种情况下,您必须明确修改   在MyEventSink.cpp中发送映射到   将DISPID与正确匹配   方法

    如何修改调度图?

    1. 现在我只定义了Startup,Quit和DocumentChange,它们不带任何参数。我真正需要的方法是做参数,特别是类型Document之一。如果我不使用MFC,如何定义此类型的参数?
    2. 代码:

      这是我项目的头文件,后跟C文件:

      #ifndef _OFFICEEVENTHANDLER_H_
      #define _OFFICEEVENTHANDLER_H_
      
      // 000209FE-0000-0000-C000-000000000046
      static const GUID IID_IApplicationEvents2 =  
      {0x000209FE,0x0000,0x0000, {0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
      
      struct IApplicationEvents2 : public IDispatch // Pretty much copied from typelib
      {
      /*
       * IDispatch methods
       */
      STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj) = 0; 
      STDMETHODIMP_(ULONG) AddRef()  = 0;  
      STDMETHODIMP_(ULONG) Release() = 0;
      
      STDMETHODIMP GetTypeInfoCount(UINT *iTInfo) = 0;
      STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) = 0;
      STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, 
                                    UINT cNames,  LCID lcid, DISPID *rgDispId) = 0;
      STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
                                    WORD wFlags, DISPPARAMS* pDispParams,
                                    VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
                                    UINT* puArgErr) = 0;
      
      /*
       * IApplicationEvents2 methods
       */
      STDMETHODIMP Startup();
      STDMETHODIMP Quit();
      STDMETHODIMP DocumentChange();
      };
      
      
      class COfficeEventHandler : IApplicationEvents2
      {
      
      public:
      DWORD                        m_dwEventCookie;
      
      COfficeEventHandler
      (
      ) :
      m_cRef(1),
      m_dwEventCookie(0)
      {
      }
      
      STDMETHOD_(ULONG, AddRef)()
      {
      InterlockedIncrement(&m_cRef);
      
      return m_cRef;  
      }
      
      STDMETHOD_(ULONG, Release)()
      {
      InterlockedDecrement(&m_cRef);
      
      if (m_cRef == 0)
      {
          delete this;
          return 0;
      }
      
      return m_cRef;
      }
      
      STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj)
      {
       if (riid == IID_IUnknown){
          *ppvObj = static_cast<IApplicationEvents2*>(this);
      }
      
      else if (riid == IID_IApplicationEvents2){
          *ppvObj = static_cast<IApplicationEvents2*>(this);
      }
      else if (riid == IID_IDispatch){
          *ppvObj = static_cast<IApplicationEvents2*>(this);
      }
      
      else
      {
          char clsidStr[256];
          WCHAR wClsidStr[256];
          char txt[512];
      
          StringFromGUID2(riid, (LPOLESTR)&wClsidStr, 256);
      
          // Convert down to ANSI
          WideCharToMultiByte(CP_ACP, 0, wClsidStr, -1, clsidStr, 256, NULL, NULL);
      
          sprintf_s(txt, 512, "riid is : %s: Unsupported Interface", clsidStr);
      
          *ppvObj = NULL;
          return E_NOINTERFACE;
      }
      
      static_cast<IUnknown*>(*ppvObj)->AddRef();
      
      return S_OK;
      }
      
      STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
      {
      return E_NOTIMPL;
      }
      
      STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
      {
      return E_NOTIMPL;
      }
      
      STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
             LCID lcid, DISPID* rgdispid)
      {
      return E_NOTIMPL;
      }
      
      STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
             LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
             EXCEPINFO* pexcepinfo, UINT* puArgErr)
      {
      return E_NOTIMPL;
      }
      
      // IApplicationEvents2 methods
      void Startup();
      void Quit();
      void DocumentChange();
      
      
      protected:
      LONG                        m_cRef;
      };
      
      #endif // _OFFICEEVENTHANDLER_H_
      

      C档案:

      #include <windows.h>
      #include <stdio.h>
      #include "OfficeEventHandler.h"
      #include "OCIdl.h"
      
      
      
      int main()
      {
      CLSID clsid;                   // CLSID of automation object 
      HRESULT hr; 
      LPUNKNOWN punk = NULL;         // IUnknown of automation object 
      LPDISPATCH pdisp = NULL;       // IDispatch of automation object 
      IConnectionPointContainer *pConnPntCont;
      IConnectionPoint *pConnPoint;
      IUnknown *iu;
      IID id;  
      COfficeEventHandler *officeEventHandler = new COfficeEventHandler;
      
      CoInitialize(NULL);
      
      hr = CLSIDFromProgID(OLESTR("Word.Application"), &clsid); 
      
      hr = CoCreateInstance(clsid, NULL, CLSCTX_SERVER,  
                            IID_IUnknown, (void FAR* FAR*)&punk); 
      
      hr = punk->QueryInterface(IID_IConnectionPointContainer, (void FAR* FAR*)&pConnPntCont); 
      
      // IID for ApplicationEvents2 
      hr = IIDFromString(L"{000209FE-0000-0000-C000-000000000046}",&id);
      
      hr = pConnPntCont->FindConnectionPoint( id, &pConnPoint );
      
      hr = officeEventHandler->QueryInterface( IID_IUnknown, (void FAR* FAR*)&iu);
      
      hr = pConnPoint->Advise( iu, &officeEventHandler->m_dwEventCookie );
      
      Sleep( 360000 );
      
      hr = pConnPoint->Unadvise( officeEventHandler->m_dwEventCookie );
      if (punk) punk->Release(); 
      if (pdisp) pdisp->Release(); 
      
      CoUninitialize();
      
      return hr; 
      }
      
      // IApplicationEvents2 methods
      void COfficeEventHandler::Startup()
      {
      printf( "In Startup\n" );
      }
      
      void COfficeEventHandler::Quit()
      {
      printf( "In Quit\n" );
      }
      
      void COfficeEventHandler::DocumentChange()
      {
      printf( "In DocumentChnage\n" );
      }
      

2 个答案:

答案 0 :(得分:3)

您的首要问题是您不在主线程和that causes the events to never reach your sink object中运行消息循环。使用Windows消息调度从您的接收器对象的COM服务器调用,因此您必须运行消息循环而不是简单地调用Sleep(),以便最终将传入事件分派到接收器对象。

答案 1 :(得分:3)

我在这里给出的代码中发现了两个问题:

  1. 正如尖锐的指出,睡眠在这里是错误的。它甚至会导致我听的应用程序挂起或睡眠。我按照尖锐的建议放了一个消息循环。
  2. 我没有实现'Invoke'。我认为如果我自己实现了事件方法,它们将由应用程序调用,但事实并非如此。我必须实现invoke并使其切换dispid并调用不同的事件方法。我在typelib中找到了dispid。查看OLE查看器中的方法,我使用了'id',它显然是dispid。
  3. 以下是更正后的.cpp文件。在.h文件中,唯一的更正是将Invoke更改为声明而不是实现。

    关于代码的两点说明:

    1. 我是根据我发现的各种例子构建的,所以你可能会发现看起来无关紧要的名字或评论,因为它们来自其他地方。
    2. 要获取COM对象,我使用了GetActiveObjectCoCreateInstance。前者仅在事件触发应用程序已经运行时才起作用。后者创建了一个不可见的实例。对于Word,这很有效,可能是因为如果我打开另一个Word实例,它是同一个进程的一部分。我还没有检查如果我打开一个新流程会发生什么,如果事件仍然会被触发。
    3. 希望这有帮助!

      #include <windows.h>
      #include <stdio.h>
      #include "OfficeEventHandler.h"
      #include "OCIdl.h"
      
      
      
      int main()
      {
          CLSID clsid;                   // CLSID of automation object 
          HRESULT hr; 
          LPUNKNOWN punk = NULL;         // IUnknown of automation object 
          LPDISPATCH pdisp = NULL;       // IDispatch of automation object 
          IConnectionPointContainer *pConnPntCont;
          IConnectionPoint *pConnPoint;
          IUnknown *iu;
          IID id;  
          COfficeEventHandler *officeEventHandler = new COfficeEventHandler;
      
          CoInitialize(NULL);
      
          hr = CLSIDFromProgID(OLESTR("Word.Application"), &clsid); 
      
       /*  hr = GetActiveObject( clsid, NULL, &punk );
       */  hr = CoCreateInstance(clsid, NULL, CLSCTX_SERVER,  
                                IID_IUnknown, (void FAR* FAR*)&punk); 
      
          hr = punk->QueryInterface(IID_IConnectionPointContainer, (void FAR* FAR*)&pConnPntCont); 
      
          // IID for ApplicationEvents2 
          hr = IIDFromString(L"{000209FE-0000-0000-C000-000000000046}",&id);
      
          hr = pConnPntCont->FindConnectionPoint( id, &pConnPoint );
      
          hr = officeEventHandler->QueryInterface( IID_IUnknown, (void FAR* FAR*)&iu);
      
          hr = pConnPoint->Advise( iu, &officeEventHandler->m_dwEventCookie );
      
          MSG   msg;
          BOOL  bRet;
      
          while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0 )
          { 
              if (bRet == -1)
              {
                  // handle the error and possibly exit
              }
              else
              {
                  TranslateMessage(&msg); 
                  DispatchMessage(&msg); 
              }
      
              if( msg.message == WM_QUERYENDSESSION || msg.message == WM_QUIT || msg.message == WM_DESTROY )
              {
                  break;
              }
          }
      
          hr = pConnPoint->Unadvise( officeEventHandler->m_dwEventCookie );
          if (punk) punk->Release(); 
          if (pdisp) pdisp->Release(); 
      
          CoUninitialize();
      
          return hr; 
      }
      
          // IApplicationEvents2 methods
      void COfficeEventHandler::Startup()
      {
          printf( "In Startup\n" );
      }
      
      void COfficeEventHandler::Quit()
      {
          printf( "In Quit\n" );
      }
      
      void COfficeEventHandler::DocumentChange()
      {
          printf( "In DocumentChnage\n" );
      }
      
      STDMETHODIMP COfficeEventHandler::Invoke(DISPID dispIdMember, REFIID riid,
                 LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
                 EXCEPINFO* pexcepinfo, UINT* puArgErr)
      {
          //Validate arguments
          if ((riid != IID_NULL))
              return E_INVALIDARG;
      
          HRESULT hr = S_OK;  // Initialize
      
          /* To see what Word sends as dispid values */
          static char myBuf[80];
          memset( &myBuf, '\0', 80 );
          sprintf_s( (char*)&myBuf, 80, " Dispid: %d :", dispIdMember );
      
          switch(dispIdMember){
          case 0x01:    // Startup
              Startup();
          break;
          case 0x02:    // Quit
              Quit();
          break;
          case 0x03:    // DocumentChange
              DocumentChange();
          break;
          }
      
          return S_OK;
       }