Как прослушивать события COM из приложений Office, которые были запущены независимо? - PullRequest
5 голосов
/ 19 октября 2010

Что я хочу сделать:

Напишите приложение, которое прослушивает события Office. Я хочу слушать события из любого экземпляра, открытого на машине. Например. если я слушаю BeforeDocumentSave в Word, то я хочу, чтобы мой приемник для этого метода активировался всякий раз, когда какой-либо экземпляр Word на хосте сохраняет документ.

Другое требование - я пишу на C ++ без MFC или ATL.

Что я сделал:

Я написал программу, которая должна прослушивать события Word. Смотрите код ниже.

Проблема:

Не работает - обработчики событий никогда не вводятся, хотя я открываю текстовое приложение и выполняю действия, которые должны вызывать события.

У меня есть некоторые конкретные вопросы, и, конечно, любые другие отзывы будут очень кстати!

Вопросы:

  1. Можно ли прослушивать события из приложения, которое я не запускал? Во всех найденных примерах приложение прослушивания запускает офисное приложение, которое оно хочет прослушивать.

  2. В Microsoft Howto (http://support.microsoft.com/kb/183599/EN-US/) я нашел следующий комментарий:

Однако большинство событий, таких как Microsoft Excel Workbook события, сделать не начинай с DISPID 1. В таких случаях, вы должны явно изменить карта отправки в MyEventSink.cpp для сопоставьте DISPID с правильными методы.

Как мне изменить карту отправки?

  1. На данный момент я определил только Startup, Quit и DocumentChange, которые не принимают аргументов. Методы, которые мне действительно нужны, принимают аргументы, в частности один из типов Document. Как определить параметры этого типа, если я не использую 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" );
}

Ответы [ 2 ]

3 голосов
/ 26 октября 2010

Я нашел две проблемы в коде, который я дал здесь:

  1. Как указал острый зуб, сон здесь не так.Это даже вызывает приложение, которое я слушаю, зависает или спит.Я включил цикл обработки сообщений в соответствии с предложением Sharp.
  2. Я не реализовал «Invoke».Я думал, что если бы я реализовал сами методы события, они бы вызывались приложением, но это не так.Мне пришлось реализовать invoke и заставить его включать dispid и вызывать различные методы события.Я нашел диспид в библиотеке типов.Глядя на метод в OLE Viewer, я использовал 'id', который, по-видимому, является dispid.

Ниже приведен исправленный файл .cpp.В файле .h единственное исправление состоит в том, чтобы заменить Invoke на объявление вместо реализации.

Два примечания к коду:

  1. Я построил его из различных примеров, которые нашел, поэтому вы можете найти имена или комментарии, которые кажутся неуместными, потому что они были взяты из других источников.
  2. Чтобы получить объект COM, я использовал либо GetActiveObject, либо CoCreateInstance.Первый работает только в том случае, если приложение запуска событий уже запущено.Последний создает невидимый экземпляр этого.Для 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;
 }
3 голосов
/ 19 октября 2010

Ваша проблема номер один в том, что вы не запускаете цикл сообщений в главном потоке и , из-за которого события никогда не достигают вашего объекта-приемника . Вызовы с COM-сервера на ваш объект приемника отправляются с использованием сообщений Windows, поэтому вам нужно запустить цикл обработки сообщений вместо простого вызова Sleep(), чтобы в конечном итоге поступающие события отправлялись объекту приемника.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...