C#中調用Dll動態連接庫

C#中調用Dll動態連接庫

起始

受限於語言的不一樣,咱們有的時候可能會用別人提供的函數及方法編程

或者其餘的什麼緣由、反正就是要調!!!api

恰巧別人所使用的的語言跟本身又不是同樣的緩存

這個時候想要調用別人的函數庫就須要借用一些別的東西了app

今天咱們要說的是「UnmanagedExports」編程語言

當前我所要實現的目的只是爲某一QQ機器人編寫插件函數

但我又不喜歡某中文編程語言,編程習慣致使 233333性能

在這裏咱們還可使用進程間UDP通訊來解決這個問題(編寫插件的問題)ui

可是這種方法侷限性比較大,操做起來又略顯繁瑣操作系統

因此今天介紹一下「UnmanagedExports」這個nuget包插件

通過

打開nuget包管理器,爲你所在的項目的安裝上這個包,這裏就不在複述了

以後即可以以相似下面的寫法來調用Dll

首先須要聲明須要調用的函數及其對用的Dll

[DllImport("user32.dll")]//DllImportAttribute
public static extern int MsgBox(int hWnd, String text, String caption, uint type);

這裏告訴編譯器咱們須要調用的Dll名稱及其對應的方法定義

使用「extern」關鍵字來標識這個方法是從外部引用

關於「DllImportAttribute」的屬性會在下面講到

DllImportAttribute詳解

DllImportAttribute是一個重要的角色,其主要做用是給CLR指示哪一個Dll是須要調用的外部庫。

字段 說明
BestFitMapping 啓用或禁用最佳匹配映射。
CallingConvention 指定用於傳遞方法參數的調用約定。 默認值爲 WinAPI,該值對應於基於 32 位 Intel 的平臺的 __stdcall。
CharSet 控制名稱重整以及將字符串參數封送到函數中的方式。 默認值爲 CharSet.Ansi。
EntryPoint 指定要調用的 DLL 入口點。
ExactSpelling 控制是否應修改入口點以對應於字符集。 對於不一樣的編程語言,默認值將有所不一樣。
PreserveSig 控制託管方法簽名是否應轉換成返回 HRESULT 而且返回值有一個附加的 [out, retval] 參數的非託管簽名。默認值爲 true(不該轉換籤名)。
SetLastError 容許調用方使用 Marshal.GetLastWin32Error API 函數來肯定執行該方法時是否發生了錯誤。 在 Visual Basic 中,默認值爲 true;在 C# 和 C++ 中,默認值爲 false。
ThrowOnUnmappableChar 控件引起的異常,將沒法映射的 Unicode 字符轉換成一個 ANSI"?"字符。

除了指出所調用的Dll外,DllImportAttribute還包含了一些可選屬性

其中有如下幾個比較經常使用:

entrypoint

入口點,用於標識函數在Dll中的位置。

你能夠將入口點映射到一個不用的名稱,這實際上就是將被調用的函數重命名。

這裏也說明如下重命名Dll函數的可能緣由

  • 避免使用區分大小寫的API函數名
  • 符合現行的命名標準
  • 提供採用不一樣數據類型的函數(經過聲明同一Dll函數的多個版本)
  • 簡化對包含ANSI和Unicode版本的API的使用
[DllImport("dllname", EntryPoint="MyFunctionname")]
[DllImport("dllname", EntryPoint="#123")]

指定入口點名稱時,您能夠提供一個字符串來指示包含入口點的 DLL 的名稱,或者也能夠按序號來標識入口點。序號以 # 符號爲前綴,如 #1。(序號看不太明白,不用先)

下面來演示一下如何使用Entrypoint字段將咱們本身的函數MessageBoxA映射(替換)爲Dll庫中的MsgBox

[DllImport("user32.dll", EntryPoint="MessageBoxA")]
public static extern int MsgBox(int hWnd, String text, String caption, uint type);

CharSet(部分摘自MSDN)

charset字段控制字符串封送處理並肯定平臺調用在dll查找函數名的方式。

對於採用字符串參數的函數,有些 API 將導出它們的兩個版本:窄版本 (ANSI) 和寬版本 (Unicode)。例如,Win32 API 包含 MessageBox 函數的如下入口點名稱:

  • MessageBoxA

提供單字節字符 ANSI 格式,其特徵是在入口點名稱後附加一個「A」。對 MessageBoxA 的調用始終會以 ANSI 格式封送字符串,它常見於 Windows 95 和 Windows 98 平臺。

  • MessageBoxW

提供雙字節字符 Unicode 格式,其特徵是在入口點名稱後附加一個「W」。對 MessageBoxW 的調用始終會以 Unicode 格式封送字符串,它常見於 Windows NT、Windows 2000 和 Windows XP 平臺。

CharSet 字段接受如下值:

CharSet.Ansi(默認值)

  • 字符串封送處理

平臺調用將字符串從託管格式 (Unicode) 封送爲 ANSI 格式。

  • 名稱匹配

在 ExactSpelling 字段爲 true(它是 Visual Basic 2005 中的默認值)時,平臺調用將只搜索您指定的名稱。例如,若是指定MessageBox,則平臺調用將搜索 MessageBox,若是它找不到徹底相同的拼寫則失敗。

當 ExactSpelling 字段爲 false(它是 C++ 和 C# 中的默認值)時,平臺調用將首先搜索未處理的別名 (MessageBox),若是找不到未處理的別名,則將搜索已處理的名稱 (MessageBoxA)。請注意,ANSI 名稱匹配行爲與 Unicode 名稱匹配行爲不一樣。

CharSet.Unicode

  • 字符串封送處理

平臺調用會將字符串從託管格式 (Unicode) 複製爲 Unicode 格式。

  • 名稱匹配

當 ExactSpelling 字段爲 true(它是 Visual Basic 2005 中的默認值)時,平臺調用將只搜索您指定的名稱。例如,若是指定MessageBox,則平臺調用將搜索 MessageBox,若是它找不到徹底相同的拼寫則失敗。

當 ExactSpelling 字段爲 false(它是 C++ 和 C# 中的默認值)時,平臺調用將首先搜索已處理的名稱 (MessageBoxW),若是找不到已處理的名稱,則將搜索未處理的別名 (MessageBox)。請注意,Unicode 名稱匹配行爲與 ANSI 名稱匹配行爲不一樣。

CharSet.Auto

  • 平臺調用在運行時根據目標平臺在 ANSI 和 Unicode 格式之間進行選擇。( 針對目標操做系統適當地自動封送字符串。在 Windows NT、Windows 2000、Windows XP 和 Windows Server2003 系列上默認值爲 System.Runtime.InteropServices.CharSet.Unicode;在 Windows 98
    和 Windows Me 上默認值爲 System.Runtime.InteropServices.CharSet.Ansi。)

下面的示例演示用於指定字符集的 MessageBox 函數的三個託管定義。在第一個定義中,經過省略,使 CharSet 字段默認爲 ANSI 字符集。

[DllImport("user32.dll")]

public static extern int MessageBoxA(int hWnd, String text, String caption, uint type);

[DllImport("user32.dll", CharSet=CharSet.Unicode)]

public static extern int MessageBoxW(int hWnd, String text, String caption, uint type);

[DllImport("user32.dll", CharSet=CharSet.Auto)]

public static extern int MessageBox(int hWnd, String text, String caption, uint type);

CharSet.Ansi 和 CharSet.Unicode 的名稱匹配規則大不相同。對於 Ansi 來講,若是將 EntryPoint 設置爲「MyMethod」且它存在的話,則返回「MyMethod」。若是 DLL 中沒有「MyMethod」,但存在「MyMethodA」,則返回「MyMethodA」。對於 Unicode 來講則正好相反。若是將 EntryPoint 設置爲「MyMethod」且它存在的話,則返回「MyMethodW」。若是 DLL 中不存在「MyMethodW」,但存在「MyMethod」,則返回「MyMethod」。若是使用的是 Auto,則匹配規則與平臺有關(在 Windows NT 上爲 Unicode,在 Windows 98 上爲 Ansi)。若是 ExactSpelling 設置爲 true,則只有當 DLL 中存在「MyMethod」時才返回「MyMethod」。

若是 DLL 函數不以任何方式處理文本,則能夠忽略 DllImportAttribute 的 CharSet 屬性。然而,當 Char 或 String 數據是等式的一部分時,應該將 CharSet 屬性設置爲 CharSet.Auto。這樣可使 CLR 根據宿主 OS 使用適當的字符集。若是沒有顯式地設置 CharSet 屬性,則其默認值爲 CharSet.Ansi。這個默認值是有缺點的,由於對於在 Windows 2000、Windows XP 和 Windows NT® 上進行的 interop 調用,它會消極地影響文本參數封送處理的性能。

應該顯式地選擇 CharSet.Ansi 或 CharSet.Unicode 的 CharSet 值而不是使用 CharSet.Auto 的惟一狀況是:您顯式地指定了一個導出函數,而該函數特定於這兩種 Win32 OS 中的某一種。ReadDirectoryChangesW API 函數就是這樣的一個例子,它只存在於基於 Windows NT 的操做系統中,而且只支持 Unicode;在這種狀況下,您應該顯式地使用 CharSet.Unicode。

有時,Windows API 是否有字符集關係並不明顯。一種決不會有錯的確認方法是在 Platform SDK 中檢查該函數的 C 語言頭文件。(若是您沒法確定要看哪一個頭文件,則能夠查看 Platform SDK 文檔中列出的每一個 API 函數的頭文件。)若是您發現該 API 函數確實定義爲一個映射到以 A 或 W 結尾的函數名的宏,則字符集與您嘗試調用的函數有關係。Windows API 函數的一個例子是在 WinUser.h 中聲明的 GetMessage API,您也許會驚訝地發現它有 A 和 W 兩種版本。

以上內容採起直譯,大意就是那樣

這裏咱們通常不設置,即便用Auto便可。

各位Dalao有看法的話歡迎補充說明。

SetLastError(摘自MSDN)

SetLastError 錯誤處理很是重要,但咱們在編程時常常會遺忘,或者直接偷懶而致使程序容錯性差。

對於該函數,咱們可使用 GetLastError 來查找擴展的錯誤信息,則應該在外部方法的 DllImportAttribute 中將 SetLastError 屬性設置爲 true。

這會致使 CLR 在每次調用外部方法以後緩存由 API 函數設置的錯誤。

而後,在包裝方法中,能夠經過調用類庫的 System.Runtime.InteropServices.Marshal 類型中定義的 Marshal.GetLastWin32Error方法來獲取緩存的錯誤值。

個人建議是檢查這些指望來自 API 函數的錯誤值,併爲這些值引起一個可感知的異常。

對於其餘全部失敗狀況(包括根本就沒意料到的失敗狀況),則引起在 System.ComponentModel 命名空間中定義的 Win32Exception,並將 Marshal.GetLastWin32Error返回的值傳遞給它。

CallingConvention

該字段的值有如下幾個:

  • CallingConvention.Cdecl : 調用方清理堆棧。它使您可以調用具備 varargs 的函數(如printf)。

  • CallingConvention.StdCall : 被調用方清理堆棧。它是從託管代碼調用非託管函數的默認約定。

  • CallingConvention 字段的默認值爲 Winapi,然後者又默認爲 StdCall 約定。

這裏不作詳解,用到的地方很少,大可能是時候默認。

這裏給一個例子

[DllExport("about", CallingConvention = CallingConvention.Cdecl)]
public static void about()
{
}

這裏咱們須要本身實現該函數

ExactSpelling

ExactSpelling 指示是否應修改非託管 DLL 中的入口點的名稱,以與 CharSet 字段中指定的 CharSet 值相對應。

若是爲 true,則當 DllImportAttribute.CharSet 字段設置爲 CharSet 的 Ansi 值時,向方法名稱中追加字母 A,當 DllImportAttribute.CharSet 字段設置爲 CharSet 的 Unicode 值時,向方法的名稱中追加字母 W。

此字段的默認值是 false。

定義明確的狀況下,不刻意使用該字段。

我的認爲會把本身繞進去

結果

MarkDown真是太好使了!

相關文章
相關標籤/搜索