使用本机函数指针来侦听托管事件/封送处理问题
Use native function pointer for listening to managed event / marshaling issue
我正在编写混合模式 C++/CLI 汇编桥,以便能够从旧 C++ 应用程序调用我的 .NET class 库。
在我的 .NET 库中的一个 classes 中,只要需要显示某些消息(到控制台或任何取决于调用应用程序的内容),就可以附加到事件。
class NetApi
{
public event EventHandler<MessageEventArgs> MessageReported;
}
为了从本机 C++ 应用程序调用它,我定义了以下 pointer/delegate 桥:
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
省略用于连接所有内容的胶水(附加到 MessageReported
,从 EventHandler
中删除 sender
,等等...),这是我从本机函数指针创建托管委托的方法:
class NetApiBridge
{
public:
void SetMessageHandler(MessageHandler handler)
{
wrappedListener = (ManagedMessageHandler^)Marshal::GetDelegateForFunctionPointer((IntPtr)handler, ManagedMessageHandler::typeid);
}
private:
msclr::auto_gcroot<NetApi^ > wrappedApi;
msclr::auto_gcroot<ManagedMessageHandler^ > wrappedListener;
// In another helper ref class in fact, but here pseudo code to simplify
void onMessageReported(Object^ sender, MessageEventArgs^ e)
{
if (!wrappedListener) { return; }
wrappedListenter(e->Message); // Send message to native function pointer
}
}
创建虚拟 C++ 测试代码时我就快到了:
void messageHandler(const char* s)
{
cout << s;
}
void main()
{
NetApiBridge api = new NetApiBridge();
api->SetMessageHandler(&messageHandler);
api->Measure();
delete api;
}
一切顺利,事件报告正确,除了 .... 除了我在离开本机处理程序时收到来自托管调试助手的 PInvokeStackImbalance
,我显然不知道为什么?
将 const char*
编组为 UnmanagedType::LPStr
与 GetDelegateForFunctionPointer
有什么问题?
注意:C++ 桥是在 x86 中编译的,如果在这里知道很重要的话。
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
您的委托声明与 32 位代码中的函数指针声明不兼容。本机代码中的默认调用约定几乎总是 __cdecl
。代表的默认值为 __stdcall
。一个有点古怪的选择但受到启发,因为互操作被认为对进行 OS 调用很有用,Windows 和 COM 使用 __stdcall.
现在的不匹配导致委托存根将参数从堆栈中弹出。本机代码也是如此,因此堆栈不平衡 4 个字节。 MDA 可以帮助您诊断这种常见的事故。
你必须提供帮助并让他们同意。使用委托声明:
using namespace System::Runtime::InteropServices;
...
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
delegate void ManagedMessageHandler(String^ msg);
或者函数指针声明:
typedef void (__stdcall * MessageHandler)(const char* msg);
我正在编写混合模式 C++/CLI 汇编桥,以便能够从旧 C++ 应用程序调用我的 .NET class 库。
在我的 .NET 库中的一个 classes 中,只要需要显示某些消息(到控制台或任何取决于调用应用程序的内容),就可以附加到事件。
class NetApi
{
public event EventHandler<MessageEventArgs> MessageReported;
}
为了从本机 C++ 应用程序调用它,我定义了以下 pointer/delegate 桥:
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
省略用于连接所有内容的胶水(附加到 MessageReported
,从 EventHandler
中删除 sender
,等等...),这是我从本机函数指针创建托管委托的方法:
class NetApiBridge
{
public:
void SetMessageHandler(MessageHandler handler)
{
wrappedListener = (ManagedMessageHandler^)Marshal::GetDelegateForFunctionPointer((IntPtr)handler, ManagedMessageHandler::typeid);
}
private:
msclr::auto_gcroot<NetApi^ > wrappedApi;
msclr::auto_gcroot<ManagedMessageHandler^ > wrappedListener;
// In another helper ref class in fact, but here pseudo code to simplify
void onMessageReported(Object^ sender, MessageEventArgs^ e)
{
if (!wrappedListener) { return; }
wrappedListenter(e->Message); // Send message to native function pointer
}
}
创建虚拟 C++ 测试代码时我就快到了:
void messageHandler(const char* s)
{
cout << s;
}
void main()
{
NetApiBridge api = new NetApiBridge();
api->SetMessageHandler(&messageHandler);
api->Measure();
delete api;
}
一切顺利,事件报告正确,除了 .... 除了我在离开本机处理程序时收到来自托管调试助手的 PInvokeStackImbalance
,我显然不知道为什么?
将 const char*
编组为 UnmanagedType::LPStr
与 GetDelegateForFunctionPointer
有什么问题?
注意:C++ 桥是在 x86 中编译的,如果在这里知道很重要的话。
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
您的委托声明与 32 位代码中的函数指针声明不兼容。本机代码中的默认调用约定几乎总是 __cdecl
。代表的默认值为 __stdcall
。一个有点古怪的选择但受到启发,因为互操作被认为对进行 OS 调用很有用,Windows 和 COM 使用 __stdcall.
现在的不匹配导致委托存根将参数从堆栈中弹出。本机代码也是如此,因此堆栈不平衡 4 个字节。 MDA 可以帮助您诊断这种常见的事故。
你必须提供帮助并让他们同意。使用委托声明:
using namespace System::Runtime::InteropServices;
...
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
delegate void ManagedMessageHandler(String^ msg);
或者函数指针声明:
typedef void (__stdcall * MessageHandler)(const char* msg);