C# 結合 PInvoke 對接 IP 攝像頭的筆記

来源:https://www.cnblogs.com/myzony/archive/2019/11/27/11944867.html
-Advertisement-
Play Games

最近做項目的時候,需要對接廠商提供的 IP 攝像頭。但是他們只提供了 C++ 的 SDK,沒辦法,只能開始擼 C 的 SDK Helper 類。本篇文章主要記錄了對接 C++ DLL 需要註意的幾個地方,以及常見類型的轉換。 要對接 C++ 的 DLL,首先得知道如何引用 DLL 內的方法。在 C ...


最近做項目的時候,需要對接廠商提供的 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 intbyte
UCHAR* stringIntPtr
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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 封裝一個資料庫模塊有三個功能:查詢,插入,關閉 1.查看 2.提交 3.關閉 ...
  • 前言 作為一個小白,在學習之前,我非常的明確,自己要學什麼編程語言。 怎麼判斷某門編程語言掉不掉發? 說起掉發,在前言中講過,程式員很多掉發原因都是因為選“錯”了編程語言,接下來讓我們看看編程語言各個撞死人(創始人)的發量是有多麼的恐怖! PHP之父:拉斯馬斯·勒德爾夫 拉斯馬斯·勒德爾夫,創建PH ...
  • 數據結構 數據結構(演算法)的介紹 數據結構的介紹 1) 數據結構是一門研究演算法的學科,只從有了編程語言也就有了數據結構.學好數據結構可以編寫 出更加漂亮,更加有效率的代碼。 2) 要學習好數據結構就要多多考慮如何將生活中遇到的問題,用程式去實現解決. 3) 程式 = 數據結構 + 演算法 20.2 數 ...
  • 一、前言 在概念上, click 把命令行分為 3 個組成:參數、選項和命令。 參數 就是跟在命令後的除選項外的內容,比如 git add a.txt 中的 a.txt 就是表示文件路徑的參數 選項 就是以 或 開頭的參數,比如 f、 file 命令 就是命令行的初衷了,比如 git 就是命令,而 ...
  • 本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/Thread.html,記錄一下學習過程以備後續查用。 一、線程的介紹 進程(Process)是應用程式的實例要使用的資源的一個集合,每個應用程式都在各自的進程中運行來確保應用程式不受其他 ...
  • using System.Reflection; static void ShowEnvironmentInfoDemo() { Type type = typeof(Environment); PropertyInfo[] pis = type.GetProperties(); if (pis !... ...
  • private void ClickCmdExecuted(object obj) { ContentOb = new ObservableCollection<string>(); Task.Run(() => { while (!cts.IsCancellationRequested) { Co ...
  • 修改屏幕DPI,會觸發控制項的Unloaded/Loaded 現象/重現案例 對Unloaded/Loaded的印象: FrameworkElement, 第一次載入顯示時,會觸發Loaded。元素被釋放時,會觸發Unloaded。視窗Show/Close時,視覺樹變化都會觸發載入事件 MenuIte ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...