標籤:style code http tar ext color
使用平台叫用
P/Invoke,它的全名叫平台叫用(platform invoke),用於調用dll 中實現的非託管的單調(flat)編程介面,被稱為使用C或C++ 呼叫慣例(calling conventions)。最有名的例子是Win32 編程介面,這是一個巨大的庫,它公開了Windows 所有的內建功能。
為了調用單調的非託管編程介面,必須首先定義準備調用的函數,可以分成兩步:第一步,用System.Runtime.InteropServices 命名空間下的 DllImport 特性(attribute),能夠定義包含想匯入函數的 .dll,加上一些其他的可選特性;第二步,用關鍵字extern,加以 C 風格函數調用的簽名,這樣,指定了傳回型別為F# 類型,和函數的名字,最後是用括弧括起來的參數類型和參數名。結果這個函數就能像外部的.NET 方法一樣進行調用。
下面的例子示範了如何匯入Windows 函數MessageBeep,並調用:
open System.Runtime.InteropServices
// declare a function found in an external dll
[<DllImport("User32.dll")>]
extern boolMessageBeep(uint32 beepType)
// call this method ignoring the result
MessageBeep(0ul) |> ignore
注意
使用平台叫用,最棘手的問題就是要找出要調用函數的簽名。在http://pinvoke.net 網站上有 C# 和 VB .NET 中常用編程介面的簽名的清單,F# 中需要的簽名也相類似。這個網站是一個維基百科(wiki),因此可以自由添加 F# 簽名。
下面的代碼示範了如何使用平台叫用,目標函數期望一個指標,有關設定指標需要注意幾點。當定義函數時,需要在類型名字的後面加星號(*),表示傳遞指標;在函數調用之前,還要定義一個彈性識別碼,表示指標指向的記憶體地區,它可能不是全域的,但是在頂層,必須是函數定義的一部分。這就是為什麼定義函數main,標識符status 是函數定義的一部分;最後,必須使用地址運算子(&&),保證傳遞給函數的是指標而不是值本身。
提示
編譯這段代碼總是有警告,因為使用了地址運算子(&&)。要抑制這個警告,可以使用編譯器開關--nowarn 51,或者命令#nowarn 51。
openSystem.Runtime.InteropServices
// declare a function found in an external dll
[<DllImport("Advapi32.dll")>]
extern boolFileEncryptionStatus(string filename, uint32* status)
let main() =
//declare a mutable idenifier to be passed to the function
let mutable status = 0ul
// call thefunction, using the address of operator with the
// secondparameter
FileEncryptionStatus(@"C:\test.txt", && status) |>ignore
printfn"%d" status
main()
這個例子的運行結果如下(假設在 C: 盤根目錄下有一個檔案test.txt,加過密的):
1ul
注意
平台叫用也可以運行在 Mono 平台上,文法與 F# 中的完全一樣,而痛點在於保證要調用的庫在所有的目標平台上都可用,且遵循在所有不同的平台上庫的不同的命名規範。更多有關解釋的細節,請看http://www.mono-project.com/Interop_with_Native_Libraries上的文章。
DllImport 特性有一些有用的函數,能夠設定用來控制如何調用非託管的函數。表 14-1 做了匯總。
表 14-1 DllImport 上有用的特性
特性名 |
描述 |
CharSet |
定義了傳送字串資料的字元集,可以是 CharSet.Auto、CharSet.Ansi、CharSet.Unicode |
EntryPoint |
設定調用函數的名字。如果沒有給定名字,那麼,關鍵字 extern 後面的名字就作為預設定義的函數名。 |
SetLastError |
這是一個邏輯值,指定是否遇到任何錯誤都應該傳送,因此,通過調用 Marshell.GetLastWin32Error() 方法檢查可用性。 |
注意
因為有 COM 組件,沒有等價的.NET 的非託管編程介面的數量在持續減少,因此,在準備調用函數前檢查一下是否有等價的託管函數,通常會節省大量時間。