假设C/C++的dll中定义回调函数:
// Received data callback typedef void(__stdcall *ReceivedDataCallback)(SerialPortRef sp, unsigned char* data, int len); /********************************************************* * Library API definitions * /*********************************************************/ #ifdef __cplusplus extern "C" { #endif // Register handl to receive data from a SerialPort. SERIALPORTRW_API void RegisterReceivedDataCallback(SerialPortRef sp, ReceivedDataCallback handle); //......................
C#中包含dll,通过包装dll的函数和回调函数,以便dll中收到数据时回调到C#:
// Wrapper of library by C# namespace PluginImport { #region delegates public delegate void ReceivedDataCallback(IntPtr serialPortRef, [MarshalAs(UnmanagedType.LPArray)]byte[] data, int len); #endregion #region API Wraper public class SerialPortDevice { // Register handl to receive data from a SerialPort. [DllImport("SerialPortDevice", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] public static extern void RegisterReceivedDataCallback(UIntPtr sp, ReceivedDataCallback handle);
使用此DLL包装的API:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ReceivedDataCallback = (IntPtr serialPortRef, byte[] data, int len) =>
{
byte[] localData = data;
this.Dispatcher.Invoke(new Action(() => { tbLog.Text += ByteUtils.bytesToHexString(localData, 0, len); }));
};
SerialPortStatusChanged = (IntPtr serialPortRef, SerialPortStatus status, string msg) =>
{
this.Dispatcher.Invoke(new Action(() => { tbLog.Text += "\r\n" + msg; }));
};
spRef = SerialPortDevice.CreateSerialPort();
if (spRef != UIntPtr.Zero)
{
SerialPortDevice.RegisterReceivedDataCallback(spRef, ReceivedDataCallback);
SerialPortDevice.RegisterSerialPortStatusChangedCallback(spRef, SerialPortStatusChanged);
}
发现上面红色部分的data的长度总是一个字节。
原因:
marshaller不知道data的长度到底是多少,只是简单的按照数据类型返回了数组中的第一个元素,根本没有办法marshal。
正确的做法是将data定义为指针,然后通过拷贝的方法将数据取到C#端:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ReceivedDataCallback = (IntPtr serialPortRef, IntPtr data, int len) =>
{
byte[] localData = new byte[len];
Marshal.Copy(data, localData, 0, len);
string strHex = ByteUtils.bytesToHexString(localData, 0, len);
this.Dispatcher.Invoke(new Action(() => { tbLog.Text += "\r\n" + strHex; }));
};
SerialPortStatusChanged = (IntPtr serialPortRef, SerialPortStatus status, string msg) =>
{
this.Dispatcher.Invoke(new Action(() => { tbLog.Text += "\r\n" + msg; }));
};
spRef = SerialPortDevice.CreateSerialPort();
if (spRef != UIntPtr.Zero)
{
SerialPortDevice.RegisterReceivedDataCallback(spRef, ReceivedDataCallback);
SerialPortDevice.RegisterSerialPortStatusChangedCallback(spRef, SerialPortStatusChanged);
}
}
当然,在这之前要在C#端的回调函数定义中的数据也要改成指针:
public delegate void ReceivedDataCallback(IntPtr serialPortRef, IntPtr data, int len);