我愛我的家園!

          成功在于你是否努力,希望在于你是否相信自己!

           

          C#動(dòng)態(tài)調(diào)用C++編寫的DLL函數(shù)

          動(dòng)態(tài)加載 DLL 需要使用 Windows API 函數(shù): LoadLibrary 、 GetProcAddress 以及 FreeLibrary 。我們可以使用 DllImport C# 中使用這三個(gè)函數(shù)。
          ?
          [DllImport("Kernel32")]
          public static extern int GetProcAddress(inthandle, Stringfuncname);
          ?
          [DllImport("Kernel32")]
          public static extern int LoadLibrary(Stringfuncname);
          ?
          [DllImport("Kernel32")]
          public static extern int FreeLibrary(inthandle);
          ?
          當(dāng)我們?cè)?/span> C++ 中動(dòng)態(tài)調(diào)用 Dll 中的函數(shù)時(shí),我們一般的方法是:
          假設(shè) DLL 中有一個(gè)導(dǎo)出函數(shù),函數(shù)原型如下:
          BOOL __stdcall foo(Object &object, LPVOID lpReserved);
          ?
          1 、首先定義相應(yīng)的函數(shù)指針:
          typedef BOOL (__stdcall *PFOO)(Object &object, LPVOID lpReserved);
          ?
          2 、調(diào)用 LoadLibrary 加載 dll
          HINSTANCE hInst = ::LoadLibraryW(dllFileName);
          ?
          3 、調(diào)用 GetProcAddress 函數(shù)獲取要調(diào)用函數(shù)的地址:
          PFOO foo = (PFOO)GetProcAddress(hInst,"foo");
          if (foo == NULL)
          {
          ??? FreeLibrary(hInst);
          ??? return false;
          }
          ?
          4 、調(diào)用 foo 函數(shù):
          BOOL bRet = foo(object,(LPVOID)NULL);
          ?
          5 、使用完后應(yīng)釋放 DLL
          FreeLibrary(hInst);
          ?
          那么在 C# 中應(yīng)該怎么做呢?方法基本上一樣,我們使用委托來代替 C++ 的函數(shù)指針,通過 .NET Framework 2.0 新增的函數(shù) GetDelegateForFunctionPointer 來得到一個(gè)委托的實(shí)例:
          ?
          下面封裝了一個(gè)類,通過該類我們就可以在 C# 中動(dòng)態(tài)調(diào)用 Dll 中的函數(shù)了:
          ?
          public class DLLWrapper
          {
          ??? ///<summary>
          ??? /// API LoadLibrary
          ??? ///</summary>
          ??? [DllImport("Kernel32")]
          ??? publicstaticexternintLoadLibrary(Stringfuncname);
          ?
          ??? ///<summary>
          ??? /// API GetProcAddress
          ??? ///</summary>
          ??? [DllImport("Kernel32")]
          ??? publicstaticexternintGetProcAddress(inthandle, Stringfuncname);
          ?
          ??? ///<summary>
          ??? /// API FreeLibrary
          ??? ///</summary>
          ??? [DllImport("Kernel32")]
          ??? publicstaticexternintFreeLibrary(inthandle);
          ?
          ??? ///<summary>
          ??? /// 通過非托管函數(shù)名轉(zhuǎn)換為對(duì)應(yīng)的委托 , by jingzhongrong
          ??? ///</summary>
          ??? ///<param name="dllModule"> 通過 LoadLibrary 獲得的 DLL 句柄 </param>
          ??? ///<param name="functionName"> 非托管函數(shù)名 </param>
          ??? ///<param name="t"> 對(duì)應(yīng)的委托類型 </param>
          ??? ///<returns> 委托實(shí)例,可強(qiáng)制轉(zhuǎn)換為適當(dāng)?shù)奈蓄愋?/span> </returns>
          ??? publicstaticDelegateGetFunctionAddress(intdllModule, stringfunctionName, Typet)
          ??? {
          ?????? intaddress = GetProcAddress(dllModule, functionName);
          ?????? if (address == 0)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
          ??? }
          ?
          ??? ///<summary>
          ??? /// 將表示函數(shù)地址的 IntPtr 實(shí)例轉(zhuǎn)換成對(duì)應(yīng)的委托 , by jingzhongrong
          ??? ///</summary>
          ??? publicstaticDelegateGetDelegateFromIntPtr(IntPtraddress, Typet)
          ??? {
          ?????? if (address == IntPtr.Zero)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(address, t);
          ??? }
          ?
          ??? ///<summary>
          ??? /// 將表示函數(shù)地址的 int 轉(zhuǎn)換成對(duì)應(yīng)的委托,by jingzhongrong
          ??? ///</summary>
          ??? publicstaticDelegateGetDelegateFromIntPtr(intaddress, Typet)
          ??? {
          ?????? if (address == 0)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
          ??? }
          }
          ?
          通過這個(gè)類,我們這樣調(diào)用 DLL
          ?
          1 、聲明相應(yīng)的委托(正確聲明很重要,否則不能調(diào)用成功,后面有詳細(xì)介紹)。
          ?
          2 、加載 DLL
          int hModule = DLLWrapper.LoadLibrary(dllFilePath);
          if (hModule == 0)
          ??? returnfalse;
          ?
          3 、獲取相應(yīng)的委托實(shí)例:
          FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
          if (foo == null)
          {
          ??? DLLWrapper.FreeLibrary(hModule);
          ??? returnfalse;
          }
          ?
          4 、調(diào)用函數(shù):
          foo(...);
          ?
          5 、 .NET 并不能自動(dòng)釋放動(dòng)態(tài)加載的 DLL ,因此我們?cè)谑褂猛?/span> DLL 后應(yīng)該自己釋放 DLL
          DLLWrapper .FreeLibrary(hModule);
          ?
          下面我們將就委托應(yīng)如何聲明進(jìn)行相應(yīng)的討論,在實(shí)際操作過程中,我發(fā)現(xiàn)使用 DllImport 方法和動(dòng)態(tài)調(diào)用方法兩者在 C# 中對(duì) DLL 中函數(shù)原型的聲明是有些區(qū)別的,下面我介紹動(dòng)態(tài)調(diào)用中委托的聲明:
          ?
          1 、首先應(yīng)該注意的是, C++ 中的類型和 C# 中類型的對(duì)應(yīng)關(guān)系,比如 C++ 中的 long 應(yīng)該對(duì)應(yīng) C# 中的 Int32 而不是 long ,否則將導(dǎo)致調(diào)用結(jié)果出錯(cuò)。
          ?
          2 、結(jié)構(gòu)的聲明使用 StructLayout對(duì)結(jié)構(gòu)的相應(yīng)布局進(jìn)行設(shè)置,具體的請(qǐng)查看 MSDN:
          ?
          使用 LayoutKind 指定結(jié)構(gòu)中成員的布局順序,一般可以使用 Sequential
          ??? [StructLayout(LayoutKind.Sequential)]
          ??? structStructVersionInfo
          ??? {
          ?????? publicintMajorVersion;
          ?????? publicintMinorVersion;
          ??? }
          另外,如果單獨(dú)使用內(nèi)部類型沒有另外使用到字符串、結(jié)構(gòu)、類,可以將結(jié)構(gòu)在 C# 中聲明為 class
          ??? [StructLayout(LayoutKind.Sequential)]
          ??? classStructVersionInfo
          ??? {
          ?????? publicintMajorVersion;
          ?????? publicintMinorVersion;
          ??? }
          ?
          對(duì)應(yīng) C++ 中的聲明:
          ??? typedef struct _VERSION_INFO
          ??? {
          ??? ??? intMajorVersion;
          ??? ??? intMinorVersion;
          ??? } VERSION_INFO, *PVERSION_INFO;
          ?
          如果結(jié)構(gòu)中使用到了字符串,最好應(yīng)指定相應(yīng)的字符集:
          ??? [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
          ?
          部分常用的聲明對(duì)應(yīng)關(guān)系(在結(jié)構(gòu)中):
          C++ :字符串?dāng)?shù)組
          ??? wchar_t Comments[120];
          C#
          ??? [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
          ??? publicstringComments;
          ?
          C++ :結(jié)構(gòu)成員
          ??? VERSION_INFO ver;
          C#
          ??? public StructVersionInfo ver;
          ?
          C++ :函數(shù)指針聲明
          ??? PFOO pFoo;?// 具體聲明見文章前面部分
          C#:
          ??? public IntPtr pFoo;? // 也可以為 public int pFoo;
          ??????? // 不同的聲明方法可以使用上面 DLLWrapper 類的相應(yīng)函數(shù)獲取對(duì)應(yīng)的委托實(shí)例
          ?
          如果在結(jié)構(gòu)中使用到了 union ,那么可以使用 FieldOffset 指定具體位置。
          ?
          3 、委托的聲明:
          ?
          當(dāng) C++ 編寫的 DLL 函數(shù)需要通過指針傳出將一個(gè)結(jié)構(gòu):如以下聲明:
          ??? void getVersionInfo( VERSION_INFO *ver);
          對(duì)于在 C# 中聲明為 class 的結(jié)構(gòu)(當(dāng) VERSION_INFO 聲明為 class
          ??? delegate void getVersionInfo ( VERSION_INFO ver);
          如果結(jié)構(gòu)聲明為 struct ,那么應(yīng)該使用如下聲明:
          ??? delegate void getVersionInfo ( ref VERSION_INFO ver);
          注意:應(yīng)該使用 ref 關(guān)鍵字。
          ?
          ?
          如果 DLL 函數(shù)需要傳入一個(gè)字符串,比如這樣:
          ??? BOOL __stdcall jingzhongrong1(constwchar_t* lpFileName, int* FileNum);
          那么使用委托來調(diào)用函數(shù)的時(shí)候應(yīng)該在 C# 中如下聲明委托:
          ??? delegatebooljingzhongrong1(
          ?????? [MarshalAs(UnmanagedType.LPWStr)]StringFileName,
          ?????? refintFileNum);
          注意:應(yīng)該使用 [MarshalAs(UnmanagedType.LPWStr)] String 進(jìn)行聲明。
          ?
          ?
          如果要在 DLL 函數(shù)中傳出一個(gè)字符串,比如這樣:
          ??? void __stdcall jingzhongrong2(
          ??? wchar_t* lpFileName, // 要傳出的字符串
          ??? int*?Length);
          那么我們?nèi)缦侣暶魑校?/span>
          ??? // 使用委托從非托管函數(shù)的參數(shù)中傳出的字符串,
          ??? // 應(yīng)該這樣聲明,并在調(diào)用前為 StringBuilder 預(yù)備足夠的空間
          ??? delegatevoidjingzhongrong2(
          ?????? [MarshalAs(UnmanagedType.LPWStr)] StringBuilderlpFileName,
          ?????? refintLength,
          ??? );
          在使用函數(shù)前,應(yīng)先為 StringBuilder 聲明足夠的空間用于存放字符串:
          ??? StringBuilder fileName = newStringBuilder(FileNameLength);

          posted on 2008-09-02 11:56 死神 閱讀(1969) 評(píng)論(1)  編輯  收藏 所屬分類: C#學(xué)習(xí)

          評(píng)論

          # re: C#動(dòng)態(tài)調(diào)用C++編寫的DLL函數(shù) 2014-02-19 10:57 xyj

          謝謝   回復(fù)  更多評(píng)論   

          導(dǎo)航

          統(tǒng)計(jì)

          公告

          歡迎大家來到我的個(gè)人世界!

          常用鏈接

          留言簿(3)

          隨筆分類(5)

          隨筆檔案(9)

          文章分類(37)

          文章檔案(41)

          相冊(cè)

          語音技術(shù)

          最新隨筆

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 沐川县| 静宁县| 普安县| 大英县| 蒙山县| 兴化市| 菏泽市| 河北省| 普兰县| 白山市| 田阳县| 简阳市| 泸西县| 竹北市| 玉环县| 襄城县| 增城市| 四川省| 雷州市| 南召县| 阿拉善盟| 历史| 五原县| 游戏| 仁寿县| 岳西县| 临洮县| 隆安县| 突泉县| 江北区| 哈尔滨市| 清水县| 黄石市| 吐鲁番市| 南康市| 西畴县| 墨脱县| 江华| 交城县| 上杭县| 咸丰县|