Windows 應用程式相容如何運作 ── 用相容模式、Shim 與 Compatibility Administrator 延續舊應用
· Go Komura · Windows, 相容模式, Shim, 應用程式相容性, Compatibility Administrator, 既有資產活用, Windows 開發, 既有系統
「一份十年前的業務應用、原始碼已經不在,在新的 Windows 11 PC 上無法啟動。我在內容對話方塊的相容性索引標籤勾了『Windows XP』,它就動了。——那到底在做什麼?繼續依賴這個沒問題嗎?」這是我們常接到的諮詢。
單靠一個核取方塊就讓東西動起來,不安感反而會升高。看起來像魔法的相容模式,真正身分是夾在應用與 Windows API 之間、回傳「謊言」的一小段程式碼集合,稱為 Shim。Windows 自己也大規模使用這套權宜之計,讓許多世代以前的應用繼續跑,並把機制的一部分開放給使用者與系統管理員。
不了解機制就使用,延長壽命會變成「不知道為什麼能跑,所以千萬別碰」的不穩定狀態。了解機制之後,才能帶著理由決定 能安心依賴到什麼程度、什麼會把它弄壞、何時該重寫。
flowchart TB
accTitle: 理解機制會改變延長壽命的品質
accDescr: 不了解機制就使用相容模式,會變成不敢碰的不穩定延長壽命;理解機制後,才能帶著理由決定能依賴到哪裡、什麼會弄壞它、何時該重寫
unknown["不了解機制就使用"] --> fear["不敢碰的不穩定延長壽命"]
known["理解機制後再使用"] --> judge["帶理由的判斷"]
judge -.-> j1["能依賴到什麼程度"]
judge -.-> j2["什麼會把它弄壞"]
judge -.-> j3["何時該重寫"]
圖 1: 同樣是延長壽命,不了解機制的不安,與建立在理解上的判斷,品質並不相同。
本文面向中小企業的 IT 人員,以及負責舊業務應用的 Windows 應用開發者,依 Microsoft Learn 第一手資料整理相容模式的真正身分——Shim 的機制、代表性 Shim 能做什麼、用 Compatibility Administrator 在組織內套用的方法、Shim 救不了的界限,以及延長壽命與遷移之間的判斷。
1. 先講結論
- 相容模式的真正身分是 Shim(相容層)。 相容性索引標籤的設定會寫入
HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers,啟動時把一組 Shim 套用到該行程。12 - Shim 是改寫匯入位址表(IAT)的使用者模式 API 攔截。 它攔截應用呼叫 Windows API 的路徑,回傳舊版 Windows 會給的答案。它不改作業系統本身。3
- Shim 能做的範圍,與在應用裡改程式碼能做的範圍相同。 它無法繞過安全性機制,也無法修正核心模式(裝置驅動程式)的問題。3
- Microsoft 出貨大量現成的 Shim——版本謊言、檔案路徑重新對應、登錄偽裝、系統管理員檢查偽裝等。可在 Compatibility Administrator 套用到個別 EXE。4
- Windows 自己預設就在用 Shim。 每次啟動都會比對 OS 標準相容性資料庫(.sdb),PCA(Program Compatibility Assistant)也可能偵測問題並自動套用相容設定。15
- 「假裝成較舊的 Windows 來回答」如今已是預設。 從 Windows 8.1 起,
GetVersionEx不會回傳應用未在資訊清單宣告的 OS 版本。相容模式是這套機制的延伸。67 - Shim 對 16 位元應用、核心驅動程式依賴、直接硬體存取無效。 尤其 16 位元應用在 64 位元 Windows 上根本無法執行。8
- 對「要求系統管理員但實際上不需要」的應用,RunAsInvoker 是標準手法。
__COMPAT_LAYER=RunAsInvoker抑制提高權限要求,讓應用以標準權限執行。9 - 在 Shim 下能跑,代表目前可以延長壽命,但真正的路仍是「讓它不靠 Shim 也能跑」。 若決定延長壽命,請記錄是哪些 Shim 讓它能跑,並作為重寫判斷的材料來管理。
2. 應用程式相容的全貌 ── Windows 已經具備的向下相容層
談 Shim 之前,先列出 Windows 為舊應用準備的機制。即使人們說「在相容模式下開始能跑了」,真正救下應用的,是這些層的其中一層,或好幾層的組合。
| 層 | 做什麼 | 典型對象 |
|---|---|---|
| Shim(相容模式) | 攔截 API 呼叫,偽裝成舊 Windows 會給的回應 | 針對較舊 OS 撰寫的應用整體 |
| UAC 虛擬化(檔案/登錄) | 把對 HKLM\Software 或 Program Files 沒有權限的寫入,重新導向到每位使用者的 VirtualStore |
以系統管理員權限為前提撰寫的 32 位元應用 |
| WOW64 | 在 64 位元 Windows 上原樣執行 32 位元應用(提供登錄與檔案系統的 32 位元檢視) | 32 位元應用整體 |
| DPI 虛擬化 | 讓未宣告 DPI 感知的應用以 96 DPI 繪製,再拉伸點陣圖來顯示 | 高 DPI 顯示器上的舊應用 |
UAC 虛擬化是針對沒有資訊清單的 32 位元互動行程的過渡措施,Microsoft 自己也寫明它是「打算從未來 Windows 版本移除的暫時技術」。10 Wow6432Node 重新導向與 VirtualStore 造成的實際傷害與對策,詳見「登錄檔的 32bit/64bit 重新導向與虛擬化的陷阱 ── Wow6432Node 與「明明寫入了卻讀不到值」的問題」;本文以 Shim 為中心,其他層只談到必要之處。
flowchart TB
accTitle: UAC 虛擬化所在的位置
accDescr: UAC 虛擬化是針對沒有資訊清單的 32 位元互動行程的過渡措施,會把寫入重新導向到每位使用者的 VirtualStore,但 Microsoft 自己寫明這是打算從未來 Windows 移除的暫時技術
proc["沒有資訊清單的 32 位元互動行程"] --> uacv["套用 UAC 虛擬化"]
uacv --> vs["重新導向到每位使用者的 VirtualStore"]
uacv -.-> tmp["打算日後移除的暫時技術"]
圖 2: UAC 虛擬化是沒有資訊清單的 32 位元行程的過渡措施,不能永久依賴。
關於 DPI 虛擬化的補充:未宣告 DPI 感知的應用會被視為以 96 DPI(100%)繪製,Windows 再拉伸點陣圖來顯示。這就是舊應用在高 DPI 螢幕上看起來「模糊」的原因,相容性索引標籤的「覆寫高 DPI 縮放行為」就是切換這套虛擬化行為的開關。11
flowchart TB
accTitle: DPI 虛擬化如何運作
accDescr: 未宣告 DPI 感知的應用會被視為以 96 DPI 繪製,Windows 拉伸點陣圖因而看起來模糊,相容性索引標籤覆寫高 DPI 設定會切換這套虛擬化行為
app["未宣告 DPI 感知的應用"] --> treat["視為以 96 DPI 繪製"]
treat --> stretch["為了顯示而拉伸點陣圖"]
stretch --> blur["在高 DPI 螢幕上看起來模糊"]
tab["覆寫高 DPI 縮放行為"] -.->|切換虛擬化行為| treat
圖 3: 未感知 DPI 的應用被視為 96 DPI 並被拉伸;相容性索引標籤的覆寫就是這套虛擬化的開關。
3. Shim 真正是什麼 ── 改寫 IAT、在 API 之間攔截
3.1. 站在應用與 OS 之間的「口譯」
Windows 可執行檔(PE 格式)透過 匯入位址表(IAT) 呼叫外部 DLL 的 API。應用呼叫 GetVersionEx 時,只是跳到 IAT 裡寫好的位址。Shim 機制就是利用這一點。載入時把目標 API 的 IAT 項目改寫成 Shim 程式碼的位址,把自己插入應用與 Windows 之間。透過 GetProcAddress 動態取得的 API,則靠攔截 GetProcAddress 本身來處理。3
攔截之後,Shim 可能對「目前 OS 版本是什麼」回傳舊版本號,或把對不可寫位置的檔案存取重新對應到別處,必要時再呼叫真正的 API。從應用來看像是「跑在舊 Windows 上」;從 OS 來看像是「一個行為端正的應用在跑」——Shim 是兩者之間的口譯。
flowchart TB
accTitle: Shim 攔截 API 呼叫的路徑
accDescr: 應用的 API 呼叫經過 IAT;載入時把 IAT 項目改寫成 Shim,就能攔截、偽裝成舊 Windows 會給的回應,必要時再呼叫真正的 API
app["應用"] -->|API 呼叫| iat["IAT 項目"]
iat -->|載入時改寫成 Shim| shim["Shim(口譯)"]
shim -->|必要時| api["真正的 Windows API"]
shim -.-> lie["偽裝成舊 Windows 會給的回應"]
gpa["經 GetProcAddress 呼叫"] -.->|以攔截處理| shim
圖 4: Shim 攔截在應用與 Windows API 之間。被改寫的是應用端的 IAT,作業系統本身沒有改變。
這個設計帶出三個重要性質。3
- Shim 以應用端程式碼執行。 它不是 OS 的一部分,因此受與應用相同的安全性約束。Shim 無法繞過 OS 安全性機制,也不需要為了用 Shim 而放寬安全性設定。
- Shim 能修的,應用端改程式碼也能修。 Shim 是「沒有原始碼/修不了」時的替代品,並不比改程式碼更強。
- 僅限使用者模式。 在核心模式執行的裝置驅動程式相容問題,Shim 修不了。
3.2. Shim 資料庫(.sdb)與比對
「對哪個 EXE 套用哪個 Shim」的對應表就是 Shim 資料庫,副檔名為 .sdb 的二進位檔。目標應用可執行檔以檔名、大小、檢查碼、版本等屬性(比對屬性)登錄,並在行程啟動時比對。處方包含注入 API 攔截的 Appfix(Shim),以及顯示「此應用有相容問題」訊息的 Apphelp。數個 Shim 與旗標的組合就是 相容層(相容模式)。1
容易忽略:這套比對不只發生在已設定相容模式的應用,而是 每一次行程啟動。Windows 出貨一份涵蓋數千個已知應用的 OS 標準資料庫(檔案在 %WINDIR%\AppPatch 下),今天你的 PC 上幾乎一定有某個舊應用在沒人注意的情況下帶著 Shim 啟動。Microsoft 提供的相容修正作為 Windows 的一部分出貨,並經 Windows Update 更新。3
flowchart TB
accTitle: 行程啟動時比對 Shim 資料庫
accDescr: 每一次行程啟動都會與 Shim 資料庫比對;若登錄符合比對屬性,Appfix 會注入 Shim 或 Apphelp 顯示訊息,否則原樣啟動
start["行程啟動"] --> db["與 .sdb 比對"]
db -.-> attr["檔名、大小等"]
db --> hit{"有登錄嗎?"}
hit -->|是| appfix["Appfix:注入 Shim"]
hit -->|是| apphelp["Apphelp:訊息"]
hit -->|否| plain["原樣啟動"]
layer["相容層"] -.->|多個 Shim 與旗標的組合| appfix
圖 5: 比對發生在每一次行程啟動,不只發生在已設定相容模式的應用。
3.3. PCA ── 自動套用 Shim 的機制
另一條系統管理員並未意圖、卻仍可能套用 Shim 的路徑是 PCA(Program Compatibility Assistant)。PCA 監視應用執行,偵測到已知相容問題的跡象時,會向使用者建議套用修正,或在某些情況下自動套用相容設定。例如呼叫已釋放 DLL 內程式碼而當掉的應用會被指定 PINDLL,寫入受保護 Windows 檔案失敗的應用會被指定 WRPMITIGATION。5
flowchart TB
accTitle: PCA 如何自動套用相容設定
accDescr: PCA 監視應用執行,偵測到已知相容問題的跡象時,會向使用者建議套用修正,或在某些情況下自動套用相容設定
run["應用執行"] --> pca["PCA 監視"]
pca --> sign{"已知問題的跡象?"}
sign -->|是| resp{"哪一種情況?"}
resp -->|以建議處理| suggest["建議套用修正"]
resp -->|某些情況| auto["自動套用相容設定"]
sign -->|否| none["原樣執行"]
auto -.-> ex["例:PINDLL 或 WRPMITIGATION"]
圖 6: PCA 監視應用執行,偵測到已知問題跡象時會建議修正或自動套用。
「我從沒設定過,不知何時相容模式核取方塊卻開著」的身分,很多時候就是這個。既不是故障,也不是誤按,而是 Windows 依設計在運作。
4. 代表性 Shim 能做什麼
從 Microsoft 公開的現成 Shim 裡,挑出延續業務應用壽命時實際常出現的項目。4
| Shim | 能做的事(摘要) |
|---|---|
| WinXPSP3VersionLie 等 VersionLie 系列 Shim | 對 OS 版本查詢回傳指定的較舊版本(版本偽裝) |
| CorrectFilePaths | 把對不可寫或不存在檔案路徑的存取重新對應到別處 |
| VirtualRegistry | 重新導向或偽裝登錄讀寫(含版本偽裝與假裝不存在的機碼) |
| ForceAdminAccess | 對「你是不是 Administrators 群組成員?」的檢查暫時回傳 True |
| RunAsAdmin / RunAsHighest / RunAsInvoker | 從外部給予相當於資訊清單 requireAdministrator / highestAvailable / asInvoker 的執行層級 |
| WRPMitigation | 對受保護 OS 檔案與登錄機碼的寫入假裝成功,讓應用能繼續 |
| EmulateGetDiskFreeSpace | 把磁碟剩餘空間回報為最多 2GB(給在大磁碟上溢位的應用) |
| GlobalMemoryStatusLie | 偽裝回報的記憶體狀態值(給啟動時記憶體檢查失敗的應用) |
| LoadLibraryRedirect | 載入 Windows 目前的 DLL,而不是應用隨附的舊系統 DLL |
看這份清單,多數 Shim 都是 「回傳舊應用所期待答案的謊言」。磁碟最多 2GB、OS 是 XP、你是系統管理員——它們只在那個行程裡,重現應用誕生那個時代的世界觀。
版本偽裝已成為「官方預設行為」
版本偽裝不是特別的駭客手法。從 Windows 8.1 起,GetVersionEx 回傳的值取決於應用的資訊清單。資訊清單 <compatibility> 區段沒有 <supportedOS> 宣告的應用,無論實際 OS 是什麼,一律拿到相當於 Windows 8 的 6.2。有宣告時,回傳的是 已宣告之中最高 OS 為止的值(例如宣告到 Windows 8.1 GUID,即使在 Windows 11 也會拿到 6.3)。67
因此「應用看到的 Windows 版本」是依這些堆疊階段決定的。
- 回傳資訊清單所宣告 OS 為止的值(沒有宣告則為 6.2)
- 若套用相容模式(VersionLie 系列 Shim),則回傳所選 OS 的版本6
flowchart TB
accTitle: 應用看到的 OS 版本如何決定
accDescr: GetVersionEx 回傳的值由資訊清單是否有 supportedOS 宣告決定;沒有宣告則回傳相當於 Windows 8 的 6.2,有宣告則回傳最高已宣告 OS 為止的值,若套用 VersionLie 系列 Shim 則覆寫成所選 OS 版本
q["GetVersionEx 查詢"] --> m{"有 supportedOS 宣告嗎?"}
m -->|否| v62["回傳相當於 Windows 8(6.2)"]
m -->|是| decl["最高已宣告 OS 為止的值"]
v62 --> lie{"套用了 VersionLie 系列 Shim?"}
decl --> lie
lie -->|是| fake["相容模式所選的 OS 值"]
lie -->|否| asis["值原樣回傳"]
圖 7: 應用看到的 Windows 版本,由資訊清單與 Shim 的堆疊階段決定。
若內部應用「依 OS 版本分支,明明是 Windows 11 卻被奇怪地判定成 8」,先懷疑資訊清單的 supportedOS 宣告。反過來說,因版本檢查而拒絕啟動的舊應用,很高機率能用 VersionLie Shim 過關。很多情況它只看版本號碼,實際行為在較新 OS 上也沒問題。
flowchart TB
accTitle: 兩種版本引起的症狀與對策
accDescr: 內部應用明明是 Windows 11 卻被判定成 8,先懷疑資訊清單的 supportedOS 宣告;因版本檢查拒絕啟動的舊應用,很高機率能用 VersionLie Shim 過關
sym1["明明是 Windows 11 卻被判定成 8"] --> fix1["懷疑 supportedOS 宣告"]
sym2["因版本檢查拒絕啟動"] --> fix2["試用 VersionLie 過關"]
fix2 -.-> why["實際行為在較新 OS 上往往沒問題"]
圖 8: 判定過舊就懷疑資訊清單;拒絕啟動就懷疑 VersionLie。
5. 相容模式核取方塊做了什麼
內容 → 相容性索引標籤的設定存在登錄的 AppCompatFlags\Layers 機碼。DXGI 應用相容設定等也用同一個機碼來指定相容層。2 實際看一下。
reg query "HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
在相容性索引標籤對某個 EXE 設定「Windows XP (Service Pack 3)」、「以系統管理員身分執行此程式」、「覆寫高 DPI 縮放行為」時,會看到類似下面的值。
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
C:\LegacyApp\Gyomu.exe REG_SZ ~ WINXPSP3 RUNASADMIN HIGHDPIAWARE
核取方塊項目與值的代表對應(在 Windows 11 確認;項目名稱與值可能隨 OS 版本改變)。
| 相容性索引標籤項目 | 寫入的值(例) | 實際是什麼 |
|---|---|---|
| 相容模式:Windows XP (Service Pack 3) | WINXPSP3 | 捆綁版本偽裝與其他數個 Shim 的相容層 |
| 降低色彩模式(8 位元/256 色) | 256COLOR | 對舊色彩模式的放寬 |
| 以 640 × 480 螢幕解析度執行 | 640X480 | 以低解析度執行 |
| 停用全螢幕最佳化 | DISABLEDXMAXIMIZEDWINDOWEDMODE | 停用全螢幕時的繪製最佳化 |
| 覆寫高 DPI 縮放行為(應用程式) | HIGHDPIAWARE | 停止 DPI 虛擬化(點陣圖拉伸)11 |
| 以系統管理員身分執行此程式 | RUNASADMIN | 啟動時要求提高權限 |
記住三點。
- 「以系統管理員身分執行此程式」寫在同一個地方。 相容模式與提高權限旗標住在同一個 Layers 機碼,這就是「設定相容模式後提高權限跟著來/消失了」這種混亂的來源。直接看值就能把兩者分開。
- 寫在 HKCU 的是「該使用者的設定」。 從索引標籤的「變更所有使用者的設定」設定時,會寫到 HKLM 側的同名機碼,套用到所有使用者。用映像部署時,要意識自己寫的是哪一側。
- 核取方塊只是現成層的入口。 索引標籤只能選代表性層,不能挑個別 Shim 再組合。那是下一章 Compatibility Administrator 做的事。
flowchart TB
accTitle: 從相容性索引標籤設定到生效的路徑
accDescr: 相容性索引標籤設定以 EXE 路徑與值存在 AppCompatFlags Layers 機碼;下次該 EXE 啟動時,載入器讀取值並把對應相容層套用到行程
tab["在相容性索引標籤設定"] --> reg["把 EXE 路徑與值存入 Layers 機碼"]
reg --> boot["下次 EXE 啟動"]
boot --> loader["載入器讀取值"]
loader --> apply["把相容層套用到行程"]
reg -.-> hkcu["HKCU 僅該使用者"]
reg -.-> hklm["HKLM 套用到所有使用者"]
圖 9: 核取方塊實際上是寫入 Layers 機碼,套用發生在下一次啟動。
6. Compatibility Administrator 實務 ── 建立並發佈自訂 .sdb
6.1. 如何取得,以及注意事項
Compatibility Administrator 是 Windows ADK(Windows Assessment and Deployment Kit)內含的工具。12 安裝後同時有 32 位元與 64 位元版本,32 位元應用必須用 32 位元版,64 位元應用必須用 64 位元版。13
還有一個重要注意事項。若以已提高權限(系統管理員)啟動 Compatibility Administrator 來測試,UAC 虛擬化與重新導向的行為會與真實使用者不同,可能誤判「已經修好」。務必用與實際使用者相同的帳戶與權限確認修正效果。4
flowchart TB
accTitle: 使用 Compatibility Administrator 時的兩項注意
accDescr: 32 位元應用用 32 位元版、64 位元應用用 64 位元版,並以與實際使用者相同的帳戶與權限確認修正效果,而不是在已提高權限的狀態下確認
app32["32 位元應用"] --> tool32["使用 32 位元版"]
app64["64 位元應用"] --> tool64["使用 64 位元版"]
elev["在已提高權限下測試"] -.-> wrong["可能誤判修正"]
user["以實際使用者相同權限測試"] --> ok["確認效果"]
圖 10: 選擇 32 位元或 64 位元版本,以及用與實際使用者相同的權限確認,是入口處的注意事項。
6.2. 建立自訂相容性資料庫的步驟
大綱如下。14
- 在 Compatibility Administrator 左窗格的「Custom Databases」建立新資料庫,選「Create New」→「Application Fix」
- 輸入應用名稱與廠商名稱,並指定目標 EXE 檔
- 選擇要套用的相容模式(層)——先試「Windows XP 相容」這類捆綁是捷徑
- 必要時再加入個別相容修正(Shim)——可以收斂到只留 VersionLie 或只留 CorrectFilePaths 的最小集合
- 確認比對條件(檔案大小、檢查碼、版本等)並儲存
比對條件是「只套用到這個 EXE」的關鍵。預設基本條件通常就夠,但我們建議 留下能識別應用版本的條件。這樣可避免廠商日後出修正版時,舊謊言繼續套到新版本的事故。1415
flowchart TB
accTitle: 建立自訂相容性資料庫的步驟
accDescr: 在新資料庫建立 Application Fix,指定應用名稱與目標 EXE,先試相容模式捆綁再視需要收斂到個別 Shim,確認比對條件後儲存
new["建立新資料庫"] --> fix["選擇 Application Fix"]
fix --> info["指定應用名稱與目標 EXE"]
info --> layer["先試相容模式捆綁"]
layer --> single["必要時收斂到個別 Shim"]
single --> match["確認比對條件並儲存"]
match -.-> ver["留下能識別版本的條件"]
圖 11: Application Fix 先試相容模式捆綁,再收斂到最小集合,並用比對條件限定對象。
先在驗證機器上測試建立的 .sdb。確認依預期運作後,再推廣到組織。
6.3. 用 sdbinst 發佈
把自訂 .sdb 套用到各 PC 的命令是 sdbinst.exe(需要系統管理員權限)。15
:: Install (-q is silent with no confirmation)
sdbinst -q "C:\Deploy\MyCorpFixes.sdb"
:: Uninstall (by file)
sdbinst -q -u "C:\Deploy\MyCorpFixes.sdb"
:: Uninstall (by database GUID)
sdbinst -q -u -g {database GUID}
作為組織部署策略,Microsoft 建議 整合成一份全公司(或各部門)的自訂資料庫並集中管理,而不是隨每個應用的安裝程式各帶一份 .sdb。修正越多,更新並重新發佈一份資料庫,比發佈許多單行資料庫更容易。自訂資料庫有自己的 GUID,用相同 GUID 安裝新版本會自動取代舊版本,更新作業也因此單純。發佈本身請掛在能以系統管理員權限執行的既有路徑,例如包裝成 MSI 或啟動指令碼。15
flowchart TB
accTitle: 從建立自訂 .sdb 到發佈的路徑
accDescr: 在 Compatibility Administrator 建立自訂相容性資料庫,在驗證機器上測試,用 sdbinst 套用到各 PC,更新時以相同 GUID 安裝新版本,舊版本會自動被取代
make["在 Compatibility Administrator 建立"] --> test["在驗證機器上測試"]
test --> deploy["用 sdbinst 套用到各 PC"]
deploy --> update["以相同 GUID 安裝新版本"]
update -.-> replace["舊版本自動被取代"]
deploy -.-> inv["登錄在程式和功能"]
圖 12: 自訂 .sdb 經建立、驗證、sdbinst 發佈推廣,更新以 GUID 管理。
已安裝的自訂資料庫會登錄為「程式和功能(已安裝的應用)」中的項目,也可從那裡確認庫存與移除。哪些 PC 有哪些 .sdb,是屬於資產管理台帳的資訊。
7. 無效的情況與界限
Shim 不是萬靈丹。依設計,下列情況無效。
- 核心模式問題。 Shim 在使用者模式行程內執行,因此修不了裝置驅動程式不相容。舊量測儀器、USB 加密狗或印表機的驅動程式若不支援 Windows 11,在應用端套用什麼都解決不了。防毒軟體部分這類在核心執行的程式碼也一樣。3
- 16 位元應用。 64 位元 Windows 不支援執行 16 位元應用。控制代碼在 64 位元 Windows 上有 32 個有效位元,無法截斷後傳給 16 位元應用,因此啟動以
ERROR_BAD_EXE_FORMAT失敗。8 即使應用本身是 32 位元,那個時代套件裡也有 安裝程式啟動部分是 16 位元 的,會表現成「應用能跑,但裝不進去」。 - 直接硬體存取。 假設能直接碰 I/O 埠或實體記憶體的工業應用,在現代 Windows 的使用者模式本來就不被允許,超出 Shim 能偽裝的範圍。
- 繞過安全性機制。 因為 Shim 與應用受相同安全性約束,它無法讓「因權限不足做不到的事」變成可能。ForceAdminAccess 與 WRPMitigation 只是 偽裝檢查或寫入成功,讓應用能繼續,並不是真的改寫受保護資源。34
- 檢查自身完整性的應用。 帶有舊複製保護或竄改偵測的應用,可能把 API 攔截本身當成異常而停止運作。
flowchart TB
accTitle: Shim 無效的情況
accDescr: Shim 在使用者模式行程內執行,因此對核心模式驅動程式問題、16 位元應用、直接硬體存取、繞過安全性機制無效
shim["Shim(在使用者模式執行)"] -->|無效| drv["核心驅動程式"]
shim -->|無效| b16["16 位元應用"]
shim -->|無效| hw["直接硬體存取"]
shim -->|無效| sec["繞過安全性機制"]
b16 -.-> fmt["在 64 位元上啟動本身就失敗"]
sec -.-> fake["只偽裝成功讓應用繼續"]
圖 13: Shim 僅限使用者模式,到不了核心、16 位元應用、直接硬體存取或安全性繞過。
而所有 Shim 共通的本質界限是:它是權宜之計。Shim 是針對特定 API 特定用法量身打造的謊言,OS 端實作一變,前提就崩塌。Microsoft 提供的 Shim 經 Windows Update 作為 Windows 的一部分維護,3 但照顧你用自訂資料庫套上的謊言,是組織的工作。請把每次功能更新都驗證「靠 Shim 延長壽命的應用清單」這項作業,算進延長壽命成本。
flowchart TB
accTitle: 作為權宜之計的 Shim,以及誰負責維護
accDescr: Shim 是針對特定 API 用法的謊言,OS 端實作改變時前提會崩塌;Microsoft 提供的 Shim 經 Windows Update 維護,但自訂資料庫套上的謊言由組織照顧,每次功能更新的驗證是延長壽命成本
shim["Shim = 權宜之計的謊言"] --> break["OS 變更會弄壞它"]
ms["Microsoft Shim"] --> wu["經 Windows Update"]
own["自訂資料庫的謊言"] --> self["組織照顧"]
self --> cost["每次更新都驗證是延長壽命成本"]
圖 14: 維護 Shim 謊言的責任,分成 Microsoft 提供的集合,與組織自己的自訂集合。
8. RunAsInvoker 的實務價值 ── 只讓提高權限要求沉默
Shim 之中,日常 IT 工作最常出現的是 RunAsInvoker。
有些舊業務應用在資訊清單宣告 requireAdministrator,或因 EXE 名稱、內容被誤判成安裝程式,每次啟動都要求 UAC 提高權限。其中許多只是 XP 時代的慣性在要系統管理員,實際上並未使用系統管理員權限。套用 RunAsInvoker Shim 會覆寫安裝程式偵測與資訊清單,應用以從父行程繼承的權杖(= 標準使用者權限)啟動。9
flowchart TB
accTitle: RunAsInvoker 如何抑制提高權限要求
accDescr: 資訊清單的 requireAdministrator 宣告或被誤判成安裝程式會在啟動時引起 UAC 提高權限要求,套用 RunAsInvoker 會覆寫兩者,應用以從父行程繼承的權杖啟動
manifest["requireAdministrator 宣告"] --> shim{"套用了 RunAsInvoker?"}
detect["被誤判成安裝程式"] --> shim
shim -->|否| uac["每次啟動都要求 UAC 提高權限"]
shim -->|是| token["以父行程的權杖啟動"]
token -.-> limit["真正需要系統管理員的作業在應用內失敗"]
圖 15: RunAsInvoker 只覆寫提高權限要求的原因;權限並不會增加。
即使不在 Compatibility Administrator 建立 .sdb,也能用 __COMPAT_LAYER 環境變數暫時套用同一層。
:: Apply RunAsInvoker to child processes started from this command prompt
set __COMPAT_LAYER=RunAsInvoker
start "" "C:\LegacyApp\Gyomu.exe"
# In PowerShell
$env:__COMPAT_LAYER = 'RunAsInvoker'
Start-Process 'C:\LegacyApp\Gyomu.exe'
把這兩行做成批次檔並以捷徑發佈,就能避免把本機系統管理員權限交給標準使用者,IT 也不再每次為了 UAC 密碼被叫。這是符合最小權限、把防線加厚的相容技法。
flowchart TB
accTitle: 發佈 RunAsInvoker 批次檔的效果
accDescr: 把設定 RunAsInvoker 的兩行批次以捷徑發佈,就能避免把本機系統管理員權限交給標準使用者,IT 不再為 UAC 密碼被叫,作業也符合最小權限
bat["發佈兩行批次"] --> noadmin["可避免交出系統管理員權限"]
bat --> nocall["IT 不會為 UAC 被叫"]
noadmin --> lp["符合最小權限的作業"]
nocall --> lp
圖 16: 只發佈批次檔,就能同時減少交出系統管理員權限與被 UAC 叫來的次數。
注意事項也說清楚。
- 權限不會增加。 真正需要系統管理員權限的作業(寫入 HKLM、更新 Program Files 底下等)會在應用內出錯,或在條件符合時被 UAC 虛擬化重新導向到 VirtualStore。10 若儲存設定突然「不行了」,先懷疑虛擬化。
- 環境變數方式只套用到子行程。 永久套用請直接寫 Layers 機碼(索引標籤沒有 RUNASINVOKER 項目)或以 .sdb 發佈才可靠。
- 修正寫入目的地才是正途。 若能改應用,把設定檔移到
%APPDATA%底下,並在資訊清單宣告asInvoker,那才是正確形狀。9
flowchart TB
accTitle: RunAsInvoker 的暫時套用與永久套用
accDescr: 以 COMPAT_LAYER 環境變數套用只對從那裡啟動的子行程有效;永久套用請直接寫 Layers 機碼或以 .sdb 發佈
env["以環境變數設定"] --> child["只套用到子行程"]
child -.-> tmp["暫時套用"]
layers["直接寫 Layers 機碼"] --> always["永久套用"]
sdb["以 sdb 發佈"] --> always
圖 17: 環境變數方式是限於子行程的暫時套用;要永久化,靠 Layers 機碼或 .sdb。
9. 延長壽命與遷移的判斷 ── Shim 讓它能跑之後要想什麼
在 Shim 下能跑的那一刻是鬆一口氣,但重要的是不要在那裡停止思考。在 Shim 下能跑,只代表它碰巧對上 Windows 準備好的插座。 判斷軸整理成表。
| 判斷軸 | 傾向延長壽命(Shim)的條件 | 傾向遷移/重寫的條件 |
|---|---|---|
| 剩餘使用期間 | 計畫 1–2 年內隨業務退役 | 預設還要再用 5 年以上 |
| 原始碼 | 沒有(廠商消失,或遺失) | 存在,或資產可恢復 |
| 依賴深度 | 僅使用者模式 API 相容問題 | 依賴驅動程式、16 位元或專用硬體 |
| 替代方案 | 沒有套裝產品或新版本 | 目的地產品與技術清楚 |
| 失敗時的影響 | 業務可用備援程序運轉 | 核心業務直接受害 |
| 驗證能量 | 每次功能更新都能確認行為 | 沒有驗證資源,容易被凍結 |
若決定延長壽命,請把以下三點成套放進作業。
- 記錄。 哪個 EXE、哪個 Shim/層、為什麼。把 Layers 機碼值與 .sdb GUID 留在台帳。「沒人知道為什麼能跑」是留給下一個人的最大負債。這與「接手了沒有原始碼也沒有規格書的系統 ── 不停機維運的實務步驟」所談的保存心態相同。
- 驗證。 把靠 Shim 延長壽命的應用的啟動與主要操作,納入 Windows 功能更新的驗證項目。也與 OS 汰換計畫綁在一起(Windows 10支援結束後的現實解方 ── ESU、LTSC、換機的判斷表)。
- 訂期限。 決定延長壽命的終點——「到下次核心系統重整為止」、「到 2028 年 3 月」——並並行推進遷移評估。
flowchart TB
accTitle: 決定延長壽命後的三點作業組合
accDescr: 把讓它能跑的 Shim 記入台帳,每次功能更新都驗證靠 Shim 延長壽命的應用行為,訂下延長壽命終點的期限,並並行推進遷移評估
decide["決定延長壽命"] --> rec["記錄:哪個 Shim 讓它能跑,記入台帳"]
rec --> verify["驗證:每次功能更新確認行為"]
verify --> deadline["期限:決定延長壽命終點"]
deadline --> mig["並行推進遷移評估"]
圖 18: 延長壽命以記錄、驗證、期限三點成套運作,也包含並行推進遷移評估。
在遷移這一側,標準選項隨應用技術而變。VB6 是「VB6 應用程式能用到什麼時候 ── 執行環境的支援現況與務實的 .NET 遷移做法」整理的全面重寫、自動轉換、分階段遷移三選一;ActiveX/OCX 依賴則是「ActiveX / OCX 現在如何處理 - 保留・包裝・取代的判斷表」的保留/包裝/取代判斷表。把 Shim 放在健康位置的方式,是為那份遷移專案的評估與準備期安全運轉而買時間。
flowchart TB
accTitle: 遷移側的選項,以及 Shim 所在的位置
accDescr: 標準遷移選項隨應用技術而變;VB6 是重寫、自動轉換、分階段遷移三選一,ActiveX 依賴適用保留/包裝/取代判斷表,Shim 的定位是為遷移專案的評估與準備買時間
tech{"應用的技術是什麼?"} -->|VB6| vb["重寫、自動轉換或分階段遷移"]
tech -->|ActiveX 依賴| ax["保留、包裝或取代"]
shim["以 Shim 延長壽命"] -.->|為評估與準備買時間| tech
圖 19: 標準遷移選項由應用技術決定,Shim 的定位是為那份評估買時間。
10. 總結
- 相容模式的真正身分是 Shim。相容性索引標籤的設定寫入 AppCompatFlags\Layers 機碼,啟動時以改寫 IAT 的 API 攔截注入行程。
- Shim 是「回傳舊應用所期待答案的謊言」集合。版本偽裝、路徑重新對應、登錄偽裝、系統管理員檢查偽裝等現成 Shim 都有提供。
- Windows 自己預設就大量使用 Shim,PCA 也可能自動套用。依賴相容模式本身,是搭上 OS 正式機制的合理選擇。
- 原則上的界限是僅限使用者模式、不能繞過安全性,核心驅動程式、16 位元應用、直接硬體存取救不了。
- 組織推廣是在 Compatibility Administrator(Windows ADK)建立自訂 .sdb,再用 sdbinst 發佈。選擇 32 位元/64 位元版本、用實際使用者帳戶測試、以 GUID 管理更新,是實務重點。
- 「要求系統管理員但實際上不需要」的應用,可用
__COMPAT_LAYER=RunAsInvoker降到標準權限。這是讓提高權限要求沉默、而不是把權限交出去的防禦技法。 - 在 Shim 下能跑是延長壽命,不是解方。記錄是什麼讓它能跑、每次功能更新都驗證、訂期限讓遷移並行——這三點組合,包含在「依賴相容模式」這項決定裡。
下次舊應用因為相容模式核取方塊而開始能跑時,再問一次。「這個應用是靠哪一句謊言在跑?那句謊言還能有效多久?」 答得出來,延長壽命就是堂堂正正的策略。
相關文章
- 登錄檔的 32bit/64bit 重新導向與虛擬化的陷阱 ── Wow6432Node 與「明明寫入了卻讀不到值」的問題
- VB6 應用程式能用到什麼時候 ── 執行環境的支援現況與務實的 .NET 遷移做法
- ActiveX / OCX 現在如何處理 - 保留・包裝・取代的判斷表
- Windows 10支援結束後的現實解方 ── ESU、LTSC、換機的判斷表
- 接手了沒有原始碼也沒有規格書的系統 ── 不停機維運的實務步驟
- Windows 殼層整合的現在 ── 快捷功能表、檔案關聯,以及 Windows 11 的變化
相關諮詢領域
小村軟體有限公司處理沒有原始碼的舊業務應用行為調查與延長壽命設計(選定 Shim 與相容模式、建立並推廣自訂 .sdb)、為遷移到 Windows 11 所做的既有應用相容驗證,以及與延長壽命並行的重寫或遷移規劃。從「在相容模式下開始能跑了,但就這樣放著可以嗎?」這個階段來諮詢也沒問題。
參考連結
-
Microsoft Learn, Application Compatibility Database. 相容基礎建設以 .sdb 格式資料庫管理問題與處方、依可執行檔屬性比對、Apphelp(顯示訊息)與 Appfix(經 Shim 的 API 攔截),以及捆綁數個 Shim 與旗標的相容層(模式)。 ↩ ↩2 ↩3
-
Microsoft Learn, DXGI overview. 應用程式相容設定存在登錄機碼 HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers(以 DXGI 相容設定為例)。 ↩ ↩2
-
Microsoft Learn, Understanding and Using Compatibility Fixes. 相容修正(Shim)以改寫 IAT(匯入位址表)重新導向 API 呼叫、動態連結靠攔截 GetProcAddress 處理、Shim 受與應用相同的安全性約束而無法繞過 OS 安全性機制、僅限使用者模式而修不了驅動程式問題、Shim 能做的修正改程式碼也能做、廠商支援已結束的應用等使用情境,以及 Microsoft 提供的相容修正作為 Windows 一部分出貨並經 Windows Update 更新。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, Compatibility Fixes for Windows 10, Windows 8, Windows 7, and Windows Vista. 包含 CorrectFilePaths、VirtualRegistry、ForceAdminAccess、RunAsAdmin/RunAsHighest/RunAsInvoker、WRPMitigation、EmulateGetDiskFreeSpace、GlobalMemoryStatusLie、LoadLibraryRedirect 與 VersionLie 系列等已知相容修正的清單與說明、Compatibility Administrator 的 32 位元/64 位元版本選擇,以及在已提高權限狀態下測試會讓虛擬化與重新導向行為不如預期,因此應以實際使用者帳戶驗證。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Program Compatibility Assistant scenarios for Windows 8. PCA 監視應用執行、偵測已知相容問題跡象並建議套用建議修正或自動套用(PINDLL、DISABLEUSERCALLBACKEXCEPTION、VIRTUALIZEDELETE、WRPMITIGATION 等),以及從相容性索引標籤與 Program Compatibility Troubleshooter 套用修正。 ↩ ↩2
-
Microsoft Learn, GetVersionExW function. 從 Windows 8.1 起 GetVersionEx 回傳值取決於資訊清單、未針對 Windows 8.1/10 做資訊清單的應用會拿到 Windows 8 版本值(6.2),以及啟用相容模式時會回報所選 OS 的版本。 ↩ ↩2 ↩3
-
Microsoft Learn, Targeting your application for Windows. 在應用資訊清單 compatibility 區段以 supportedOS 元素宣告支援 OS GUID 的方法、沒有宣告時的行為,以及不含 trustInfo 的 32 位元 x86 應用會成為 UAC 檔案虛擬化(寫入重新導向到 VirtualStore)的對象。 ↩ ↩2
-
Microsoft Learn, Running 32-bit Applications. WOW64 是在 64 位元 Windows 上執行 32 位元應用並隔離檔案與登錄衝突的模擬層,以及 64 位元 Windows 不支援執行 16 位元應用,因控制代碼有效位元數而以 ERROR_BAD_EXE_FORMAT 啟動失敗。 ↩ ↩2
-
Microsoft Learn, Using the RunAsInvoker Fix. RunAsInvoker 相容修正以從父行程繼承的權杖啟動應用、覆寫安裝程式偵測與資訊清單處理、以載入器旗標套用而不攔截 API,以及能修正程式碼時正確修法是在資訊清單宣告 asInvoker。 ↩ ↩2 ↩3
-
Microsoft Learn, Registry Virtualization. 登錄虛擬化是把對 HKLM\Software 的全域寫入透明重新導向到每位使用者 VirtualStore 的相容技術、範圍僅限 32 位元互動行程且對在資訊清單指定 requestedExecutionLevel 的行程與 64 位元行程停用,以及被定位為打算從未來 Windows 移除的暫時技術。 ↩ ↩2
-
Microsoft Learn, High DPI Desktop Application Development on Windows. 未感知 DPI 的應用被視為以固定 96 DPI 繪製,在高 DPI 顯示器上 Windows 拉伸點陣圖因而看起來模糊,以及 DPI 感知模式(Unaware/System/Per-Monitor)的差異。 ↩ ↩2
-
Microsoft Learn, Download and install the Windows ADK. Windows ADK 包含 Compatibility Administrator 與 Standard User Analyzer,以及選擇 ADK 版本的想法與下載安裝方法。 ↩
-
Microsoft Learn, Compatibility Administrator User’s Guide. Compatibility Administrator 提供套用相容修正、相容模式與 AppHelp 訊息以及建立自訂資料庫的功能,且同時安裝 32 位元與 64 位元版本,32 位元應用必須用 32 位元版、64 位元應用必須用 64 位元版。 ↩
-
Microsoft Learn, Creating a Custom Compatibility Fix in Compatibility Administrator. 相容修正(舊稱 Shim)是攔截 API 呼叫的一小段程式碼、在自訂資料庫建立 Application Fix 的步驟(指定應用名稱、廠商與目標 EXE、選擇相容模式、選擇額外 Shim、設定比對條件),以及應留下能正確識別應用同時縮小比對資訊的條件。 ↩ ↩2
-
Microsoft Learn, Compatibility Fix Database Management Strategies and Deployment. 自訂相容性資料庫的管理策略建議採集中管理資料庫、相容修正應包含版本檢查(比對條件)以免套用到新版本、以 Sdbinst.exe 本機安裝(-q、-u、-g 選項)、以相同資料庫 GUID 安裝新版本會自動解除安裝舊版本,以及經 MSI 或指令碼發佈的方法。 ↩ ↩2 ↩3
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
Win32 執行緒集區 API ── 用 CreateThreadpoolWork 做「不自己建立執行緒」的並行
原生程式碼裡到處呼叫 CreateThread 嗎?本文依一次資訊解說 Vista 重新設計的 Win32 執行緒集區 API──work、timer、wait、io 四種物件、清理群組,以及回呼裡不該做的事。
具名管道實務 ── 從設計到安全性,整理 Windows 的標準 IPC
以實務角度解說 Windows 標準行程間通訊──具名管道。從一次資訊整理位元組/訊息模式的選擇、同時服務多個用戶端的伺服器設計、ACL 與偽裝的安全性,以及 .NET 的具名管道串流。
從睡眠恢復就壞掉的應用程式 ── Windows 電源事件的機制,以及耐得住恢復的業務應用寫法
打開筆電,業務應用程式的連線卻斷了──原因是設計從未把睡眠算進去。本文依一次資訊整理 WM_POWERBROADCAST 的通知流程、Modern Standby 的行為、斷線/重連設計、睡眠抑制,以及調查指令。
DllMain 與載入器鎖定 ── 「DLL 初始化什麼都別做」真正的理由
為什麼不能從 DllMain 呼叫 LoadLibrary,也不能和其他執行緒同步。本文依一次資訊說明載入器鎖定如何序列化每個 DLL 通知、結構上必定成立的死結場景、延遲初始化的正確設計,以及無回應的調查方式。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
既有資產活用 & 遷移支援
在持續活用 COM / ActiveX / OCX 資產、原生程式碼與 32 位元相依的同時,協助規劃階段性的遷移。
常見問題
整理諮詢這個主題時常見的問題。
- 勾選相容模式後應用就能跑了,這樣繼續用可以嗎?
- 就短期維持業務運轉而言,可以。相容模式的本質是稱為 Shim 的使用者模式 API 攔截,是作業系統正式提供的機制。不過 Shim 仍是「不修正應用也能跑」的權宜之計,另一次 OS 更新可能改變前提、再次讓它失效。請把「靠相容模式才能跑」這件事記入台帳,並把這份紀錄納入「重寫應用」或「有計畫地延長壽命」的判斷材料。
- 相容模式的核取方塊實際上做了什麼?
- 在內容對話方塊的相容性索引標籤儲存設定時,Windows 會把目標 EXE 路徑與「WINXPSP3」「HIGHDPIAWARE」這類值寫入 HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers 機碼。下次啟動該 EXE 時,Windows 載入器讀取這個值,並把對應的相容層(一組 Shim)套用到行程。例如 Windows XP 相容模式會讓查詢版本的 API 回傳舊值,藉此偽裝 OS 版本。作業系統本身沒有被改寫,只是對那個行程假裝成較舊的 Windows。
- 16 位元時代的應用能在 64 位元 Windows 的相容模式下執行嗎?
- 不能。64 位元 Windows 透過 WOW64 執行 32 位元應用,但不支援執行 16 位元應用,嘗試啟動會以 ERROR_BAD_EXE_FORMAT 失敗。這是 Shim 無法繞過的架構限制。安裝程式啟動部分是 16 位元的舊套件也會因同樣理由失敗。真的需要的話,必須看相容模式以外的手段,例如包含 32 位元 Windows 的虛擬機器。
- 「不以系統管理員執行就無法啟動」的應用,能用標準使用者帳戶跑嗎?
- 值得一試 RunAsInvoker。在命令提示字元執行 set __COMPAT_LAYER=RunAsInvoker 再啟動應用,就能抑制 requireAdministrator 資訊清單或安裝程式偵測所引起的提高權限要求,應用會以與呼叫端相同(標準使用者)的權限啟動。若應用只是要求系統管理員、實際上並未使用,單靠這一步就能把提高權限從日常作業拿掉。權限並不會增加,真正需要系統管理員的作業仍會在應用內失敗。請先確認行為再採用。
- Compatibility Administrator 要從哪裡取得?
- 它包含在 Windows ADK(Windows Assessment and Deployment Kit)裡。從 Microsoft 網站下載 ADK,安裝時選取 Application Compatibility Tools 功能即可。32 位元與 64 位元版本都會安裝;32 位元應用必須用 32 位元版,64 位元應用必須用 64 位元版。建立的自訂相容性資料庫(.sdb)在各 PC 上以 sdbinst 命令套用。