Применить тему Windows к надстройке Office Com - PullRequest
6 голосов
/ 27 февраля 2011

Веками Delphi поддерживал переключатель Включить темы выполнения на вкладке «Настройки приложения».Однако это работает только для исполняемых файлов.Предполагается, что библиотеки DLL принимают на себя настройки тем и (и других) из родительского приложения.

К сожалению, Microsoft Office там не очень хорошо работает.Их «тематический» вид достигается с помощью пользовательских элементов управления, а не с помощью собственных общих элементов управления Windows.

В статье MSDN 830033 - Как применять темы Windows XP к надстройкам Office COM Microsoftобъясняет, как применить манифест к DLL, сделав его Isolation Aware таким образом, чтобы параметры родительского процесса игнорировались.

По сути, это сводится к двум шагам:

  1. Включите ресурс манифеста по умолчанию в ваш процесс, используя внутренний идентификатор ресурса, равный 2 (в отличие от 1, который выобычно используется).
  2. Скомпилируйте с определением ISOLATION_AWARE_ENABLED.** Что недоступно в Delphi. **

Я думаю, что я (1) прибил, хотя я никогда не совсем уверен, выбирает ли brcc32 идентификаторы ресурсов как целые числа или какбуквальные строки.Реальная проблема заключается в (2).Предположительно, это определение изменяет несколько привязок функций DLL.

Кто-нибудь решил эту проблему в Delphi?Стоит ли мне дополнительно исследовать этот маршрут, попытаться ли вручную создать контексты активации, или есть другие элегантные решения этой проблемы?

1 Ответ

8 голосов
/ 27 февраля 2011

Я сделал это для моей надстройки 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;

Вам нужно, чтобы каждая точка входа, которая работает с графическим интерфейсом, была заключена в такой код.

Обратите внимание, что в моей DLL-надстройке COM я предпринял особые меры, чтобы избежать запуска кода во время DLLMain, и поэтому мои вызовы InitialiseActivationContext и FinaliseActivationContext не находятся в разделах инициализации / завершения модуля. Однако я не вижу причин, по которым этот код было бы небезопасно размещать там.

...