C# PInvoke 方式呼叫 C/C++ 庫 簡單例子
阿新 • • 發佈:2020-09-15
本文意在給出一個可用的 C# 呼叫 C/C++ lib 的例項。
C/C++ lib
C/C++ header
#ifndef DPS_H #define DPS_H #ifdef __cplusplus extern "C" { #endif #if (defined _WIN32) || (defined _WINDOWS) || (defined WIN32) #ifdef MM_EXPORTS #define MMAPIEXP __declspec(dllexport) #else #define MMAPIEXP __declspec(dllimport) #endif #define MMAPI __cdecl #else #define MMAPIEXP #define MMAPI #endif // 定義一個回撥 typedef int(MMAPI *MMCallBack)(int handle, char* UserData); // 一個結構體 typedef struct { int a; int b; char c; char* d; }DataInfo; // API 函式指標組成的結構體 typedef struct { int (MMAPI* func01)(int a, int b); void (MMAPI* func02)(int c, DataInfo* d, MMCallBack cb); }MMAPIINFO; // 定義一個獲取 API的函式,所有 API 通過該函式獲取,注意,MMAPIEXP 用於指定WIN下匯出函式名稱 MMAPIEXP void MMAPI GetAPI(MMAPIINFO* api); #ifdef __cplusplus } #endif #endif
CPP 實現
#include <cstddef> #include <cstring> #include "dps.h" // func01 函式實現 int func01(int a, int b){ return a + b; } // func01 函式實現 void func02(int c, DataInfo* d, MMCallBack cb){ cb(c,d->d); } // 定義獲取 API 的函式 void GetAPI(MMAPIINFO* api){ if (api == NULL) return; memset(api, 0x00, sizeof(MMAPIINFO)); api->func01 = func01; api->func02 = func02; }
上述檔案編譯成為dll/lib. 對於C#而言,要呼叫的是dll. 上述檔案可以生成為 dps.dll.
注:名稱無實際意義, 該程式碼自說明。
C# 呼叫
using System; using System.Runtime.InteropServices; namespace loadLib { class Program { static void Main(string[] args) { // 例項化一個 GetApi 類 GetApi ss = new GetApi(); Console.WriteLine(ss.Ffunc01(1,2)); GetApi.DataInfo data = new GetApi.DataInfo { a = 1, b = 2, c = 'x' }; string str1 = "adbcd"; data.d = str1; ss.Ffunc02(1024,ref data); } } class GetApi { // 特性定義了 MMAPIINFO 函式指標結構體的序列化方式和字符集 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct MMAPIINFO { public IntPtr func01; public IntPtr func02; } // 特性定義了 DataInfo 結構體的序列化方式和字符集 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DataInfo { public int a; public int b; public char c; // char* 要使用 MarshalAs [MarshalAs(UnmanagedType.LPStr)] public string d; } // 委託函式MMCB的定義用於傳入回撥函式,對應於 dps.h 中的 // int(MMAPI *MMCallBack)(int handle, char* UserData) // 特別注意:C# 回撥函式的呼叫約定必須宣告為 CallingConvention.Cdecl // 因為 C# 的呼叫約定是 __stdcall, 如果不指定呼叫約定,會預設 __stdcall, // 不同的呼叫約定會導致衝突 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void MMCB(int handle, string userData); // 定義 MMAPIINFO.func01 的委託函式 public delegate int Func01( int a, int b); // 定義 MMAPIINFO.func02 的委託函式 public delegate void Func02(int c, ref DataInfo data, MMCB cbMmcb); // 封裝函式,封裝呼叫 MMAPIINFO.func01 public int Ffunc01(int aa, int bb) { Func01 func = (Func01) Marshal.GetDelegateForFunctionPointer(A.func01, typeof(Func01)); return func(aa, bb); } // 封裝函式,封裝呼叫 MMAPIINFO.func02 public void Ffunc02(int c, ref DataInfo dat) { Func02 func = (Func02) Marshal.GetDelegateForFunctionPointer(A.func02, typeof(Func02)); func(c, ref dat, apiCB); } // 成員變數 MMAPIINFO A 作為指標結構體 public static MMAPIINFO A = new MMAPIINFO(); // 宣告 dsp.dll 的匯出函式,注意呼叫約定 __cdecl 和字符集 [DllImport("dps.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public static extern void GetAPI(ref MMAPIINFO param); // 建構函式用於從 dsp.dll 中獲取api函式結構體 public GetApi() { GetAPI(ref A); } // 實際傳入的回撥(被委託)函式 public void apiCB(int handle, string userData) { Console.WriteLine(handle); Console.WriteLine(userData); } } }