我愛我的家園!

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

           

          C#動態調用C++編寫的DLL函數

          動態加載 DLL 需要使用 Windows API 函數: LoadLibrary GetProcAddress 以及 FreeLibrary 。我們可以使用 DllImport C# 中使用這三個函數。
          ?
          [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++ 中動態調用 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)
          {
          ??? FreeLibrary(hInst);
          ??? return false;
          }
          ?
          4 、調用 foo 函數:
          BOOL bRet = foo(object,(LPVOID)NULL);
          ?
          5 、使用完后應釋放 DLL
          FreeLibrary(hInst);
          ?
          那么在 C# 中應該怎么做呢?方法基本上一樣,我們使用委托來代替 C++ 的函數指針,通過 .NET Framework 2.0 新增的函數 GetDelegateForFunctionPointer 來得到一個委托的實例:
          ?
          下面封裝了一個類,通過該類我們就可以在 C# 中動態調用 Dll 中的函數了:
          ?
          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>
          ??? /// 通過非托管函數名轉換為對應的委托 , by jingzhongrong
          ??? ///</summary>
          ??? ///<param name="dllModule"> 通過 LoadLibrary 獲得的 DLL 句柄 </param>
          ??? ///<param name="functionName"> 非托管函數名 </param>
          ??? ///<param name="t"> 對應的委托類型 </param>
          ??? ///<returns> 委托實例,可強制轉換為適當的委托類型 </returns>
          ??? publicstaticDelegateGetFunctionAddress(intdllModule, stringfunctionName, Typet)
          ??? {
          ?????? intaddress = GetProcAddress(dllModule, functionName);
          ?????? if (address == 0)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
          ??? }
          ?
          ??? ///<summary>
          ??? /// 將表示函數地址的 IntPtr 實例轉換成對應的委托 , by jingzhongrong
          ??? ///</summary>
          ??? publicstaticDelegateGetDelegateFromIntPtr(IntPtraddress, Typet)
          ??? {
          ?????? if (address == IntPtr.Zero)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(address, t);
          ??? }
          ?
          ??? ///<summary>
          ??? /// 將表示函數地址的 int 轉換成對應的委托,by jingzhongrong
          ??? ///</summary>
          ??? publicstaticDelegateGetDelegateFromIntPtr(intaddress, Typet)
          ??? {
          ?????? if (address == 0)
          ?????????? returnnull;
          ?????? else
          ?????????? returnMarshal.GetDelegateForFunctionPointer(newIntPtr(address), t);
          ??? }
          }
          ?
          通過這個類,我們這樣調用 DLL
          ?
          1 、聲明相應的委托(正確聲明很重要,否則不能調用成功,后面有詳細介紹)。
          ?
          2 、加載 DLL
          int hModule = DLLWrapper.LoadLibrary(dllFilePath);
          if (hModule == 0)
          ??? returnfalse;
          ?
          3 、獲取相應的委托實例:
          FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
          if (foo == null)
          {
          ??? DLLWrapper.FreeLibrary(hModule);
          ??? returnfalse;
          }
          ?
          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)]
          ??? structStructVersionInfo
          ??? {
          ?????? publicintMajorVersion;
          ?????? publicintMinorVersion;
          ??? }
          另外,如果單獨使用內部類型沒有另外使用到字符串、結構、類,可以將結構在 C# 中聲明為 class
          ??? [StructLayout(LayoutKind.Sequential)]
          ??? classStructVersionInfo
          ??? {
          ?????? publicintMajorVersion;
          ?????? publicintMinorVersion;
          ??? }
          ?
          對應 C++ 中的聲明:
          ??? typedef struct _VERSION_INFO
          ??? {
          ??? ??? intMajorVersion;
          ??? ??? intMinorVersion;
          ??? } VERSION_INFO, *PVERSION_INFO;
          ?
          如果結構中使用到了字符串,最好應指定相應的字符集:
          ??? [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
          ?
          部分常用的聲明對應關系(在結構中):
          C++ :字符串數組
          ??? wchar_t Comments[120];
          C#
          ??? [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
          ??? publicstringComments;
          ?
          C++ :結構成員
          ??? VERSION_INFO ver;
          C#
          ??? public StructVersionInfo ver;
          ?
          C++ :函數指針聲明
          ??? PFOO pFoo;?// 具體聲明見文章前面部分
          C#:
          ??? public IntPtr pFoo;? // 也可以為 public int pFoo;
          ??????? // 不同的聲明方法可以使用上面 DLLWrapper 類的相應函數獲取對應的委托實例
          ?
          如果在結構中使用到了 union ,那么可以使用 FieldOffset 指定具體位置。
          ?
          3 、委托的聲明:
          ?
          C++ 編寫的 DLL 函數需要通過指針傳出將一個結構:如以下聲明:
          ??? void getVersionInfo( VERSION_INFO *ver);
          對于在 C# 中聲明為 class 的結構(當 VERSION_INFO 聲明為 class
          ??? delegate void getVersionInfo ( VERSION_INFO ver);
          如果結構聲明為 struct ,那么應該使用如下聲明:
          ??? delegate void getVersionInfo ( ref VERSION_INFO ver);
          注意:應該使用 ref 關鍵字。
          ?
          ?
          如果 DLL 函數需要傳入一個字符串,比如這樣:
          ??? BOOL __stdcall jingzhongrong1(constwchar_t* lpFileName, int* FileNum);
          那么使用委托來調用函數的時候應該在 C# 中如下聲明委托:
          ??? delegatebooljingzhongrong1(
          ?????? [MarshalAs(UnmanagedType.LPWStr)]StringFileName,
          ?????? refintFileNum);
          注意:應該使用 [MarshalAs(UnmanagedType.LPWStr)] String 進行聲明。
          ?
          ?
          如果要在 DLL 函數中傳出一個字符串,比如這樣:
          ??? void __stdcall jingzhongrong2(
          ??? wchar_t* lpFileName, // 要傳出的字符串
          ??? int*?Length);
          那么我們如下聲明委托:
          ??? // 使用委托從非托管函數的參數中傳出的字符串,
          ??? // 應該這樣聲明,并在調用前為 StringBuilder 預備足夠的空間
          ??? delegatevoidjingzhongrong2(
          ?????? [MarshalAs(UnmanagedType.LPWStr)] StringBuilderlpFileName,
          ?????? refintLength,
          ??? );
          在使用函數前,應先為 StringBuilder 聲明足夠的空間用于存放字符串:
          ??? StringBuilder fileName = newStringBuilder(FileNameLength);

          posted on 2008-09-02 11:56 死神 閱讀(1960) 評論(1)  編輯  收藏 所屬分類: C#學習

          評論

          # re: C#動態調用C++編寫的DLL函數 2014-02-19 10:57 xyj

          謝謝   回復  更多評論   

          導航

          統計

          公告

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

          常用鏈接

          留言簿(3)

          隨筆分類(5)

          隨筆檔案(9)

          文章分類(37)

          文章檔案(41)

          相冊

          語音技術

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 威信县| 周口市| 丹棱县| 翁源县| 鄂托克前旗| 株洲县| 贡觉县| 九龙县| 赫章县| 玛曲县| 清镇市| 电白县| 锡林郭勒盟| 修武县| 余江县| 错那县| 谷城县| 牙克石市| 宜宾市| 磐石市| 栖霞市| 特克斯县| 平和县| 临洮县| 白山市| 望奎县| 沾益县| 宾川县| 墨江| 棋牌| 陇川县| 武功县| 九龙坡区| 石嘴山市| 兴海县| 马山县| 卢湾区| 孟村| 黎平县| 晋江市| 克拉玛依市|