【问题标题】:Apply Windows Theme to Office Com add-in将 Windows 主题应用到 Office Com 加载项
【发布时间】:2011-07-05 04:44:31
【问题描述】:

多年来,Delphi 一直支持应用程序设置选项卡上的启用运行时主题开关。但是,这仅适用于可执行文件。假定 DLL 会从其父应用程序中接管主题(和其他)设置。

不幸的是,Microsoft Office 在那里表现不佳。它们的“主题”外观是使用自定义控件实现的,而不是通过 Windows 自己的通用控件。

在 MSDN 文章 830033 - How to apply Windows XP themes to Office COM add-ins Microsoft 中解释了如何将清单应用到 DLL,使其隔离感知,从而忽略来自父进程的设置。

基本上,它归结为两个步骤:

  1. 在您的进程中包含默认清单资源,使用 int-resource id 2(与您通常使用的 1 相反)。
  2. 使用 ISOLATION_AWARE_ENABLED 定义进行编译。 **这在 Delphi 中不可用。**

我认为我已经确定了 (1),尽管我不太确定 brcc32 是否将资源 ID 作为整数或文字字符串来获取。真正的问题在于(2)。据说,这个定义改变了几个 DLL 函数绑定。

有人在 Delphi 中解决过这个问题吗?我应该进一步调查这条路线,我应该尝试手动创建激活上下文,还是有其他优雅的解决方案来解决这个问题?

【问题讨论】:

  • 我不知道任何优雅的解决方案。快速浏览 WinSDK 标头显示 ActivateActCtx 和 DeactivateActCtx 似乎很重要 - 它们在加载 CommCtrl API 函数时包装了对 LoadLibrary 和 GetProcAddress 的调用。 WinSDK 标头包含所有代码 AFAICS,但需要进行一些反混淆,因为它们似乎已被重命名以避免冲突(例如 IsolationAwarePrivatezltRgCebPnQQeRff、CommctrlIsolationAwarePrivatetRgCebPnQQeRff_pbZPgYQP_QYY 等)
  • @Barry:感谢您的探索!我是否正确假设 Delphi RTL/VCL 没有对激活上下文做任何事情,并且完全依赖于生成的清单?
  • 我没有任何特别的知识,我只是查看了 SDK 标头以帮助提供线索。

标签: delphi dll ms-office theming


【解决方案1】:

我已经为我的 COM 加载项完成了这项工作。我使用了激活上下文。 COM 加载项非常容易,因为加载项接口的表面积非常小。我可以发布代码,但直到明天我才会在机器上。希望这会有所帮助!


更新

按照承诺,这是我使用的代码:

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 工作的每个入口点包装在此类代码中。

请注意,在我的 COM 加载项 DLL 中,我采取了特殊措施来避免在 DLLMain 期间运行代码,因此我对 InitialiseActivationContextFinaliseActivationContext 的调用不在单元初始化/完成部分中。但是,我认为没有理由将这段代码放在那里不安全。

【讨论】:

  • 感谢 David,确认激活上下文是一个可行的解决方案。将进一步调查。你对总表面积的评论让我有点困惑,这会影响所涉及的工作量吗?比如:更多的控制,更多的激活上下文需要被启动?
  • 进入DLL时需要激活,离开时需要去激活。但是您的 COM 加载项的入口点很少。如果您希望我发布示例代码但我无法在 24 小时内发布,请告诉我。
  • 啊,我真傻,我完全误读了你在那儿说的话。由于我的注意力仍然停留在主题上,我阅读了界面 == UI,事情从那里开始走下坡路。感谢您清除它! :)
  • @Paul-Jan 我现在添加了一些代码,希望对您有所帮助,尽管您可能已经掌握了它!
  • 太棒了,谢谢分享。今天有这么多杂项项目管理的东西,我没有接触任何代码(星期一......你知道它是怎么回事:P),所以它及时成为一个巨大的推动力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-31
相关资源
最近更新 更多