C# 结合 PInvoke 对接 IP 摄像头的笔记
最近做项目的时候,需要对接厂商提供的 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
C# 结合 PInvoke 对接 IP 摄像头的笔记的更多相关文章
- 《图解tcp/ip》读书笔记(二)
<图解tcp/ip>读书笔记(二) 本周主要阅读的是本书的第三章--数据链路. 当然了,从某些角度讲,我认为这一章就是计算机网络的最基本的内容之一.整章讲述了数据链路层的作用和相关技术,主 ...
- OpenCV 连接 Android IP摄像头
0.下载IP摄像头(android软件)并安装 比如这个(图标是一个灰色的摄像头的那个软件) 1.新建cpp文件,编译 #include "opencv2/opencv.hpp" ...
- opencv获取IP摄像头(IP-camera)实时视频流
之前这篇文章讲了如何通过网络摄像头(web camera)获取实时视频流,但是这种方法的缺陷就是摄像头和主机必须连在一起,那这种在室外部署的时候就会非常麻烦并且不安全,所以后来找了下用海康威视或者大华 ...
- OpenCV获取IP摄像头视频
从开源中国博客搬来,合并博客 实验室做一个智能小车的小项目,期间涉及到在PC端处理小车摄像头的视频.这里先用安卓手机代替一下进行试验.大致流程就是手机摄像头获取视频,开启一个IP摄像头服务软件,在局域 ...
- 【opencv】VideoCapture打不开本地视频文件或者网络IP摄像头
1.前提:成功打开本地USB摄像头 // 创建VideoCapture对象 VideoCapture vc = new VideoCapture(); // 可以成功打开本地USB摄像头 // 参数可 ...
- tcp/ip http socket笔记
1.TCP/IP协议是传输层协议,主要解决数据如何在网络中传输 HTTP是应用层协议,主要解决如何包装数据 2.TCP连接的三次握手 第一次握手:客户端发送syn包到服务器,并进入SYN_SEND状态 ...
- TCP/IP协议学习笔记
计算机网络基础知识复习汇总:计算机网络基础知识复习 HTTP协议的解析:剖析 HTTP 协议 一个系列的解析文章: TCP/IP详解学习笔记(1)-- 概述 TCP/IP详解学习笔记(2)-- 数据链 ...
- 《图解tcp/ip》读书笔记(一)
我先讲三句话: 一."万物互联的时代到了."我们生活在这样一个互联网急速发展的时代,也许很快就会发现,你能接触到的一切都可以连接到互联网了,电脑.手机这 ...
- TCP/IP协议精华笔记
1.简介 TCP/IP协议并非单指TCP协议.IP协议,它是一组包括TCP协议和IP协议,UDP(User Datagram Protocol)协议.ICMP(Internet Control Mes ...
随机推荐
- 鲲鹏性能优化十板斧(二)——CPU与内存子系统性能调优
1.1 CPU与内存子系统性能调优简介 调优思路 性能优化的思路如下: l 如果CPU的利用率不高,说明资源没有充分利用,可以通过工具(如strace)查看应用程序阻塞在哪里,一般为磁盘,网络或应 ...
- YUM平台的搭建
网络安全学习内容 三.挂载yum仓库 3.1连接映像文件 步骤如下: 1.右击映像文件,单击设置,选择CentOS映像文件 2.右击映像文件,单击连接 3.2挂载本地yum 打开终端,输入vim /e ...
- 基于VMware Workstation下CentOS的搭建
网络安全学习内容 二.VMware安装CentOS系统 需要准备的文件: 从http://mirrors.huaweicloud.com/centos/7.7.1908/isos/x86_64/中下载 ...
- 【Java库】如何使用优秀的加密库Jasypt来保护你的敏感信息?
1 简介 今天我们介绍一个Java库-Jasypt,全称为Java Simplified Encryption,用于加密解密.它能够让开发者用花费最小的工作而把加密集成到项目中,并且不需要对加密/解密 ...
- 利用Mitmproxy抓包
http://mrpeak.cn/blog/mitmproxy/ 和Charles同样强大的免费抓包软件. 它是在终端操作的,界面没有Charles那么可视化.不过也很好用,通过各种快捷键操作,效 ...
- jQuery操作元素对象的样式
在jQuery中操作元素为了加快速度,或者书写速度,可以用到json的格式: <!DOCTYPE html> <html> <head> <meta char ...
- HDU2242 考研路茫茫——空调教室 (双联通分+树形DP)
考研路茫茫——空调教室 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 2019 湖南多校第一场(2018~2019NCPC) 题解
解题过程 开场shl过B,C,然后lfw写J,J WA了以后shl写A,但是因为OJ上空间开小WA了,而不是MLE?,J加了特判过了.之后一直在检查A错哪了,直到qt发现问题改了空间,浪费许多时间,但 ...
- IoTClient开发5 - ModBusRtu协议
前言 前面我们介绍了ModBusTcp协议.今天我们接着来介绍ModBusRtu协议.和ModBusTcp不同的是ModBusRtu基于串口通信,ModBusTcp是基于Tcp以太网通信. 所以我们在 ...
- python爬虫--selenium模块.上来自己动!
selenium 基本操作 from selenium import webdriver from time import sleep #实例化一个浏览器对象 bro = webdriver.Chro ...