C#與C/C++ DLL交互方式總結
C#調用C/C++ DLL導出
C/C++的導出函數:
int __stdcall AFunc(wchar_t* str)
{
//ugsdifgoisuhfgiosugdtfuywegouy
return 0;
}
C#聲明:
[DllImport("xxxx.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
internal static extern int AFunc(char[] str);
最前面的爲dll模塊名稱
CharSet聲明char[]字符格式,這裏設置爲Unicode(UTF-16);
CallingConvention = CallingConvention.Winapi 設置函數調用規則
使用方法很簡單:
void func()
{
string str = "HelloWorld";
AFunc(str.ToArray());
}
如果C/C++的形參是多字節,就會麻煩一些:
C/C++:
int __stdcall AFunc(char* str)
{
//ugsdifgoisuhfgiosugdtfuywegouy
return 0;
}
C#聲明:
[DllImport("xxxx.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
internal static extern int AFunc(char[] str);
C#使用:
void func()
{
string str = "HelloWorld";
IntPtr s = Marshal.StringToHGlobalAnsi(str);
AFunc(s);
Marshal.FreeHGlobal(s);
}
回調函數:
使用場景:C#將一個函數扔給C/C++作爲回調函數,當C/C++任務完成後,可以通知C#程序,並返回結果。
C/C++回調函數格式
typedef int (__stdcall *pfnCallback)(int result);
//導出一個函數設置回調函數
void SetCallback(pfnCallback callback);
C#對應聲明:
//這是聲明瞭一個回調類型
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate int pfnCallback(int result);
//定義函數
public int Callback(int result)
{
//balalabalala
}
public void fun1()
{
//使用導出的函數設置回調
pfnCallback pCallback = new pfnCallback(Callback);
SetCallback(pCallback);
}
CallingConvention.StdCall 代表函數調用規則,Windows x86 情況下C/C++默認是cdcel或者stdcall,x64統一fastcall;而C#默認fastcall。
CharSet負責設置回調函數的字符參數;當前示例未使用,加不加都行。
記得要new
結構體:
使用場景:C/C++回調函數傳參,肯定數據不止一個,這種情況會使用結構體。
假設當前有一個結構體OSVERSIONINFOEXW
C/C++聲明:
typedef struct _OSVERSIONINFOEXW {
DWORD dwOSVersionInfoSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformId;
WCHAR szCSDVersion[ 128 ]; // Maintenance string for PSS usage
WORD wServicePackMajor;
WORD wServicePackMinor;
WORD wSuiteMask;
BYTE wProductType;
BYTE wReserved;
} OSVERSIONINFOEXW, *POSVERSIONINFOEXW, *LPOSVERSIONINFOEXW, RTL_OSVERSIONINFOEXW, *PRTL_OSVERSIONINFOEXW;
C#聲明:
[System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OSVERSIONINFOEXW
{
public UInt32 dwOSVersionInfoSize;
public UInt32 dwMajorVersion;
public UInt32 dwMinorVersion;
public UInt32 dwBuildNumber;
public UInt32 dwPlatformId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public char[] szCSDVersion;
public UInt16 wServicePackMajor;
public UInt16 wServicePackMinor;
public UInt16 wSuiteMask;
public Byte wProductType;
public Byte wReserved;
}
LayoutKind.Sequential設置結構體的對其方式。CharSet設置字符類型,當前設置爲Unicode(UTF-16)
在結構體裏面szCSDVersion是字符串,C#的聲明會有一些不同
假設目前需求,C/C++獲取系統信息完成,通過回調函數,將OSVERSIONINFOEXW結構體傳給C#程序。
首先回調函數聲明:
C/C++:
typedef void (__stdcall *pfnCompleteCallback)(OSVERSIONINFOEXW* pOSVersionInfo);
C#:
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void pfnCompleteCallback(IntPtr pOSVersionInfo);
可以看出,C#的形參不是填寫結構體類型,而是使用指針
接下來調用代碼:
C/C++:
void DoSomething()
{
OSVERSIONINFOEXW OSVersionInfo = {0};
/*
*Do some thing
*/
pfnCompleteCallback(&OSVersionInfo);
}
C#:
void func()
{
//一個設置回調的DLL導出函數
SetCallback(CompleteCallback);
}
//這是回調函數內部處理
void CompleteCallback(IntPtr pOSVersionInfo)
{
//動態解碼結構體
OSVERSIONINFOEXW OSVersionInfo = (OSVERSIONINFOEXW)Marshal.PtrToStructure(pOSVersionInfo, typeof(pOSVersionInfo));
//Do other things
}
目前項目遇上的痛點就這幾個,以後遇上了再次更新。
以上代碼在Web上臨時寫的,未經過編譯器檢查,有問題的地方歡迎指正。