動態載入dll需要使用windows api函式:loadlibrary、getprocaddress以及freelibrary。我們可以使用dllimport在c#中使用這三個函式。
[dllimport("kernel32")]
public static extern int getprocaddress(int handle, string funcname);
[dllimport("kernel32")]
public static extern int loadlibrary(string funcname);
[dllimport("kernel32")]
public static extern int freelibrary(int handle);
當我們在c++中動態呼叫dll中的函式時,我們一般的方法是:
假設dll中有乙個匯出函式,函式原型如下:
bool __stdcall foo(object &object, lpvoid lpreserved);
1、首先定義相應的函式指標:
typedef bool (__stdcall *pfoo)(object &object, lpvoid lpreserved);
2、呼叫loadlibrary載入dll:
hinstance hinst = ::loadlibraryw(dllfilename);
3、呼叫getprocaddress函式獲取要呼叫函式的位址:
pfoo foo = (pfoo)getprocaddress(hinst,"foo");
if(foo == null)
4、呼叫foo函式:
bool bret = foo(object,(lpvoid)null);
5、使用完後應釋放dll:
freelibrary(hinst);
那麼在c#中應該怎麼做呢?方法基本上一樣,我們使用委託來代替c++的函式指標,通過.net framework 2.0新增的函式getdelegateforfunctionpointer來得到乙個委託的例項:
下面封裝了乙個類,通過該類我們就可以在c#中動態呼叫dll中的函式了:
public class dllwrapper
///
///將表示函式位址的intptr例項轉換成對應的委託, by jingzhongrong
///
public static delegate getdelegatefromintptr(intptr address, type t)
///
///將表示函式位址的int轉換成對應的委託,by jingzhongrong
///
public static delegate getdelegatefromintptr(int address, type t) }
通過這個類,我們這樣呼叫dll:
1、宣告相應的委託(正確宣告很重要,否則不能呼叫成功,後面有詳細介紹)。
2、載入dll:
int hmodule = dllwrapper.loadlibrary(dllfilepath);
if (hmodule == 0)
return false;
3、獲取相應的委託例項:
foo foo = (foo)dllwrapper.getfunctionaddress(hmodule, "foo", typeof(foo));
if (foo == null)
4、呼叫函式:
foo(...);
5、.net並不能自動釋放動態載入的dll,因此我們在使用完dll後應該自己釋放dll:
dllwrapper.freelibrary(hmodule);
下面我們將就委託應如何宣告進行相應的討論,在實際操作過程中,我發現使用dllimport方法和動態呼叫方法兩者在c#中對dll中函式原型的宣告是有些區別的,下面我介紹動態呼叫中委託的宣告:
1、首先應該注意的是,c++中的型別和c#中型別的對應關係,比如c++中的long應該對應c#中的int32而不是long,否則將導致呼叫結果出錯。
2、結構的宣告使用structlayout對結構的相應布局進行設定,具體的請檢視msdn:
使用layoutkind指定結構中成員的布局順序,一般可以使用sequential:
[structlayout(layoutkind.sequential)]
struct structversioninfo
另外,如果單獨使用內部型別沒有另外使用到字串、結構、類,可以將結構在c#中宣告為class:
[structlayout(layoutkind.sequential)]
class structversioninfo
對應c++中的宣告:
typedef struct _version_info
version_info, *pversion_info;
如果結構中使用到了字串,最好應指定相應的字符集:
[structlayout(layoutkind.sequential,charset=charset.unicode)]
部分常用的宣告對應關係(在結構中):
c++:字串陣列
wchar_t comments[120];
c#:
[marshalas(unmanagedtype.byvaltstr, sizeconst = 120)]
public string comments;
c++:結構成員
version_info ver;
c# publicstructversioninfo ver;
c++:函式指標宣告
pfoo pfoo; //具體宣告見文章前面部分
c#:
publicintptr pfoo; //也可以為 public int pfoo;
//不同的宣告方法可以使用上面dllwrapper類的相應函式獲取對應的委託例項
如果在結構中使用到了union,那麼可以使用fieldoffset指定具體位置。
3、委託的宣告:
當c++編寫的dll函式需要通過指標傳出將乙個結構:如以下宣告:
void getversioninfo(version_info *ver);
對於在c#中宣告為class的結構(當version_info宣告為class)
delegate voidgetversioninfo(version_info ver);
如果結構宣告為struct,那麼應該使用如下宣告:
delegate voidgetversioninfo(refversion_info ver);
注意:應該使用ref關鍵字。
如果dll函式需要傳入乙個字串,比如這樣:
bool __stdcall jingzhongrong1(const wchar_t* lpfilename, int* filenum);
那麼使用委託來呼叫函式的時候應該在c#中如下宣告委託:
delegate bool jingzhongrong1(
[marshalas(unmanagedtype.lpwstr)]string filename,
ref int filenum);
注意:應該使用[marshalas(unmanagedtype.lpwstr)]和string進行宣告。
如果要在dll函式中傳出乙個字串,比如這樣:
void __stdcall jingzhongrong2(
wchar_t* lpfilename, //要傳出的字串
int* length);
那麼我們如下宣告委託:
//使用委託從非託管函式的引數中傳出的字串,
//應該這樣宣告,並在呼叫前為stringbuilder預備足夠的空間
delegate void jingzhongrong2(
[marshalas(unmanagedtype.lpwstr)] stringbuilder lpfilename,
ref int length,
); 在使用函式前,應先為stringbuilder宣告足夠的空間用於存放字串:
stringbuilder filename = new stringbuilder(filenamelength);
C 動態呼叫C 編寫的DLL函式
c 動態呼叫c 編寫的dll函式 c 動態呼叫c 編寫的dll函式 動態載入 dll 需要使用 windows api 函式 loadlibrary getprocaddress 以及 freelibrary 我們可以使用 dllimport 在 c 中使用這三個函式。dllimport kerne...
C 呼叫C 編寫的dll
介面還是c 寫的方便點,主要是有乙個視覺化的編輯器,不想畫太多的時間在介面上。但是自己又對c 了解的多一些,所以在需要乙個良好的介面的情況下,使用c 來寫 邏輯,將其編譯成乙個dll,然後用c 寫介面,extern c declspec dllexport int testadd int a,int...
C 呼叫C 程式編寫的dll
c 呼叫c 程式編寫的dll 比起 c 呼叫c 程式編寫的dll要方便得多。假定我已經有個cplusplusdll.dll,此dll是用c 寫的,下面的程式是c 呼叫的程式。注意dll要放到c 工程的bin目錄下的debug目錄下。using system using system.collecti...