簡體   English   中英

如何在 COM 事件源和處理程序中指定“事件類型”?

[英]How do you specify “event type” in COM event source and handler?

我有一個 COM object 寫在 C# 中,我在 ZF6F87C9FDCF8B3C3F07F93F1Z71 中使用它沒有問題,直到我有問題。 我已經嘗試在這里查看無數的教程、文檔和問題,但奇怪的是,它們都不適合我的確切情況。

從我的代碼中將事件源掛鈎/取消掛鈎到接收器的部分,我收到此錯誤:

Error   C3731   incompatible event 'HRESULT EggplantClient::IEggplantClientEvents::Completed(void)' and handler 'HRESULT CReceiver::Completed(void)'; event source and event handler must have the same event type

我不知道這個“事件類型”是什么。 我假設它是 CReceiver class 屬性中的“com”部分:

[module(name = "EventReceiver")]
[event_receiver(com, true)]
class CReceiver {
...

至少這是我可以從Microsoft 文檔中收集到的關於錯誤代碼的信息。 如果是這樣,我如何將 C# 事件源設置為相同類型?

我遇到的另一個非常奇怪的錯誤是:

Error   C3702   ATL is required for COM events

這指向我定義class CReceiver的行。 我有與錯誤的Microsoft 文檔中包含的完全相同的 header 文件。 我還收到一條警告,說明[module(name = "EventReceiver")]usage of ATL attributes is deprecated ,我認為這些是相關的嗎?

我已經堅持了好幾天了。 這是我第一次用 COM 做事,甚至 COM 服務器的基本實現也很困難,但試圖讓事件正常工作卻是一場徹頭徹尾的噩夢。 如果有人能以任何方式提供幫助,我將不勝感激,即使是一個教程的鏈接,該鏈接顯示了在 C++ 客戶端中的 C# COM 服務器上工作的事件。 以下是到目前為止我能夠拼湊的相關部分。 我將用於客戶端代碼,服務器部分我什至找不到了,因為我已經瀏覽了很多頁面。

C# COM 服務器,事件源

namespace EggplantClient
{
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("C61C7C47-BB98-4DF3-BC61-7CA9430EDE7A")]
    [ComVisible(true)]
    public interface IEggplantClientEvents
    {
        [DispId(1)]
        void Completed();
    }

    [Guid("0a805b99-756a-493c-96b7-063400f171ed")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IEggplantClientEvents))]
    [ProgId("EggplantClient.CEggplantClient")]
    public class CEggplantClient : IEggplantClient
    {
        [ComVisible(false)]public delegate void CompletedDelegate();
        public event CompletedDelegate Completed;
...

C++ COM 客戶端,事件接收器

#define _ATL_ATTRIBUTES 1
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <stdio.h>

int Flag = 0;

[module(name = "EventReceiver")]
[event_receiver(com, true)]
class CReceiver {
public:

    HRESULT Completed() {
        printf_s("Event received");
        Flag = 1;
        return S_OK;
    }

    void HookEvent(EggplantClient::IEggplantClient* pSource) {
        __hook(&EggplantClient::IEggplantClientEvents::Completed, pSource, &CReceiver::Completed);
    }

    void UnhookEvent(EggplantClient::IEggplantClient* pSource) {
        __unhook(&EggplantClient::IEggplantClientEvents::Completed, pSource, &CReceiver::Completed);
    }
};

.NET 中的事件被視為本機端的連接點 您可以將它們與 ATL 一起使用,如此處所述ATL 連接點事件處理原則

所以這里有一個小回顧。

這是您的 C# class

namespace Eggplant
{
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("C61C7C47-BB98-4DF3-BC61-7CA9430EDE7A")]
    [ComVisible(true)]
    public interface IEggplantClientEvents
    {
        [DispId(1)]
        void Completed(string text);
    }

    [Guid("0a805b99-756a-493c-96b7-063400f171ed")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IEggplantClientEvents))]
    [ProgId("EggplantClient.CEggplantClient")]
    public class CEggplantClient
    {
        [ComVisible(false)] public delegate void CompletedDelegate(string text);
        public event CompletedDelegate Completed;

        public CEggplantClient()
        {
            // wait 2 seconds and then call every second
            Task.Delay(2000).ContinueWith(async t =>
            {
                do
                {
                    Completed?.Invoke("Time is " + DateTime.Now);
                    await Task.Delay(1000);
                }
                while (true);
            });
        }
    }
}

您可以像這樣使用 .NET 框架注冊您的 C# class (將創建一個Eggplant.tlb文件):

%windir%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe Eggplant.dll /codebase /tlb

note: with .NET core and .NET 5 you'll have to build your own.TLB, or copy your C# .NET Core code into a .NET Framework.dll and use this...

這是您的 C/C++ 代碼(省略了前向引用):

#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
#include <atlcom.h>

#import "D:\kilroy\was\here\Eggplant\bin\Debug\Eggplant.tlb" // import the tlb

using namespace Eggplant; // #import by default puts generated code in a specific namespace

int main()
{
    CoInitialize(nullptr);
    {
        CComPtr<IUnknown> app;
        if (SUCCEEDED(app.CoCreateInstance(__uuidof(CEggplantClient))))
        {
            // sink events
            auto sink = new CEggplantClientEventsSink();
            if (SUCCEEDED(sink->Connect(app)))
            {
                // this message box allows us to wait while events arrive
                MessageBox(nullptr, L"Click to stop listening", L"Events", MB_OK);
            }
        }
    }
    CoUninitialize();
    return 0;
}

// this is the event sink
class CEggplantClientEventsSink : public CDispInterfaceBase<IEggplantClientEvents>
{
public:
    CEggplantClientEventsSink() { }

    HRESULT Invoke(DISPID dispid, DISPPARAMS* pdispparams, VARIANT* pvarResult)
    {
        switch (dispid)
        {
        case 1: // the Completed DISPID value
            wprintf(L"Completed called text:%s\n", pdispparams->rgvarg[0].bstrVal);
            break;
        }
        return S_OK;
    }
};

// this is a generic support class to hook IDispatch events
// adapted from here: https://devblogs.microsoft.com/oldnewthing/20130612-00/?p=4103
template<typename DispInterface>
class CDispInterfaceBase : public DispInterface
{
    LONG m_cRef;
    CComPtr<IConnectionPoint> m_spcp;
    DWORD m_dwCookie;

public:
    CDispInterfaceBase() : m_cRef(1), m_dwCookie(0) { }

    // IUnknown
    IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        *ppv = nullptr;
        HRESULT hr = E_NOINTERFACE;
        if (riid == IID_IUnknown || riid == IID_IDispatch || riid == __uuidof(DispInterface))
        {
            *ppv = static_cast<DispInterface*>(static_cast<IDispatch*>(this));
            AddRef();
            hr = S_OK;
        }
        return hr;
    }

    IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_cRef); }
    IFACEMETHODIMP_(ULONG) Release() { LONG cRef = InterlockedDecrement(&m_cRef); if (!cRef) delete this; return cRef; }

    // IDispatch
    IFACEMETHODIMP GetTypeInfoCount(UINT* pctinfo) { *pctinfo = 0; return E_NOTIMPL; }
    IFACEMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { *ppTInfo = nullptr; return E_NOTIMPL; }
    IFACEMETHODIMP GetIDsOfNames(REFIID, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { return E_NOTIMPL; }
    IFACEMETHODIMP Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
    {
        if (pvarResult) VariantInit(pvarResult);
        return Invoke(dispid, pdispparams, pvarResult);
    }

    virtual HRESULT Invoke(DISPID dispid, DISPPARAMS* pdispparams, VARIANT* pvarResult) = 0;

public:
    HRESULT Connect(IUnknown* punk)
    {
        CComPtr<IConnectionPointContainer> spcpc;
        HRESULT  hr = punk->QueryInterface(IID_PPV_ARGS(&spcpc));
        if (SUCCEEDED(hr)) hr = spcpc->FindConnectionPoint(__uuidof(DispInterface), &m_spcp);
        if (SUCCEEDED(hr)) hr = m_spcp->Advise(this, &m_dwCookie);
        return hr;
    }

    void Disconnect()
    {
        if (m_dwCookie)
        {
            m_spcp->Unadvise(m_dwCookie);
            m_spcp.Release();
            m_dwCookie = 0;
        }
    }
};

這是結果:

.NET COM 事件

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM