我在(stdcall)DLL中有一些函数,它们接受一个函数指针来执行回调。
方法DeviceStateChangedNotify
和CardDataStateChangedNotify
接受回调地址的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#中进行相同的操作?
我尝试使用与OnEventDeviceStateChanged
和OnEventCardDataStateChanged
相同的方法签名创建委托,但似乎无效:
delegate void DeviceStateChanged(long parm);
DeviceStateChanged stateChanged = EventDeviceStateChanged;
CardDataStateChangedNotify(stateChanged);
这会在最后一行产生语法错误:
无法从'DeviceStateChanged'转换为'long'
答案 0 :(得分:3)
你非常接近。
是的,您需要创建一个委托以匹配回调函数签名,然后更改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项目设置为target any,但是你需要检查DLL回调是否使用32位整数(C ++ {{1}等等)或系统整数(C ++ DWORD
)。
如果它确实使用系统整数,那么您需要更新您的委托和回调函数以使用int
而不是IntPtr
或int
。
答案 1 :(得分:1)
您需要声明您的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);
当然,您需要调整回调函数以匹配代理的新定义。
就是这样!