使用C++(ATL、MFC或纯C++)处理COM事件以实现Java交互操作

10

我目前正在尝试构建一个C++库(DLL文件),用于与COM组件进行接口交互,以使其可在Java中使用。我的想法是,我将构建一个非常小的C++ DLL,并使用一个“包装”COM组件的类,然后使用SWIG导出它。通过使用#import语句,我已经取得了相当大的进展:

#import "ComponentName.dll"

我需要调用CoInitialize()函数并通过由Visual Studio生成的IComponentNamePtr类创建组件实例。这对于所有常规的COM方法调用都有效,这很好。

然而,我不知道如何使事件起作用。我看到有一个IComponentNameEventsPtr与主“智能指针”类相辅相成,但我不知道该怎么做才能让它起作用。

我尝试了以下所有方法来使事件起作用:

  • Pure C++ — 我不知道如何做到这一点。我尝试创建一个继承自IComponentNameEvents类的新类,为所有抽象函数创建存根,并重写函数,但是函数没有标记为虚函数,因此无法正常工作。
  • MFC — 我无法正确调用AfxOleInit。谷歌搜索告诉我,在从DLL调用时,调用会失败,因为它假定OLE已经初始化。我不太确定如何修复这个问题。每次尝试创建COM组件实例时,我的库都会崩溃(我认为是因为COM未正确初始化)。
  • ATL — 我不知道如何在ATL中处理事件。我可以通过“简单ATL”向导,然后“实现接口”向导创建类,但不能理解如何使用它。我阅读了MSDN上的IDispEventImpl使用,但无法理解如何使用该教程生成的类。我是否也需要通过ATL使用COM对象(或者可以使用自动生成的#import类)?如何“附加”事件侦听器类?
  • 我阅读了COM中的事件处理,该文档使用event_receiver属性(属于新的Visual C++统一事件模型)。最初,我无法弄清如何将其与通过#import语句创建的COM组件的使用结合起来。我最终弄清楚了(该页面上也提到了!),我需要在#import语句上使用“embedded_idl”标志,但这破坏了其他事情(在.tlh文件中出现了一堆“expecting a type specification near”错误)。

有人知道如何做到这一点吗?采取什么最简单的方法?我的背景是C#和PHP,所以我在使用C ++中的COM方面没有太多经验。

tldr:在C ++ DLL中使用COM事件的最简单方法是什么?

2个回答

3

在您的代码中实现源接口(使用任何机制,包括使用midl编译器生成纯C代码)。在您要使用的外部类型库中查找类似以下接口:

 [source] interface IOutGoing;

一旦你实现了它,使用Advise在产生事件的对象上注册它(使用Unadvise取消注册)

这里是一个示例代码片段,展示了一个典型的用法,假设你采用了MIDL方式(使用ATL/MFC可以写更少的代码,但需要了解更多的宏/模板)

class CSink : public IOutGoing
{
public:
    // IUnknown
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();
    HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);

    // IOutGoing
    HRESULT __stdcall GotMessage(int Message);

    CSink() : m_cRef(0) { }
    ~CSink() { }

private:
    long m_cRef;
};


IUnknown* pUnknown;
CoCreateInstance(CLSID_XXXXXXXXX, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pUnknown);

IConnectionPointContainer* pConnectionPointContainer;
hr = pUnknown->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);

hr = pConnectionPointContainer->FindConnectionPoint(IID_IOutGoing, &pConnectionPoint);

// Instantiate the sink object.
CSink* mySink = new CSink;

// Give the connectable object a pointer to the sink.
DWORD dwCookie;
pConnectionPoint->Advise((IUnknown*)mySink, &dwCookie);

我自己使用统一事件模型实现了这个功能。等我有空的时候,我会在这里写一个答案 :) - Daniel Lo Nigro
我正在努力尝试自己完成这个项目,但我不确定IOutGoing应该是什么,我没有对COM库的所有权,并且它似乎没有包含任何类似于IOutGoing的类型。我该如何创建我的CSink类? - innova

2

我使用了统一事件模型,最终使它工作起来了。以下是我在实现过程中的一些注意事项:

然后添加一个 元素。
typedef struct StructName StructName;

对于每个抛出错误的结构体
  • Once that's done, you should be able to initialise COM and create an instance of your object. Ugly example code:

    IComponentName blah;
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        MessageBox(NULL, "Failed to initialize COM", "Hi!", 0);
        throw "Failed to initialize COM";
    }
    
    try
    {
        hr = blah.CreateInstance("Something.Something");
        if (FAILED(hr))
        {
            CoUninitialize();
            MessageBox(NULL, "Failed to initialize the COM library!!", "Hi!", 0);
            throw "Failed to initialize the COM library";
        }
    }
    catch (...)
    {
        MessageBox(NULL, "Exception occured when initialising library!", "Error", 0);
        CoUninitialize();
        throw "Exception occured when initialising library!";
    }
    

一旦您拥有了COM对象,您可以按照MSDN的“COM事件处理”文章中的说明连接事件:

__hook(&IComponentNameEvents::OnWhatever, blah, &EventHandlerClass::OnWhatever);

在调用CoUninitialize()之前,请务必取消所有事件的挂接,否则会导致错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接