2010-10-19 108 views
3

我想要做的事:如何从独立启动的Office应用程序侦听COM事件?

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

另一个要求是我用C++编写而没有MFC或ATL。

我所做的:

我写的是应该听字事件的程序。请参阅下面的代码。

问题:

它不工作 - 事件处理程序从未进入过,虽然我打开一个Word应用程序,并做到这一点应该触发事件的动作。

我有一些具体的问题,当然任何其他的输入将非常欢迎!

问题:

  1. 是否有可能听取不是由我启动的应用程序的事件?在我发现的所有例子中,侦听应用程序启动它想要听的办公应用程序。

  2. 在Microsoft HOWTO(http://support.microsoft.com/kb/183599/EN-US/)我发现了以下评论:

然而,大多数事件,比如作为 微软Excel的工作簿事件,不 不与DISPID 1开始。在这种情况下 ,你必须明确修改MyEventSink.cpp的 调度映射到 用正确的方法 匹配的DISPID。

如何修改调度映射?

  1. 现在我只定义了Startup,Quit和DocumentChange,它们不带任何参数。我真正需要的方法需要参数,特别是一种类型的文档。如果我不使用MFC,我该如何定义这种类型的参数?

代码:

下面是我的项目的头文件,接着是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"); 
} 

回答

3

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

3

我发现我给这里的代码两个问题:

  1. 正如锐齿指出,睡眠是错在这里。它甚至会导致我听的应用程序挂起或睡眠。我提出了一个消息循环。
  2. 我还没有实施'调用'。我认为如果我自己实现了事件方法,它们会被应用程序调用,但事实并非如此。我必须实现调用并使其开启dispid并调用不同的事件方法。我在typelib中发现了这个坏蛋。在OLE查看器中查看一个方法,我使用了'id',这显然是dispid。

以下是更正后的.cpp文件。在.h文件中,唯一的更正是将Invoke更改为声明,而不是实现。在代码

有两点需要注意:

  1. 我构造出来的,我发现各种例子,所以你可能会发现,似乎无关紧要,因为他们是从其他地方取的名字或意见。
  2. 获取COM对象我使用GetActiveObjectCoCreateInstance。只有事件触发应用程序已经运行,前者才能工作。后者创造了一个不可见的实例。对于Word来说,这很有效,可能是因为如果我打开另一个Word实例它是同一过程的一部分。我还没有检查,如果我打开一个新的进程,如果事件仍然会被触发,会发生什么。

希望这会有所帮助!

#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; 
} 
相关问题