我愛我的家園!

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

           

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

          動態(tài)加載 DLL 需要使用 Windows API 函數(shù): LoadLibrary GetProcAddress 以及 FreeLibrary 。我們可以使用 DllImport C# 中使用這三個函數(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);
          ?
          當我們在 C++ 中動態(tài)調(diào)用 Dll 中的函數(shù)時,我們一般的方法是:
          假設(shè) DLL 中有一個導(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 來得到一個委托的實例:
          ?
          下面封裝了一個類,通過該類我們就可以在 C# 中動態(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)換為對應(yīng)的委托 , by jingzhongrong
          ??? ///</summary>
          ??? ///<param name="dllModule"> 通過 LoadLibrary 獲得的 DLL 句柄 </param>
          ??? ///<param name="functionName"> 非托管函數(shù)名 </param>
          ??? ///<param name="t"> 對應(yīng)的委托類型 </param>
          ??? ///<returns> 委托實例,可強制轉(zhuǎn)換為適當?shù)奈蓄愋?/span> </returns>
          ??? publicstaticDelegateGetFunctionAddress(intdllModule, stringfunctionName, Typet)
          ??? {
          ?????? intaddress = GetProcAddress(dllModule, functionName);
          ?????? if (address == 0)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
          ??? }
          ?
          ??? ///<summary>
          ??? /// 將表示函數(shù)地址的 IntPtr 實例轉(zhuǎn)換成對應(yīng)的委托 , by jingzhongrong
          ??? ///</summary>
          ??? publicstaticDelegateGetDelegateFromIntPtr(IntPtraddress, Typet)
          ??? {
          ?????? if (address == IntPtr.Zero)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(address, t);
          ??? }
          ?
          ??? ///<summary>
          ??? /// 將表示函數(shù)地址的 int 轉(zhuǎn)換成對應(yīng)的委托,by jingzhongrong
          ??? ///</summary>
          ??? publicstaticDelegateGetDelegateFromIntPtr(intaddress, Typet)
          ??? {
          ?????? if (address == 0)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
          ??? }
          }
          ?
          通過這個類,我們這樣調(diào)用 DLL
          ?
          1 、聲明相應(yīng)的委托(正確聲明很重要,否則不能調(diào)用成功,后面有詳細介紹)。
          ?
          2 、加載 DLL
          int hModule = DLLWrapper.LoadLibrary(dllFilePath);
          if (hModule == 0)
          ??? returnfalse;
          ?
          3 、獲取相應(yīng)的委托實例:
          FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
          if (foo == null)
          {
          ??? DLLWrapper.FreeLibrary(hModule);
          ??? returnfalse;
          }
          ?
          4 、調(diào)用函數(shù):
          foo(...);
          ?
          5 .NET 并不能自動釋放動態(tài)加載的 DLL ,因此我們在使用完 DLL 后應(yīng)該自己釋放 DLL
          DLLWrapper .FreeLibrary(hModule);
          ?
          下面我們將就委托應(yīng)如何聲明進行相應(yīng)的討論,在實際操作過程中,我發(fā)現(xiàn)使用 DllImport 方法和動態(tài)調(diào)用方法兩者在 C# 中對 DLL 中函數(shù)原型的聲明是有些區(qū)別的,下面我介紹動態(tài)調(diào)用中委托的聲明:
          ?
          1 、首先應(yīng)該注意的是, C++ 中的類型和 C# 中類型的對應(yīng)關(guān)系,比如 C++ 中的 long 應(yīng)該對應(yīng) C# 中的 Int32 而不是 long ,否則將導(dǎo)致調(diào)用結(jié)果出錯。
          ?
          2 、結(jié)構(gòu)的聲明使用 StructLayout對結(jié)構(gòu)的相應(yīng)布局進行設(shè)置,具體的請查看 MSDN:
          ?
          使用 LayoutKind 指定結(jié)構(gòu)中成員的布局順序,一般可以使用 Sequential
          ??? [StructLayout(LayoutKind.Sequential)]
          ??? structStructVersionInfo
          ??? {
          ?????? publicintMajorVersion;
          ?????? publicintMinorVersion;
          ??? }
          另外,如果單獨使用內(nèi)部類型沒有另外使用到字符串、結(jié)構(gòu)、類,可以將結(jié)構(gòu)在 C# 中聲明為 class
          ??? [StructLayout(LayoutKind.Sequential)]
          ??? classStructVersionInfo
          ??? {
          ?????? publicintMajorVersion;
          ?????? publicintMinorVersion;
          ??? }
          ?
          對應(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)]
          ?
          部分常用的聲明對應(yīng)關(guān)系(在結(jié)構(gòu)中):
          C++ :字符串數(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ù)獲取對應(yīng)的委托實例
          ?
          如果在結(jié)構(gòu)中使用到了 union ,那么可以使用 FieldOffset 指定具體位置。
          ?
          3 、委托的聲明:
          ?
          C++ 編寫的 DLL 函數(shù)需要通過指針傳出將一個結(jié)構(gòu):如以下聲明:
          ??? void getVersionInfo( VERSION_INFO *ver);
          對于在 C# 中聲明為 class 的結(jié)構(gòu)(當 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ù)需要傳入一個字符串,比如這樣:
          ??? BOOL __stdcall jingzhongrong1(constwchar_t* lpFileName, int* FileNum);
          那么使用委托來調(diào)用函數(shù)的時候應(yīng)該在 C# 中如下聲明委托:
          ??? delegatebooljingzhongrong1(
          ?????? [MarshalAs(UnmanagedType.LPWStr)]StringFileName,
          ?????? refintFileNum);
          注意:應(yīng)該使用 [MarshalAs(UnmanagedType.LPWStr)] String 進行聲明。
          ?
          ?
          如果要在 DLL 函數(shù)中傳出一個字符串,比如這樣:
          ??? 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 死神 閱讀(1960) 評論(1)  編輯  收藏 所屬分類: C#學(xué)習(xí)

          評論

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

          謝謝   回復(fù)  更多評論   

          導(dǎo)航

          統(tǒng)計

          公告

          歡迎大家來到我的個人世界!

          常用鏈接

          留言簿(3)

          隨筆分類(5)

          隨筆檔案(9)

          文章分類(37)

          文章檔案(41)

          相冊

          語音技術(shù)

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 康定县| 灵山县| 敖汉旗| 信丰县| 永仁县| 盖州市| 长丰县| 宜城市| 营山县| 田阳县| 翁源县| 建德市| 荣成市| 游戏| 疏附县| 乐至县| 新干县| 巴楚县| 揭西县| 乐东| 尉氏县| 桐庐县| 嘉鱼县| 贵南县| 龙海市| 丹江口市| 勐海县| 安泽县| 阿鲁科尔沁旗| 博湖县| 临猗县| 徐汇区| 永州市| 怀集县| 郓城县| 温州市| 忻州市| 元谋县| 邹城市| 仪征市| 略阳县|