从 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)的:
但在您的代码中,您已将其设为双界面。
[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/