C++ 回調簽名:typedef void (*TDataEvent)(char *AData, int ALen);
AData:C++ DLL 內部分配內存的字符串指針ALen:數據長度C# 直接用 byte[] 接收會導致數據截斷(僅收到第一字節),因為默認封送無法正確處理非托管指針。
// 錯誤:byte[] 默認封送為 In/Out 參數,不匹配 char*
public delegate void TDataEvent(byte[] AData, int ALen);
public void getDate(byte[] AData, int ALen)
{
// AData.Length 永遠為 1,數據丟失
}
using System;
using System.Runtime.InteropServices;
public delegate void TDataEvent(IntPtr pData, int len); // 1. 用 IntPtr 接收指針
public class CppInterop
{
// 2. 保持委托實例存活,防止 GC 回收
private TDataEvent _callbackInstance;
public void RegisterCallback()
{
_callbackInstance = new TDataEvent(OnDataReceived);
// 假設 ReceDllPoxy.AddServer 是導入的 C++ 函數
ReceDllPoxy.AddServer(1024, _callbackInstance, 2);
}
private void OnDataReceived(IntPtr pData, int len)
{
if (pData == IntPtr.Zero || len <= 0) return;
// 3. 正確複製非托管內存到托管字節數組
byte[] managedData = new byte[len];
Marshal.Copy(pData, managedData, 0, len);
// 4. 按編碼轉字符串(視 C++ 實際編碼而定)
string str = System.Text.Encoding.Default.GetString(managedData);
// 或 UTF8: Encoding.UTF8.GetString(managedData)
Console.WriteLine($\"收到數據 ({len} 字節): {str}\");
// 注意:內存由 C++ 端管理,C# 端不應釋放
}
}
// C++ DLL 導入簽名示例
internal static class ReceDllPoxy
{
[DllImport(\"YourCppDll.dll\", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddServer(int port, TDataEvent callback, int flag);
}
| 問題 | 解決方案 |
|---|---|
| byte[] 只收到 1 字節 | 改用 IntPtr 接收原始指針 |
| 委托被 GC 回收 | 將委托實例存為類成員變量 |
| 非托管內存複製 | Marshal.Copy(ptr, byte[], 0, len) |
| 編碼不匹配 | 確認 C++ 端編碼(ANSI/UTF-8),對應選擇 Encoding |
| 調用約定 | C++ 默認 __cdecl,C# 需指定 CallingConvention.Cdecl |
文中提供的 clDllBasePoxy / clDllPoxy 展示了通過 LoadLibrary / GetProcAddress / Marshal.GetDelegateForFunctionPointer 實現運行時動態綁定非托管 DLL 的完整框架,適合插件化架構。