C#調用C/C++ DLL方式總結

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上臨時寫的,未經過編譯器檢查,有問題的地方歡迎指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章