c 中DllImportAttribute用法彙總

2021-05-28 15:01:11 字數 4505 閱讀 2371

前文中說到dllimport用法彙總,說到dllimportattribute用法,現在結合msdn,現在把dllimportattribute下4個字段的用法彙總如下:

dllimportattribute屬性提供呼叫非託管函式的規範。在對託管**進行p/invoke呼叫時,dllimportattribute型別扮演著重要的角色。dllimportattribute的主要作用就是給clr指示哪個dll匯出您想要的呼叫的函式。相關dll的名稱被作為乙個建構函式引數傳遞給dllimportattribute。

如果您無法肯定哪個 dll 定義了您要使用的 windows api 函式,platform sdk 文件將為您提供最好的幫助資源。在 windows api 函式主題文字臨近結尾的位置,sdk 文件指定了 c 應用程式要使用該函式必須鏈結的 .lib 檔案。在幾乎所有的情況下,該 .lib 檔案具有與定義該函式的系統 dll 檔案相同的名稱。例如,如果該函式需要 c 應用程式鏈結到 kernel32.lib,則該函式就定義在 kernel32.dll 中。您可以在 messagebeep 中找到有關 messagebeep 的 platform sdk 文件主題。在該主題結尾處,您會注意到它指出庫檔案是 user32.lib;這表明 messagebeep 是從 user32.dll 中匯出的。
可選的 dllimportattribute 屬性
除了指出宿主 dll 外,dllimportattribute 還包含了一些可選屬性,其中四個特別有趣:entrypoint、charset、setlasterror 和 callingconvention。
entrypoint
指定要呼叫的dll入口點的名稱或序號(預設入口點名稱就是託管方法的名稱)。序號以「#」符號為字首,如#1。如果省略此欄位,則clr將使用以dllimportattribute標記的.net方法的名稱。entrypoint 在不希望外部託管方法具有與 dll 匯出相同的名稱的情況下,可以設定該屬性來指示匯出的 dll 函式的入口點名稱。當您定義兩個呼叫相同非託管函式的外部方法時,這特別有用。另外,在 windows 中還可以通過它們的序號值繫結到匯出的 dll 函式。如果您需要這樣做,則諸如「#1」或「#129」的 entrypoint 值指示 dll 中非託管函式的序號值而不是函式名。
下面的**示例(語言:c#)演示如何使用 dllimportattribute 屬性匯入 win32 messagebox 函式。 該**示例使用 entrypoint 屬性指定要匯入的函式,然後將名稱更改為 mynewmessageboxmethod。
using system;
using system.runtime.interopservices;
class example
}
charset
指示如何向方法封送字串引數。並控制名稱重整。通過乙個 charset 列舉的成員使用此欄位指定字串引數的封送處理行為,並指定要呼叫的入口點名稱(給定的確切名稱或以「a」、「w」結尾的名稱)。用於 c# 和 visual basic 的預設列舉成員為 charset.ansi,用於 c++ 的預設列舉成員為 charset.none,它與 charset.ansi 等效。在 visual basic 中可以使用 declare 語句指定 charset 字段。

exactspelling 欄位會影響 charset 欄位在確定要呼叫的入口點名稱時的行為。

charset 對於字符集,並非所有版本的 windows 都是同樣建立的。windows 9x 系列產品缺少重要的 unicode 支援,而 windows nt 和 windows ce 系列則一開始就使用 unicode。在這些作業系統上執行的 clr 將unicode 用於 string 和 char 資料的內部表示。但也不必擔心 — 當呼叫 windows 9x api 函式時,clr 會自動進行必要的轉換,將其從 unicode轉換為 ansi。

如果 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 兩種版本。

示例同上。
setlasterror
用來指示被呼叫方在從屬性化方法返回之前是否呼叫setlasterror win32 api函式。true 指示被呼叫方將呼叫 setlasterror;否則為 false。預設值為 false。執行時封送拆收器將呼叫 getlasterror 並快取返回的值,以防其被其他 api 呼叫覆蓋。
setlasterror 錯誤處理非常重要,但在程式設計時經常被遺忘。當您進行 p/invoke 呼叫時,也會面臨其他的挑戰 — 處理託管**中 windows api 錯誤處理和異常之間的區別。我可以給您一點建議。

如果您正在使用 p/invoke 呼叫 windows api 函式,而對於該函式,您使用 getlasterror 來查詢擴充套件的錯誤資訊,則應該在外部方法的 dllimportattribute 中將 setlasterror 屬性設定為 true。這適用於大多數外部方法。

這會導致 clr 在每次呼叫外部方法之後快取由 api 函式設定的錯誤。然後,在包裝方法中,可以通過呼叫類庫的 system.runtime.interopservices.marshal 型別中定義的 marshal.getlastwin32error 方法來獲取快取的錯誤值。我的建議是檢查這些期望來自 api 函式的錯誤值,並為這些值引發乙個可感知的異常。對於其他所有失敗情況(包括根本就沒意料到的失敗情況),則引發在 system.componentmodel 命名空間中定義的 win32exception,並將 marshal.getlastwin32error 返回的值傳遞給它。如果您回頭看一下上文的**,您會看到我在 extern messagebeep 方法的公共包裝中就採用了這種方法。

示例:
using system.runtime.interopservices;
public class win32
callingconvention指定傳遞方法引數時使用的呼叫約定值。預設值為 callingconvention.

winapi(winapi預設stdcall約定),它在大多數情況下都可行。該值與 windows ce 平台上的 __cdecl 相對應。

然而,如果該呼叫不起作用,則可以檢查 platform sdk 中的宣告標頭檔案,看看您呼叫的 api 函式是否是乙個不符合呼叫約定標準的異常 api。

通常,本機函式(例如 windows api 函式或 c- 執行時 dll 函式)的呼叫約定描述了如何將引數推入執行緒堆疊或從執行緒堆疊中清除。大多數 windows api 函式都是首先將函式的最後乙個引數推入堆疊,然後由被呼叫的函式負責清理該堆疊。相反,許多 c-執行時 dll 函式都被定義為按照方法引數在方法簽名中出現的順序將其推入堆疊,將堆疊清理工作交給呼叫者。

幸運的是,要讓 p/invoke 呼叫工作只需要讓外圍裝置理解呼叫約定即可。通常,從預設值 callingconvention.winapi 開始是最好的選擇。然後,在 c 執行時 dll 函式和少數函式中,可能需要將約定更改為 callingconvention.cdecl。

示例:在某些情況下,visual basic 開發人員會使用 dllimportattribute 來代替 declare 語句,在託管**中定義 dll 函式。設定 callingconvention 欄位即屬於這種情況。

using system;

using system.runtime.interopservices;

public class libwrap

}

C中extern和C 中的export

為了訪問其他編譯單元 如另一 檔案 中的變數或物件,對普通型別 包括基本資料類 結構和類 可以利用關鍵字extern,來使用這些變數或物件時 但是對模板型別,則必須在定義這些模板類物件和模板函式時,使用標準c 新增加的關鍵字export 匯出 出口 輸出 例如 extern int n extern...

C中相容C 操作

在c 中加入c 風格的 在編譯的時候,報錯的是必然的.因為不相容.例如,在c中加入c 的namespace namespace cv get window image rectangle coordinates,width and height cvapi cv rect cvgetwindowim...

C中呼叫C 函式

將 c 函式宣告為 extern c 在你的 c 裡做這個宣告 然後呼叫它 在你的 c 或者 c 裡呼叫 例如 c code extern c void f int void f int i 然後,你可以這樣使用 f c code void f int void cc int i f i 當然,這招...