2011-02-27 56 views
6

多年以來,Delphi支持啓用運行時主題開關在應用程序設置選項卡上。但是,這隻適用於可執行文件。假定DLLs接管來自其父應用程序的主題(和其他)設置。將Windows主題應用於Office Com插件

不幸的是,Microsoft Office在那裏播放不好。他們的'主題'外觀是通過自定義控件實現的,而不是通過Windows自己的通用控件。

在MSDN文章830033 - How to apply Windows XP themes to Office COM add-ins 中,Microsoft解釋瞭如何將清單應用於DLL,使其隔離感知使得忽略來自父進程的設置。

基本上可以歸結爲兩個步驟:

  1. 包括默認的清單資源在你的過程中,採用的2的int-資源ID(而不是你通常使用1)。
  2. 使用ISOLATION_AWARE_ENABLED定義進行編譯。 **這是不是在德爾福提供。**

我想我有(1)確定下來,雖然我從來沒有很肯定brcc32是否拿起資源ID爲整數或文本字符串。真正的問題在於(2)。據說,這定義了更改幾個DLL函數綁定。

有沒有人在Delphi中解決了這個問題?我是否應該進一步調查此路線,是否應該嘗試手動創建激活環境,或者是否有其他優雅的解決方案來解決此問題?

+0

我不知道任何優雅的解決方案。在WinSDK頭文件中快速瀏覽顯示ActivateActCtx和DeactivateActCtx似乎很重要 - 它們在加載CommCtrl API函數時將調用包裝爲LoadLibrary和GetProcAddress。 WinSDK頭文件包含所有的代碼AFAICS,但由於它們似乎已被重命名以避免衝突(例如IsolationAwarePrivatezltRgCebPnQQeRff,CommctrlIsolationAwarePrivatetRgCebPnQQeRff_pbZPgYQP_QYY等),因此需要進行一些反混淆處理。 – 2011-02-27 11:31:15

+0

@Barry:謝謝您的探索!我是否正確地假設Delphi RTL/VCL沒有使用激活上下文_anything_,並完全依賴於生成的清單? – 2011-02-27 13:11:49

+0

我沒有任何特別的知識,我只是看着SDK標題來幫助提供線索。 – 2011-02-27 16:36:01

回答

8

我已經完成了我的COM加載項。我使用了激活上下文。 COM插件非常容易,因爲插件接口的表面區域非常小。我可以發佈代碼,但明天之前我不會在機器上使用它。希望這可以幫助!


UPDATE

正如所承諾的,這裏是我使用的代碼:

type 
    (* TActivationContext is a loose wrapper around the Windows Activation Context API and can be used 
    to ensure that comctl32 v6 and visual styles are available for UI elements created from a DLL .*) 
    TActivationContext = class 
    private 
    FCookie: LongWord; 
    FSucceeded: Boolean; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    end; 

var 
    ActCtxHandle: THandle=INVALID_HANDLE_VALUE; 
    CreateActCtx: function(var pActCtx: TActCtx): THandle; stdcall; 
    ActivateActCtx: function(hActCtx: THandle; var lpCookie: LongWord): BOOL; stdcall; 
    DeactivateActCtx: function(dwFlags: DWORD; ulCookie: LongWord): BOOL; stdcall; 
    ReleaseActCtx: procedure(hActCtx: THandle); stdcall; 

constructor TActivationContext.Create; 
begin 
    inherited; 
    FSucceeded := (ActCtxHandle<>INVALID_HANDLE_VALUE) and ActivateActCtx(ActCtxHandle, FCookie); 
end; 

destructor TActivationContext.Destroy; 
begin 
    if FSucceeded then begin 
    DeactivateActCtx(0, FCookie); 
    end; 
    inherited; 
end; 

procedure InitialiseActivationContext; 
var 
    ActCtx: TActCtx; 
    hKernel32: HMODULE; 
begin 
    if IsLibrary then begin 
    hKernel32 := GetModuleHandle(kernel32); 
    CreateActCtx := GetProcAddress(hKernel32, 'CreateActCtxW'); 
    if Assigned(CreateActCtx) then begin 
     ReleaseActCtx := GetProcAddress(hKernel32, 'ReleaseActCtx'); 
     ActivateActCtx := GetProcAddress(hKernel32, 'ActivateActCtx'); 
     DeactivateActCtx := GetProcAddress(hKernel32, 'DeactivateActCtx'); 
     ZeroMemory(@ActCtx, SizeOf(ActCtx)); 
     ActCtx.cbSize := SizeOf(ActCtx); 
     ActCtx.dwFlags := ACTCTX_FLAG_RESOURCE_NAME_VALID or ACTCTX_FLAG_HMODULE_VALID; 
     ActCtx.lpResourceName := MakeIntResource(2);//ID of manifest resource in isolation aware DLL 
     ActCtx.hModule := HInstance; 
     ActCtxHandle := CreateActCtx(ActCtx); 
    end; 
    end; 
end; 

procedure FinaliseActivationContext; 
begin 
    if ActCtxHandle<>INVALID_HANDLE_VALUE then begin 
    ReleaseActCtx(ActCtxHandle); 
    end; 
end; 

initialization 
    InitialiseActivationContext; 

finalization 
    FinaliseActivationContext; 

當你想使用它,您只需編寫代碼,像這樣:

var 
    ActivationContext: TActivationContext; 
.... 
ActivationContext := TActivationContext.Create; 
try 
    //GUI code in here will support XP themes 
finally 
    ActivationContext.Free; 
end; 

您需要每個進行GUI工作的入口點進行包裝d在這樣的代碼中。

請注意,在我的COM加載項DLL中,我採取了特殊措施以避免在DLLMain期間運行代碼,因此我對InitialiseActivationContextFinaliseActivationContext的調用不在單元初始化/結束部分。但是,我沒有看到爲什麼這個代碼不安全放置在那裏。

+0

感謝David,對於確認激活上下文是一個可行的解決方案。將進一步研究。我對你對總表面積的評論有點困惑,這是否會影響工作量?如:更多的控件,需要激發更多的激活上下文? – 2011-02-27 12:44:19

+0

當您輸入DLL並在離開時停用時,您需要激活。但是,如果你想讓我發佈示例代碼,但是我無法在24小時內完成,請告知我。 – 2011-02-27 13:04:22

+0

啊,我傻了,我完全誤讀你在那裏說什麼。隨着我的思緒仍然掛在主題上,我閱讀界面==用戶界面,事情從那裏開始下降。謝謝你澄清! :) – 2011-02-27 18:09:40