将c#函数传递给DLL以进行回调

本文关键字:回调 DLL 函数 | 更新日期: 2023-09-27 18:16:57

我在(stdcall) DLL中有一些函数,它们接受函数指针来执行回调。

方法DeviceStateChangedNotifyCardDataStateChangedNotify接受一个Long参数作为回调地址。

下面是一个VB6示例:

Declare Sub DeviceStateChangedNotify Lib "Device.dll" (ByVal lpFunc As Long)
Declare Sub CardDataStateChangedNotify Lib "Device.dll" (ByVal lpFunc As Long)
Public Sub SetupCallBacks()
     Call DeviceStateChangedNotify(AddressOf OnEventDeviceStateChanged)
     Call CardDataStateChangedNotify(AddressOf OnEventCardDataStateChanged)
End Sub
Public Sub OnEventDeviceStateChanged(ByVal parm As Long)
    ...
End Sub
Public Sub OnEventCardDataStateChanged(ByVal parm As Long)
    ...
End Sub

我如何在c#中做等效的?

我试图创建一个与OnEventDeviceStateChangedOnEventCardDataStateChanged相同方法签名的委托,但似乎不起作用:

delegate void DeviceStateChanged(long parm);
DeviceStateChanged stateChanged = EventDeviceStateChanged;
CardDataStateChangedNotify(stateChanged);

这会在最后一行产生语法错误:

无法从"DeviceStateChanged"转换为"long"

将c#函数传递给DLL以进行回调

你很接近了。

是的,您需要创建一个委托来匹配回调函数签名,然后更改DLL函数声明以将该委托用于函数指针参数。P/Invoke将根据需要封送它,并传递一个指向函数的有效指针。

[DllImport("Device.dll")]
private static extern void DeviceStateChangedNotify(DeviceStateChangedDelegate Func);
private delegate void DeviceStateChangedDelegate(Int32 parm);
private void OnDeviceStateChanged(Int32 parm) {
  ...
}

当你需要传递它时,只需直接传递函数:

DeviceStateChangedNotify(OnDeviceStateChanged);

请注意,由于。net应用程序(默认情况下)与体系结构无关(将以32位或64位运行,具体取决于系统体系结构),您需要确保您的DLL和目标体系结构匹配。

如果你只有一个32位的DLL,那么你需要将你的。net项目设置为x86。

如果你有32位和64位DLL,那么你可以把。net项目设置为任意目标,但你需要检查DLL回调是否使用32位整数(c++ DWORD等)或系统整数(c++ int)。如果它确实使用了一个系统整数,那么你需要更新你的委托和回调函数来使用IntPtr而不是intInt32

您需要声明您的p/invoke代码,以便它接收委托而不是long。在VB6中,没有办法指定委托,甚至指针,因此使用整数和AddressOf

另一个问题是VB6 Long是32位的,所以你需要使用c# int来匹配。这是因为c# long是64位的。

所以,在c#中你可以这样做:
delegate void DeviceStateChanged(int parm);
[DllImport(@"Device.dll")]
static extern void DeviceStateChangedNotify(DeviceStateChanged lpFunc);
[DllImport(@"Device.dll")]
static extern void CardDataStateChangedNotify(DeviceStateChanged lpFunc);

当然你需要调整你的回调函数来匹配委托的新定义。

就是这样!