最近做專案的時候,需要對接廠商提供的 IP 攝像頭,但是他們只提供了 C++ 的 SDK,沒辦法,只能開始擼 C# 的 SDK Helper 類,本篇文章主要記錄了對接 C++ DLL 需要注意的幾個地方,以及常見型別的轉換,
要對接 C++ 的 DLL,首先得知道如何參考 DLL 內的方法,在 C# 當中,只需要撰寫符合 C++ 的函式簽名,再使用 [DllImport] 特性指定 DLL 檔案路徑和入口點等引數即可,
假如你需要使用 Win32 API 提供的方法,這里我以 SetProcessDPIAware 函式為例:
public static class Win32Helper
{
[DllImport("user32.dll")]
public static extern bool SetProcessDPIAware();
}
接下來你只需要像使用靜態方法一樣,呼叫 Win32Helper.SetProcessDPIAware() 方法即可,
對接 DLL 時的問題記錄
一般來說,提供 SDK 的廠商都會給你一份 DEMO 專案,或者是包含有函式定義的頭檔案 (*.h),你只需要按照轉換規則,將頭檔案里面的函式簽名翻譯成 C# 版本的即可,
函式簽名不正確

有的時候,你名字直接和頭檔案一樣還不行,得手動指定 EntryPoint 引數,你可以使用 DLL Export Viewer 工具來查看 DLL 的所有開放函式簽名,將其復制下來,填寫到 EntryPoint 引數即可,

[DllImport(@"ThirdFiles\AlprSDK.dll", EntryPoint = "AlprSDK_Startup@12", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
public static extern int AlprSDK_Startup(IntPtr hNotifyWnd, uint nCommandId, string pLocalAddress);
傳遞回呼函式
有時第三方 SDK 需要你傳遞回呼函式,一般都只提供了一個 void* 定義,也就是一個函式指標,那我們在 C# 如何將委托傳遞給該引數作為回呼函式呢?
ALPRSDK_API OS_Error WINAPI AlprSDK_SearchAllCameras(unsigned int nTimeout,void* callback, char *pLocalAddr = NULL);
這個時候就需要使用到 [UnmanagedFunctionPointer] 特性來指定函式指標了,只需要將其標注到委托定義上,指定函式的呼叫方式即可,
最后我在 C# 里面撰寫的方法簽名如下:
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)]
public delegate void SearchAllCamerasCallback(uint deviceType, string deviceName, string deviceIp,
byte[] macAddress, ushort wPortWeb, ushort wPortListen, string pSubMask, string pGateway,
string pMultiAddress, string pDnsAddress, ushort wMultiPort, int nChannelNum, int nFindCount,
uint dwDeviceId);
[DllImport(@"ThirdFiles\AlprSDK.dll", EntryPoint = "_AlprSDK_SearchAllCameras@12", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
public static extern int AlprSDK_SearchAllCameras(uint nTimeout, SearchAllCamerasCallback callback, string pLocalAddress);
獲取攝像頭傳遞的位圖
原始 C++ 的函式簽名如下:
////////////////////////////////////////////////////////////////////////////////////////////
//捕獲一張bmp圖片.
//pBmpBuf:存放資料的緩沖區,傳入引數時應該為NULL,記憶體由SDK自行管理.外面的應用程式不用去釋放記憶體
//len: 資料的長度
ALPRSDK_API OS_Error WINAPI AlprSDK_CaptureBmp(int nHandleID, void **pBmpBuf, int *len);
主要的難點在于引數 void** pbmp 的翻譯,這里引數 xx 就是指標的指標,因為這個位圖是 SDK 來生成的,所以它會在記憶體空間開辟一段區域用于位圖的存盤,所以 void* 指向的是這個位圖的起始地址,而我傳遞 void** 就是讓 SDK 將這個起始地址傳遞給我,
所以 void* 可以翻譯為 IntPtr,而這個地址不是我賦值的,而是 SDK 給我的地址,所以我們需要加上按參考傳遞關鍵字 ref ,
如此,我們便獲得了位圖在記憶體空間的起始地址,而且方法也將這個位圖的大小給了我們,我們只需要從起始地址讀取 N 個位元組的資料,將其轉儲到 byte[] 即可,有了 byte[] 物件,你就可以進行其他的操作了,例如加載,保存等,
在 C# 內部,我是這樣定義方法簽名,并進行使用的:
[DllImport(@"ThirdFiles\AlprSDK.dll", EntryPoint = "_AlprSDK_CaptureBmp@12", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
public static extern uint AlprSDK_CaptureBmp(int nHandleId, ref IntPtr pBmpBuf, ref int len);
讀取位圖資料,并將其存盤到磁盤當中,
var bitmapPtr = IntPtr.Zero;
var length = 0;
var result = AlprSdk.AlprSDK_CaptureBmp(0, ref bitmapPtr, ref length);
ThrowIfResultNotZero("無法從攝像頭獲取位圖",result);
var bytes = new byte[length];
Marshal.Copy(bitmapPtr, bytes, 0, length);
using (var ms = File.Create(@"D:\bitmap.bmp"))
{
using (var writer = new StreamWriter(ms))
{
writer.Write(bytes);
}
}
附錄 1:常用資料型別對照表
| C/C++ | C# | 備注 |
|---|---|---|
WORD |
ushort |
|
DWORD |
uint |
|
UCHAR |
int 或 byte |
|
UCHAR* |
string 或 IntPtr |
|
unsigned char* |
[MarshalAs(UnmanagedType.LPArray)]byte[] |
|
char* |
string |
|
LPCTSTR |
string |
|
LPTSTR |
[MarshalAs(UnmanagedType.LPTStr)] string |
|
long |
int |
|
ulong |
uint |
|
HANDLE |
IntPtr |
|
HWND |
IntPtr |
|
void* |
IntPtr |
|
int |
int |
|
int* |
ref int |
|
*int |
IntPtr |
|
unsigned int |
uint |
|
COLORREF |
uint |
|
CHAR |
char |
|
HDC |
int |
|
HGDIOBJ |
int |
|
BOOL |
bool |
|
LPSTR |
string |
|
LPCSTR |
string |
|
BYTE |
byte |
參考文章:C# 與 C++ 資料型別對照
附錄 2:相關工具軟體下載
DLL Export Viewer v1.66:https://files.cnblogs.com/files/myzony/DLL_Export_Viewer_v1.66.zip
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/97963.html
標籤:C#
