title: How to listen to COM events from Office applications that were started independently?
body: <p><strong>What I want to do:</strong></p>
  
  <p>Write an application that listens to Office events.  I want to listen to events from any instance opened on the machine.  E.g. if I'm listening to BeforeDocumentSave in Word, then I want my sink for this method to be activated whenever any instance of Word on the host saves a document.</p>
  
  <p>Another requirement is that I'm writing in C++ without MFC or ATL.</p>
  
  <p><strong>What I've done:</strong></p>
  
  <p>I've written a program that's supposed to listen to Word events.  See the code below. </p>
  
  <p><strong>The problem:</strong></p>
  
  <p>It doesn't work - the event handlers are never entered, although I open a word application and do the actions that should trigger the events.</p>
  
  <p>I have some specific questions, and of course any other input would be very welcome!</p>
  
  <p><strong>Questions:</strong></p>
  
  <ol>
  <li><p>Is it possible to listen to events from an application that wasn't started by me?  In all the examples I found, the listening application starts the office application it wants to listen to.</p></li>
  <li><p>In a microsoft howto (http://support.microsoft.com/kb/183599/EN-US/) I found the following comment:</p></li>
  </ol>
  
  <blockquote>
    <p>However, most events, such as
    Microsoft Excel's Workbook events, do
    not start with DISPID 1. In such
    cases, you must explicitly modify the
    dispatch map in MyEventSink.cpp to
    match the DISPIDs with the correct
    methods.</p>
  </blockquote>
  
  <p>How do I modify the dispatch map?</p>
  
  <ol>
  <li>For now I've defined only Startup, Quit and DocumentChange, which take no arguments.  The methods I really need do take arguments, specifically one of type Document.  How do I define parameters of this type if I'm not using MFC?</li>
  </ol>
  
  <p><strong>Code:</strong></p>
  
  <p>Here's the header file for my project, followed by the C file:</p>
  
  <pre><code>#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(&amp;m_cRef);
  
  return m_cRef;  
  }
  
  STDMETHOD_(ULONG, Release)()
  {
  InterlockedDecrement(&amp;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&lt;IApplicationEvents2*&gt;(this);
  }
  
  else if (riid == IID_IApplicationEvents2){
      *ppvObj = static_cast&lt;IApplicationEvents2*&gt;(this);
  }
  else if (riid == IID_IDispatch){
      *ppvObj = static_cast&lt;IApplicationEvents2*&gt;(this);
  }
  
  else
  {
      char clsidStr[256];
      WCHAR wClsidStr[256];
      char txt[512];
  
      StringFromGUID2(riid, (LPOLESTR)&amp;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&lt;IUnknown*&gt;(*ppvObj)-&gt;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_
  </code></pre>
  
  <p>The C file:</p>
  
  <pre><code>#include &lt;windows.h&gt;
  #include &lt;stdio.h&gt;
  #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"), &amp;clsid); 
  
  hr = CoCreateInstance(clsid, NULL, CLSCTX_SERVER,  
                        IID_IUnknown, (void FAR* FAR*)&amp;punk); 
  
  hr = punk-&gt;QueryInterface(IID_IConnectionPointContainer, (void FAR* FAR*)&amp;pConnPntCont); 
  
  // IID for ApplicationEvents2 
  hr = IIDFromString(L"{000209FE-0000-0000-C000-000000000046}",&amp;id);
  
  hr = pConnPntCont-&gt;FindConnectionPoint( id, &amp;pConnPoint );
  
  hr = officeEventHandler-&gt;QueryInterface( IID_IUnknown, (void FAR* FAR*)&amp;iu);
  
  hr = pConnPoint-&gt;Advise( iu, &amp;officeEventHandler-&gt;m_dwEventCookie );
  
  Sleep( 360000 );
  
  hr = pConnPoint-&gt;Unadvise( officeEventHandler-&gt;m_dwEventCookie );
  if (punk) punk-&gt;Release(); 
  if (pdisp) pdisp-&gt;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" );
  }
  </code></pre>