Windows下的動態鏈接庫可能存在多個版本,比如系統提供的基本控件所在的Comctl32.dll 就包括版本6.0和之前的版本。在新版本之前編寫的程序能否保證絕對和新版本的動態鏈接庫兼容呢?只能說,一般情況下可以,但是不能絕對保證。 Windows提供了解決辦法,那就是設定程序執行的Active Context。對於客戶自己編寫的動態鏈接庫,同樣也存在這樣的問題。這裡說的是動態鏈接庫,如果考慮動態鏈接庫的其他應用,就涉及到更廣闊的應用范圍 了,可能是普通的動態鏈接庫,可能是COM服務器等,這些都是可以通過配置程序執行環境來改變具體使用哪個動態鏈接庫。
配置程序執行上下文,實際就是配置程序執行的組件配置,這裡的組件只要是指動態鏈接庫。對組件進行配置,簡單來說,就是配置程序使用什麼動態鏈接庫。這種 配置具體是怎麼實現的呢?其實就是在Windows程序真正啟動之前增加了一層處理。這層處理就是把程序執行所需要的環境先建立好,直接點說就是把需要的 動態鏈接庫准備好。
上面說到,配置組件就是在程序真正執行之前准備好需要的動態鏈接庫,這項工作可以由Windows系統的Module Loader或者應用程序自己完成。具體的實現方式有三種:
1.文件級的配置:在應用程序所在的目錄下創建一個.exe.manifest文件,這個文件中說明組件配置的具體信息,具體的信息格式可以查詢MSDN。在VS2003及以上的版本,新建工程時,會自動生成一個manifest文件在res目錄下。
2.資源級的配置:創建資源,類型:RT_MANIFEST(值為24),ID:CREATEPROCESS_
MANIFEST_RESOURCE_ID(值 為1),內容為上面的manifest文件的內容。Module Loader在加載EXE之後,執行程序之前,會檢測到程序的資源表中是否有類型為24,ID為1的資源,如果有這項配置信息,就會按照配置信息中的配 置,加載對應的動態鏈接庫了。
3.資源和代碼混合的配置:首先也需要添加資源,資源類型RT_MANIFEST(值為24),ID為 ISOLATIONAWARE_MANIFEST_RESOURCE_ID(值為2),內容也為上述符合格式的manifest文件;然後再 在#include之前增加一句
#define ISOLATION_AWARE_ENABLED 1。這樣編譯器在編譯時,會把一些API重定向到另外一個API,這個API先進行當成程序Active Context的檢測,如果程序執行Context沒有配置成資源文件所描述,會調用相應的API配置好Context,然後再從目的API所在的動態鏈 接庫中獲得函數地址(GetProcAddress)後,通過函數指針調用目標API,最後執行完畢後恢復執行環境。這可以保證在系統Module Loader沒有程序Active Context支持時,配置好程序執行環境。但是采用這種方法時需要注意,這是一種代碼級的支持,所以對MFC/ATL並不支持,因為MFC/ATL庫編 譯時,並不是在ISOLATION_AWARE_ENABLED已經定義的情況下編譯的。
4.代碼級的配置:除了以上幾種方法之外,你還可以通過調用相應的API,自己通過代碼來控制當前的執行環境,還可以做到一段代碼調用這個版本動態鏈接庫 中的代碼,另外一段代碼調用另外一個版本的動態鏈接庫代碼。程序執行Context是線程有效的。這可能在什麼情況下有應用呢?比如說,你要開發某個應用 程序的插件,你的插件依賴與系統提供的某個版本的動態鏈接庫,但是應用程序在不斷的升級,不能保證應用程序的執行環境始終與最初發布版本一致。這時你就可 以自己通過代碼來限定執行環境了。需要注意的是,如果程序已經采用了第三種方式進行執行環境的管理了,你就不能使用代碼級的環境管理了。
前面說的都是程序執行Context,實際准確的說應該是模塊執行Context。對於動態鏈接庫,同樣可以進行類似的配置。這些配置都有相應的API及manifest文件格式,詳見MSDN。
上面說了這麼多,下面我們來做幾個簡單的試驗吧。
1.使用VS2003,新建一個MFC對話框程序,隨便拖幾個控件進去,編譯。到Debug目錄下運行,你會發現界面並不是XP風格的。在Res目錄下, 你會發現一個manifest文件,這個manifest文件是VC自動生成的,裡面包含使用XP風格控件動態鏈接庫的信息。把這個動態鏈接庫拷貝到 Debug目錄下,並重命名為.exe.manifest,再運行程序,程序界面是否是XP風格了?這是第一種方式的實現。
2.新建一個Win32工程,在about對話框中隨便添加幾個控件,編譯運行,對話框中的控件不是XP風格的。添加ID為1的RT_MANIFEST類 型資源,內容為例1中的manifest文件內容,再編譯運行,可以看見對話框中控件為XP風格。這是第二種方式的實現。
3.把上述工程中的RT_MANIFEST資源ID修改為2,然後在stdafx.h中#include之前增加#define ISOLATION_AWARE_ENABLED 1,編譯運行,對話框控件也是XP風格的。這是第三種方式的實現。
4.在上述最原始Win32工程中,把WinMain函數修改如下:
ACTCTX actctx;
HANDLE hActCtx = INVALID_HANDLE_VALUE;
static const TCHAR szManifest[] = TEXT("Win32.manifest");
ULONG_PTR ulpActivationCookIE;
DWord dwLastError;
// Set up the activation context structure
memset(&actctx, 0, sizeof(actctx));
actctx.cbSize = sizeof(actctx);
actctx.lpSource = szManifest;
// Create the activation context, then delete the string - we don''t need it
// anymore.
hActCtx = CreateActCtx(&actctx);
dwLastError = GetLastError();
// Did we fail creating the activation context?
if (
hActCtx == INVALID_HANDLE_VALUE)
...{
SetLastError(dwLastError); // Operator delete[] may have cleared it
return 0;
}
// Activate the context and make use of it
if (ActivateActCtx(hActCtx, &ulpActivationCookIE))
...{
……
之前WinMian函數的代碼
……
// Use SearchPath to find DLLs mentioned in the application''s manifest,
// use COM to find isolated components, or use CreateWindow to find isolated
// window classes.
if (!DeactivateActCtx(0, ulpActivationCookIE))
return 0;;
}
ReleaseActCtx(hActCtx);
保證增加以上代碼後可以編譯通過,同時保證EXE所在目錄下有Win32.manifest文件,內容為之前的XP主題配置信息。運行程序,會看見控件為XP主題,注釋掉ActivateActCtx,控件不再是XP主題。
以上的例子主要是如何實現XP主題,但是程序執行環境的配置不僅僅實現XP主題,還有很多更廣闊的應用。