01??
將C++ SDK 集成到 IDE 中
以下是在?Microsoft Visual Studio?平臺下?SDK?的集成。
2.1 Visual Studio 平臺下 C/C++環境配置及集成到 IDE 中
xxx.lib 和 xxx.dll 適合在 Windows 操作系統平臺使用,這里以 VS2022 環境為例。
2.1.1 C/C++ 工程環境配置與集成
1、C# SDK?接口文件包含
a、將 a.h,b.h,c.h,
a.cpp?拷貝到用戶指定路徑下。
b、點擊項目?-?屬性?- C/C++ -常規。在附加包含目錄下指定頭文件的路徑。
2、添加庫引用和庫的路徑
a?、 將 提 供 的?a_sdk.dll?,?b.dll?拷 貝 到 程 序 運 行 的 目 錄 下 ; 將
a.lib?拷貝到用戶指定路徑下。
b、點擊項目?-?屬性?-?鏈接器?-?常規。在附加庫目錄中指定?a.lib?的路
徑。
02??
2.1.2 在用戶 C/C++ 項目中使用 SDK
以下為獲取數據的簡單步驟,更詳細的使用方法可以參考提供的?Demo?程序。
添加頭文件,添加庫以后,將dll放在可執行目錄,即可調用外部SDK動態庫使用。
03??
2.2 Visual Studio 平臺下 C#環境配置及集成到 IDE 中
CSharp_Lib 適合在 Windows 操作系統平臺使用,這里以 VS2022 環境為例。
2.2.1 C# 工程環境配置與集成
1、工程文件包含
將?CSharp_Lib?文件夾拷貝到工程目錄下,并把?dll?文件夾相對應?x64?和?x86
的?aa.dll?和bb_sdk.dll?拷貝到工程目錄的運行目錄下。
注意:將C++ DLL給C#上位機使用,有很多語法需要轉換,這個我們會詳細介紹。
04??
C#為例
將C++ 的動態庫依賴的頭文件和源文件轉為.cs格式。
C#需要的模塊
using System;
:引用了System
命名空間,這是C#中最基礎的命名空間,包含了基本的類和功能,如Console
類、Math
類等。using System.Collections.Generic;
:引用了System.Collections.Generic
命名空間,這個命名空間包含了泛型集合類,如List<T>
、Dictionary<TKey, TValue>
等,這些類在處理數據集合時非常有用。using System.Linq;
:引用了System.Linq
命名空間,這個命名空間包含了LINQ(Language Integrated Query)相關的類和功能,LINQ允許在C#中編寫查詢語句來操作數據集合。using System.Text;
:引用了System.Text
命名空間,這個命名空間包含了處理字符串和文本的類,如StringBuilder
類。using System.Threading.Tasks;
:引用了System.Threading.Tasks
命名空間,這個命名空間包含了異步編程相關的類和功能,如Task
類和async
/await
關鍵字。
代碼格式
namespace?DEVICE
{
? ? classParameterDefine
? ? {
? ? ? ??//代碼實現?
? ? ? ??public?const?string?a =?"123";
? ? }
} ? ?
枚舉格式
?public?enum?StatusTypeDef
?{
? ? RED;
?}
使用C++動態庫的頭文件的結構體的中含有字符串,需要進行轉化。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
?這是一個特性,用于指定結構體在內存中的布局方式以及如何處理字符串數據。
LayoutKind.Sequential
?表示結構體中的元素在內存中按它們在代碼中定義的順序進行布局,不會被打亂。這對于與非托管代碼(如C++編寫的DLL)進行交互非常重要,以確保數據在內存中的排列方式匹配。
CharSet = CharSet.Ansi
?指定了結構體中的字符串使用ANSI字符集。ANSI字符集是一種單字節字符集,通常用于支持英語和其他西歐語言。這對于與需要ANSI字符串的非托管代碼進行交互是有必要的。如果需要支持Unicode字符集,則應使用?
CharSet = CharSet.Unicode
。這里使用ANSI是為了確保與SDK中的函數參數類型匹配。
使用C++動態庫的頭文件的字符數組,需要進行轉化。如char a[32];
C++ 字符數組轉為字符串
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
:[MarshalAs(...)]
: 這是一個C#中的特性(attribute),用于指定字段在封送到非托管代碼時應如何處理。它屬于
System.Runtime.InteropServices
命名空間,因此在使用之前需要引入該命名空間。UnmanagedType.ByValTStr
: 這個值指定字段應該被封送為固定長度的ANSI字符數組。ANSI字符通常是單字節字符,適合處理不包含Unicode字符的數據。
SizeConst = 32
: 這個值指定了字段在非托管代碼中的大小(以字符為單位)。在這里,
SizeConst = 32
表示字符數組的長度為32個字符(包括結束符’\0’)。
?[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
?//設備IP地址和端口號
?public?struct?DeviceNetPara_t
?{
? ? ?[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
? ? ?public?string?ip_addr;
? ? ?public?UInt32 port;
?}
C++ char*轉為字符數組
特性(Attributes): 特性是用于向程序中的類型、方法或屬性添加元數據的方式。C#中的特性可以用于編譯時檢查以及運行時的行為修改。
StructLayout: 這是一個特性,用于指定結構體在內存中的布局方式。它有幾個參數可以配置,這里主要用到了
LayoutKind.Sequential
、CharSet
和Pack
。LayoutKind.Sequential: 這個參數指定結構體中的字段按照它們在代碼中聲明的順序存儲在內存中。這與C/C++中的結構體布局方式一致。
CharSet = CharSet.Ansi: 這個參數用于指定結構體中字符串字段的字符編碼方式。
CharSet.Ansi
表示使用ANSI字符集來存儲字符串。這意味著每個字符占用一個字節,適用于ASCII字符集。Pack = 4: 這個參數用于指定結構體字段的對齊方式。
Pack = 4
表示結構體中的字段按照4字節對齊。這意味著每個字段的起始地址必須是4字節的倍數。這通常用于優化性能,特別是在與非托管代碼(如C/C++庫)交互時。
?[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
?//數據
?public?struct?Info_t
?{
??
? ? ?[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
? ? ?public?char[] ip;
?}
C++ void* 轉換IntPtr
- 特性聲明?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
:
StructLayout
?是一個C#特性,用于定義結構體在內存中的布局方式。在C#中,結構體默認使用?
LayoutKind.Auto
,這意味著編譯器可以根據需要重新排列結構體中的字段以優化內存使用。LayoutKind.Sequential
?選項強制編譯器按結構體中字段定義的順序在內存中布局這些字段。這對于與非托管代碼(如C/C++代碼)進行交互非常重要,因為你需要確保結構體的布局與非托管代碼期望的布局一致。
CharSet = CharSet.Auto
?選項指定了非托管字符串的類型。
CharSet.Auto
?在Windows平臺上通常被解釋為?CharSet.Unicode
,但在其他平臺上則可能是?CharSet.Ansi
。這確保了字符串在與非托管代碼交互時的正確編碼。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
//異步事件參數
public?struct?Args_t
{
? ??public?IntPtr data; ? ? ? ? ? ? ? ??//數據
}
C++ 回調函數指針轉為C#
特性(Attribute):
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
:這是一個特性,用于修飾一個委托(delegate),告訴編譯器這個委托指向的函數是未托管代碼中的函數,并且使用哪種調用約定(Calling Convention)來調用這些函數。
UnmanagedFunctionPointer 特性:
UnmanagedFunctionPointer
:這是一個.NET 特性,用于定義一個委托,該委托指向的函數存在于非托管代碼中(例如C/C++編寫的DLL)。通過這個特性,可以確保.NET 應用程序能夠正確地調用這些非托管函數。
CallingConvention.Cdecl:
CallingConvention.Cdecl
:這是調用約定的一種類型。在這種約定下,函數的調用者負責清理堆棧。這通常用于C語言的函數調用,因此在調用C/C++編寫的函數時,使用這種約定是比較常見的。Cdecl約定通常提供更好的性能,因為它讓調用者(而不是被調函數)負責清理堆棧,從而避免了額外的堆棧操作。
? ??// 事件委托類型
? ? [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
? ??// public delegate void DBL_UserEventCallbackHandleDelegate: 這行代碼定義了一個名為DBL_UserEventCallbackHandleDelegate的委托。
? ??public?delegate?void?UserEventCallbackHandleDelegate(int?id, Args_t arg, IntPtr userThis);
05??
C++ 動態庫DLL轉為C#
- DLL導入
: 通過
DllImport
屬性,你可以在C#中調用a_sdk.dll
中的函數。這意味著你的程序可以利用這個DLL中的功能,例如硬件控制、數據采集等。 - 字符集
:?
CharSet.Auto
允許程序根據運行時環境自動選擇字符集,這在處理不同平臺上的字符串時非常有用。 - 調用約定
:?
CallingConvention.Cdecl
確保了函數調用的參數傳遞和清理方式符合C語言的規范,這對于確保你的C#代碼能夠正確地與非托管代碼交互非常重要。
// 外部庫,記得的放在可執行文件目錄下
[DllImport("a_sdk.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
// 外部庫dll中的函數
extern?static?int?ScanDeviceList(ref?IntPtr deviceList,?ref?UInt32 deviceNumber);
ref如同C++的& 代表引用。IntPtr如同C++的*
06??
字節數組轉為結構體
// 返回類型:object 表示該方法返回一個類型為 object 的對象。由于 object 是所有類型的基類,返回的值可以是任何類型。
// Type type:表示目標結構體的類型
public?static?object?BytesToStuct(byte[] bytes,?int?pos, Type type)
{
? ??//得到結構體的大小
? ??int?size = Marshal.SizeOf(type);
? ??//byte數組長度小于結構體的大小
? ??if?(size > bytes.Length + pos)
? ? {
? ? ? ??//返回空
? ? ? ??return?null;
? ? }
? ??//分配結構體大小的內存空間
? ? IntPtr structPtr = Marshal.AllocHGlobal(size);
? ??//將byte數組拷到分配好的內存空間
? ? Marshal.Copy(bytes, pos, structPtr, size);
? ??//將內存空間轉換為目標結構體
? ??object?obj = Marshal.PtrToStructure(structPtr, type);
? ??//釋放內存空間
? ? Marshal.FreeHGlobal(structPtr);
? ??//返回結構體
? ??return?obj;
}
其實以目前的AI來看,只要掌握其中一門語言,轉化一下就好。