c# - 使用 CSExeCOMServer 如何在 VS C++ 中接收事件

标签 c# c++ visual-studio-2012 com

从 Microsoft 的 C# .NET CSExeCOMserver(进程 EXE 外)示例开始 - 我有以下内容:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;


namespace CSExeCOMServer
{
#region Interfaces

[Guid(CSSimpleObject.InterfaceId), ComVisible(true)]
public interface ICSSimpleObject
{
    #region Properties

    float FloatProperty { get; set; }

    #endregion

    #region Methods

    string HelloWorld();

    void GetProcessThreadID(out uint processId, out uint threadId);

    #endregion
}

[Guid(CSSimpleObject.EventsId), ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICSSimpleObjectEvents
{
    #region Events

    [DispId(1)]
    void FloatPropertyChanging(float NewValue, ref bool Cancel);

    #endregion
}

#endregion

[ComVisible(true)]
[Guid(CSSimpleObject.ClassId)]
[ClassInterface(ClassInterfaceType.None)]           // No ClassInterface
[ComSourceInterfaces(typeof(ICSSimpleObjectEvents))]
public class CSSimpleObject : ReferenceCountedObject, ICSSimpleObject
{
    #region COM Component Registration

    internal const string ClassId =
        "DB9935C1-19C5-4ed2-ADD2-9A57E19F53A3";
    internal const string InterfaceId =
        "941D219B-7601-4375-B68A-61E23A4C8425";
    internal const string EventsId =
        "014C067E-660D-4d20-9952-CD973CE50436";

    // These routines perform the additional COM registration needed by 
    // the service.

    [EditorBrowsable(EditorBrowsableState.Never)]
    [ComRegisterFunction()]
    public static void Register(Type t)
    {
        try
        {
            COMHelper.RegasmRegisterLocalServer(t);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message); // Log the error
            throw ex; // Re-throw the exception
        }
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    [ComUnregisterFunction()]
    public static void Unregister(Type t)
    {
        try
        {
            COMHelper.RegasmUnregisterLocalServer(t);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message); // Log the error
            throw ex; // Re-throw the exception
        }
    }

    #endregion

    #region Properties

    private float fField = 0;

    public float FloatProperty
    {
        get { return this.fField; }
        set
        {
            bool cancel = false;
            // Raise the event FloatPropertyChanging
            if (null != FloatPropertyChanging)
                FloatPropertyChanging(value, ref cancel);
            if (!cancel)
                this.fField = value;
        }
    }

    #endregion

    #region Methods

    public string HelloWorld()
    {
        return "HelloWorld";
    }

    public void GetProcessThreadID(out uint processId, out uint threadId)
    {
        processId = NativeMethod.GetCurrentProcessId();
        threadId = NativeMethod.GetCurrentThreadId();
    }

    #endregion

    #region Events

    [ComVisible(false)]
    public delegate void FloatPropertyChangingEventHandler(float NewValue, ref bool Cancel);
    public event FloatPropertyChangingEventHandler FloatPropertyChanging;

    #endregion
}

/// <summary>
/// Class factory for the class CSSimpleObject.
/// </summary>
internal class CSSimpleObjectClassFactory : IClassFactory
{
    public int CreateInstance(IntPtr pUnkOuter, ref Guid riid, 
        out IntPtr ppvObject)
    {
        ppvObject = IntPtr.Zero;

        if (pUnkOuter != IntPtr.Zero)
        {
            // The pUnkOuter parameter was non-NULL and the object does 
            // not support aggregation.
            Marshal.ThrowExceptionForHR(COMNative.CLASS_E_NOAGGREGATION);
        }

        if (riid == new Guid(CSSimpleObject.ClassId) ||
            riid == new Guid(COMNative.IID_IDispatch) ||
            riid == new Guid(COMNative.IID_IUnknown))
        {
            // Create the instance of the .NET object
            ppvObject = Marshal.GetComInterfaceForObject(
                new CSSimpleObject(), typeof(ICSSimpleObject));
        }
        else
        {
            // The object that ppvObject points to does not support the 
            // interface identified by riid.
            Marshal.ThrowExceptionForHR(COMNative.E_NOINTERFACE);
        }

        return 0;   // S_OK
    }

    public int LockServer(bool fLock)
    {
        return 0;   // S_OK
    }
}

/// <summary>
/// Reference counted object base.
/// </summary>
[ComVisible(false)]
public class ReferenceCountedObject
{
    public ReferenceCountedObject()
    {
        // Increment the lock count of objects in the COM server.
        ExeCOMServer.Instance.Lock();
    }

    ~ReferenceCountedObject()
    {
        // Decrement the lock count of objects in the COM server.
        ExeCOMServer.Instance.Unlock();
    }
}
}

上面的寄存器和我输出的 test.tlb 文件很好 - 我有一个 COM 客户端,它在标准的 Visual C++ 中使用上面的 COM 服务器,如下所示:

#include "stdafx.h"
#include <objbase.h>
#include <comutil.h>

#import <C:\Users\ndavis\Documents\Visual Studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\test.tlb> no_namespace named_guids

int _tmain(int argc, _TCHAR* argv[])
{

ULONG procID;
ULONG threadID;

//initialize COM for this thread
//HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
HRESULT hr = CoInitialize(NULL);

ICSSimpleObjectPtr pSimple_1;
hr = pSimple_1.CreateInstance(CLSID_CSSimpleObject, NULL, CLSCTX_LOCAL_SERVER);
_bstr_t hw_response = pSimple_1->HelloWorld();
_bstr_t testID = pSimple_1->GetProcessThreadID(&procID, &threadID);

pSimple_1->FloatProperty = 1.7f;

CoUninitialize();

    return 0;

}

以上所有工作:pSimple_1->HelloWorld();返回正确的响应,pSimple_1->GetProcessThreadID(...) 返回正确的响应; pSimple_1->FloatProperty = 1.7f 正确设置 FloatProperty。

问题:如何在上面的 Visual C++ 客户端代码中接收 FloatPropertyChanging(float value, bool* cancel) 事件? (请不要使用 ATL)。

谢谢

让我更进一步的附加信息 - 我已将以下内容添加到我的代码中:

将以下类添加到我的客户端:

class EventHandler : public ICSSimpleObjectEvents
{

public:
EventHandler(void) { }
~EventHandler(void) { }

HRESULT __stdcall QueryInterface(const IID &, void **);
ULONG __stdcall AddRef(void) { return 1; }
ULONG __stdcall Release(void) { return 1; }

virtual HRESULT __stdcall GetTypeInfoCount(UINT * pTypeInfoCount) { return -1; }
virtual HRESULT __stdcall GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) { return -1; }
virtual HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) { return -1; }
virtual HRESULT __stdcall Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr) { return -1; }

virtual HRESULT __stdcall raw_FloatPropertyChanging(float x, VARIANT_BOOL * cancel) { return -1; }

HRESULT __stdcall FloatPropertyChanging(float NewValue, bool *Cancel);
};

HRESULT __stdcall EventHandler::FloatPropertyChanging(float NewValue, bool *Cancel) {
printf("float value changing: NewValue = %f", NewValue);
return S_OK;
}

HRESULT __stdcall EventHandler::QueryInterface(const IID &iid, void **pp) {

if(iid == __uuidof(ICSSimpleObjectEvents) || iid == __uuidof(IUnknown) || iid == __uuidof(IDispatch))
{
    *pp = this;
    AddRef();
    return S_OK;
}
return E_NOINTERFACE;
}

并添加以下内容以获得 IConnectionPoint:

IUnknown *pUnk = NULL;

hr = CoCreateInstance(CLSID_CSSimpleObject, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **)&pUnk);

IConnectionPointContainerPtr pContainer;
IConnectionPointPtr pConnection;
hr = pUnk->QueryInterface(__uuidof(IConnectionPointContainer), (void**) &pContainer);
hr = pContainer->FindConnectionPoint(__uuidof(ICSSimpleObjectEvents), (IConnectionPoint**) &pConnection);


EventHandler* pSink = new EventHandler;

DWORD dwAdviseCookie;

hr = pConnection->Advise((IUnknown*)pSink, &dwAdviseCookie);

pSimple_1->FloatProperty = 1.7f;

在 C# .NET COM 服务器中设置断点:

    public float FloatProperty
    {
        get { return this.fField; }
        set
        {
            bool cancel = false;   <--- I set break point here
            // Raise the event FloatPropertyChanging
            if(FloatPropertyChanging != null)  <--- FloatPropertyChanging is null here?
                FloatPropertyChanging(value, ref cancel);
            if (!cancel)
                this.fField = value;
        }
    }

我看到 FloatPropertyChanging 为空,因此 FloatPropertyChanging(...) 从未被调用? 有人看到我做错了什么吗?

最佳答案

示例文档特别要求事件接口(interface)是仅分派(dispatch)的:

enter image description here

但在您的代码中,您已将其设为双界面。

[Guid(CSSimpleObject.EventsId), ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICSSimpleObjectEvents

我也没有在您的 C++ 客户端代码中看到 Invoke 的实现。好吧,我愿意。这是一个严重损坏的。

我强烈建议您尝试通过 IDispatch 实现事件,因为示例指示这样做。您可以保留双界面并使用 DispInvoke根据 v 表自动实现 IDispatch::Invoke

至少,在您的 Invoke 空实现中放置一个断点,并查看在触发事件时是否命中断点。

另外,例如this sample (not .NET)还指示 IDispatch 应该用于事件源/接收器。

关于c# - 使用 CSExeCOMServer 如何在 VS C++ 中接收事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20691899/

相关文章:

c# - Microsoft 内部属性 - 该属性没有 setter

c# - 创建结果已经准备好的任务的正确方法

c++ - 结构的原型(prototype)函数

c# console 从用户输入中获取带有索引的数组值

c++ - 为什么 lambda 函数不适用于 std::min_element?

c++ - printf/sprintf 删除登录号码的问题

mocking - 在 VS 2012 中放弃 Pex 和 Moles?

c# - 测试资源管理器 > 全部运行 : System. 异常:找不到主应用程序程序集

c# - 如何访问Silverlight客户端和服务器端?

c# - Quartz.NET 在随机持续时间后停止触发