我正在尝试将C ++库中的事件触发到C#中的应用程序中。之前的问题:Using unmanaged pointer to callback function on managed C++
感谢我在那个问题评论中收到的帮助,以及Hans Passant回答的前一个问题的这个例子:c++/cli pass (managed) delegate to unmanaged code我思考我得到了它的工作,但是在真实的测试中硬件,事件不会触发。从调试器我可以看到当硬件I / O触发事件时调用函数callback_function,那里的指令打印相应的行,然后调用TriggerEvent(1),但是我没有收到C#代码中的事件处理它。
我怀疑这与如何在"步骤3"之前创建新的CppDIOHandler实例有关,而不是在原始代码中创建委托。我尝试获取当前对象的句柄(类似于"这个"但是使用托管代码,使用GCHandle)来创建CallbackDelegate对象,没有运气。我不明白为什么我不能简单地使用callback = gcnew CallbackDelegate(& callback_function)而不是那条线。
#include "stdafx.h"
#include <WDT_DIO.H>
#include <string.h>
#include <stdio.h>
using namespace System;
using namespace System::Runtime::InteropServices;
public ref class PinStateChangedEventArgs : public EventArgs
{
public:
property int Direction;
property DateTime TimeReached;
};
public ref class CppDIOHandler
{
public:
CppDIOHandler() {
}
delegate void CallbackDelegate(COS_INT_CALLBACK_ARG*);
static CallbackDelegate^ callback;
void __stdcall callback_function(COS_INT_CALLBACK_ARG* arg)
{
printf("data=0x%02x, flag=0x%02x, seq=%02d\n",
arg->portData, arg->intrFlag, arg->intrSeq);
TriggerEvent(1);
}
static void StartDIO() {
//Step 1, initialize DIO library by invoking InitDIO()
if (!InitDIO())
{
Console::WriteLine("InitDIO --> FAILED");
return;
}
Console::WriteLine("InitDIO --> PASSED");
//Step 2, setup Change-of-State Interrupt mask and level/edge mode
COS_INT_SETUP setup;
memset(&setup, 0, sizeof(setup));
setup.portMask = 0x0f; // 00001111b, enable ch.0~3
setup.edgeMode = 0x00; // generate interrupt on level change
setup.edgeType = 0x00; // rising/falling edge, only effective when edgeMode = 1
if (!SetupDICOS(&setup, sizeof(setup)))
{
Console::WriteLine("SetupDICOS --> FAILED");
return;
}
Console::WriteLine("SetupDICOS --> PASSED");
CppDIOHandler^ cdh = gcnew CppDIOHandler();
callback = gcnew CallbackDelegate(cdh,&callback_function);
IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(callback);
COS_INT_CALLBACK functionPointer = static_cast<COS_INT_CALLBACK>(stubPointer.ToPointer());
//TO-DO: Make callback static and remove the GC instruction
//GC::KeepAlive(callback);
//Step 3, register the callback function
if (!RegisterCallbackDICOS(functionPointer))
{
Console::WriteLine("RegisterCallbackDICOS --> FAILED");
return;
}
Console::WriteLine("RegisterCallbackDICOS --> PASSED");
//Step 4, start the DI Change-of-State Interrupt
if (!StartDICOS())
{
Console::WriteLine("StartDICOS --> FAILED");
return;
}
Console::WriteLine("StartDICOS --> PASSED");
}
void StopDIO() {
//Step 5, stop the DI Change-of-State Interrupt operation
if (!StopDICOS())
{
Console::WriteLine("StopDICOS --> FAILED");
return;
}
Console::WriteLine("StopDICOS --> PASSED");
}
void TriggerEvent(int direction)
{
PinStateChangedEventArgs^ args = gcnew PinStateChangedEventArgs();
args->Direction = direction;
args->TimeReached = DateTime::Now;
OnPinStateChanged(args);
}
event EventHandler<PinStateChangedEventArgs^>^ PinStateChanged;
virtual void OnPinStateChanged(PinStateChangedEventArgs^ e)
{
PinStateChanged(this, e);
}
};
C#代码是微不足道的,所以似乎不太可能存在问题,我只是这样做了:
CppDIOHandler cc = new CppDIOHandler();
CppDIOHandler.StartDIO();
cc.PinStateChanged += Cs_PinStateChanged;
但是从不执行Cs_PinstateChanged回调。解决这个问题的正确方法是什么?是否真的有必要使用GCHandle来引用托管代码中的当前对象,否则这与我的问题无关?