将Windows主题应用于Office Com加载项

Pau*_*Jan 6 delphi dll theming ms-office

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

不幸的是,Microsoft Office在那里玩得不好.他们的"主题"外观是使用自定义控件实现的,而不是通过Windows自己的通用控件实现的.

在MSDN文章830033中 - 如何将Windows XP主题应用于Office COM加载项 Microsoft解释了如何将清单应用于DLL,使其成为隔离感知,以便忽略来自父进程的设置.

基本上,它分为两个步骤:

  1. 使用int-resource id为2(而不是通常使用的1),在进程中包含缺省清单资源.
  2. 使用ISOLATION_AWARE_ENABLED定义进行编译.**Delphi中没有.**

我认为我已经确定了(1)已经确定了,尽管我不确定brcc32是将整个资源ID作为整数还是作为文字字符串.真正的问题在于(2).据推测,这个定义改变了几个DLL函数绑定.

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

Dav*_*nan 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;
Run Code Online (Sandbox Code Playgroud)

当你想使用它时,你只需编写如下代码:

var
  ActivationContext: TActivationContext;
....
ActivationContext := TActivationContext.Create;
try
  //GUI code in here will support XP themes
finally
  ActivationContext.Free;
end;
Run Code Online (Sandbox Code Playgroud)

您需要将GUI工作的每个入口点包含在此类代码中.

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