API 集合載入器作業

重要

本主題中的資訊適用於所有 Windows 10 版本及更新版本。 我們將在這裡將這些版本稱為「Windows」,並在必要時呼叫任何例外狀況。

API 集 依賴函式庫載入器的作業系統支援,在函式庫綁定過程中引入模組命名空間重定向。 API 合約集名稱並不代表檔案名稱。 載入器會執行執行時重定向,從該合約名稱轉移到包含實作的主機二進位檔。

當載入器在執行時遇到對某 API 集合的依賴時,會參考映像中的設定資料,以辨識該 API 集合的宿主二進位檔。 此組態資料稱為 API 集合架構。 該結構是作為作業系統的屬性組合而成,API 集合與二進位檔之間的對應會根據裝置中包含的二進位檔而有所不同。 結構是讓匯入單一二進位的函式能在不同裝置上正確路由的關鍵,即使承載實作的模組已被重新命名、拆分或重構。

匯入如何到達實作

二進位檔可以透過兩種方式到達 API 集合實作,分別由匯入表中的名稱決定:

  • 直接 API 設定匯入。 該二進位檔匯入一個 API 集合的合約名稱。 載入器透過 API 集合架構將該名稱解析為目前裝置上的主機二進位檔。
  • 舊有模組匯入。 該二進位檔匯入一個舊有的模組名稱Windows模組名稱,例如 samplefeature.dll。 在出貨該模組的版本中,載入者會直接綁定該模組。 在已取代它的版本中,帶有同名的 反向轉發器 會將匯入導向到 API 集合,然後載入器透過結構解析該集合。

這些名稱最後會出現在匯入表中,通常是由你連結的函式庫決定,而不是你寫的原始碼。 請參見 Windows 總函式庫。

針對目前 Windows 版本的程式碼,建議使用 API 集合合約名稱。 載入器會直接解析給主機,中間沒有轉發器。 當你需要一個同時能在 API 集尚未推出的 Windows 版本上執行的單一二進位檔時,匯入舊有模組名稱。 反向轉發讓該二進位檔在舊有模組被替換的版本中仍能正常運作。

Direct API set import

解決是一個三步驟的過程:

  1. 你的二進位檔會匯入一個 API 合約名稱,或把一個合約傳給 LoadLibrary。
  2. 載入器會在目前裝置的 API 集合架構中查找合約,並找到該架構對應到的主機二進位檔。
  3. 載入器會載入主機的二進位檔,並將匯入的函式綁定到主機的匯出檔。

由於映射存在於結構中而非檔案系統中,相同匯入可能會解析成不同裝置上的二進位檔:

裝置 api-win-core-samplefeature 映射至
具備此功能的裝置 samplefeature.dll
一個能發佈重構實作的裝置 samplefeaturecore.dll
一個沒有這個功能的裝置 未對應

samplefeature此處使用的名稱是虛構 Windows 元件的說明性名稱。

消費二進位不知道自己綁定在哪個宿主。 這就是機制的重點:合約是穩定的,而實作它的模組可以自由從一個裝置切換到下一個。

匯入合約名稱只需一次操作即可解決,無需中間轉運模組。 這是最有效率的形式,也是針對 API 集合撰寫程式碼的正常路徑。

API 集合名稱與 .dll 後綴

由於映射資料保存在結構中而非磁碟中,API 集合名稱若以 .dll 結尾,並不代表該名稱的檔案。 .dll 部分只是命名慣例,延續自匯入表中模組名稱的拼寫方式。 API 集合名稱更像是實體 DLL 檔案的別名或虛擬名稱。

當載入器操作收到以 api- 或 ext-開頭的名稱時,載入器會將其路由至 API 集合執行時,該執行時是載入器的擴充,透過結構解決合約。 API 集合執行時是依照 API 集合命名規則解析名稱,而非檔案名稱,因此 .dll 後綴不會包含在被解決的合約名稱中。 當你根據匯入表中出現的名稱工作時,請加上後綴;否則你可以直接關掉。

載入器透過相同的結構解析兩種合約名稱,即版本化合約名稱與合約別名。 關於規範這些名稱的慣例,請參見 API 集合合約名稱。

名稱穩定性不等於可用性

API 集合名稱在 Windows 裝置間是穩定的,意思是同一名稱在被識別的合約中總是能識別到相同的合約。 這是命名空間的特性,並非對任何特定裝置的保證。

某個契約可能不存在於裝置,或存在但未映射到主機。 名字裡沒有任何跡象告訴你是哪一個。 想確認實作是否真的存在,請參閱 Detect API set availability。

解決所需的條件

若要透過 API 集合呼叫抵達實作,以下條件必須全部成立:

  • 合約存在於目前裝置的結構中。
  • 該結構會將合約映射到主機二進位檔,該主機可以被載入。
  • 主機會匯出你二進位檔所呼叫的特定函式。

當其中一項不成立時,失敗的出現地點取決於你如何匯入 API 集合:

進口風格 合約無法解決時的行為
靜態匯入 這個過程未能開始。 載入器會在你的程式碼執行前解決靜態匯入問題。
延遲載入匯入 這個過程會正常開始。 解析會延後到第一次呼叫 API 時,你的程式碼可以處理失敗。

缺失的出口與缺失的合約會分開報告;一個二進位檔如果匯入了主機沒有匯出的函式,會因缺少入口點錯誤而失敗。

成功裝載並不能告訴你什麼

解決是將 合約 綁定於宿主。 它不會評估該合約中個人能力的狀態。

合約可以將其個別可用的能力組織成 命名群組。 即使承載該群組的合約正常解決,群組仍可能無法在裝置上使用,因為載入器在合約細節上綁定,綁定時不會參考群組狀態。 這是刻意為之:拒絕綁定主機對靜態匯入是致命的,因此載入者會選擇允許路徑,並將更細緻的問題留給呼叫者處理。

對你的程式碼來說,成功的載入或 LoadLibrary 呼叫,並不代表某項能力已經可用。 用可排問題明確問這個問題。 請參見 Detect API set availability。

可選的 API 集合與延遲載入

如果你的應用程式呼叫的 API 集合可能不存在,單靠可用性檢查是不夠的:靜態匯入程序無法啟動,執行無法到達檢查。

為了保持可選的程式碼路徑可達,要麼設定攜帶可選 API 的模組以延遲 載入,要麼在可用性查詢成功後,使用 LoadLibrary 和 GetProcAddress 動態解析目標。 關於兩種方法的詳細資訊,請參見 偵測 API 集合可用性。

反向轉送

雖然 API 集合名稱為跨裝置的模組提供了穩定的命名空間,但並非總是將每個二進位檔轉換成此系統。 一個應用程式可能已經被廣泛使用多年,重新編譯其二進位檔案可能不可行。 有些應用程式還需要持續在特定 API 集導入前所建置的系統上運行。

為了配合這點,未包含原始模組的版本包含一組反向轉發器:相容性二進位檔,攜帶最初在 Windows PC 上引入的模組名稱,並將匯出重新導向至 API 集合。

完整桌面版會附帶原始模組,因此匯入舊有模組名稱時會像往常一樣綁定到模組。 在取代該模組的版本中,同名的反向前進器會覆蓋該空隙。

載入器作業的行為如下所示:

  1. 載入器會依賴一個舊有的 Windows PC 模組名稱,而該模組名稱在裝置上並不存在。
  2. 載入器會找到攜帶該模組名稱的反向轉發器,並加載它。
  3. 反向轉發器會將匯入的函式重新導向到 API 集合。
  4. 載入器透過結構解析該 API 集合,如本主題先前所述。

概念上,映射如下:

進口 DLL: samplefeature.dll

  • 在包含原始模組的版本中: samplefeature.dll
  • 在已取代它的版本中: samplefeature.dll 反向轉送器 ->api-win-core-samplefeature ->samplefeaturecore.dll

這條路徑的限制是出口覆蓋範圍。 反向轉發器只承載有 API 設定等效的匯出,因此不一定匯出原始模組完成的所有函式。 一個二進位檔如果匯入了反向轉發器沒攜帶的函式,會導致載入失敗,缺少一個入口點錯誤。

反向轉發也是不應將成功解決方案視為實作存在的證明。 對舊有模組名稱呼叫 GetProcAddress 可以回傳一個有效的函式指標,該指標會解析為一個返回錯誤的存根。 而是明確查詢可用性。 請參見 Detect API set availability。

注意

反向轉發只涵蓋 Win32 API 表面的部分。 它不允許針對桌面版 Windows 的應用程式在所有 Windows 裝置上執行。 如果你的二進位檔是針對目前版本的 Windows,API 集合合約名稱會是更直接的選擇。

另請參閱