Apply Windows Theme to Office Com add-in

后端 未结 1 1952
遇见更好的自我
遇见更好的自我 2020-12-17 02:27

For ages, Delphi has supported the Enable runtime themes switch on the Application Settings tab. However, this only works for executables. DLLs are assumed to take

相关标签:
1条回答
  • 2020-12-17 02:49

    I've done this for my COM add-in. I used activation contexts. It's pretty easy for a COM add-in because the surface area of the add-in interface is so small. I could post code but I won't be at a machine with it on until tomorrow. Hope this helps!


    UPDATE

    As promised, here is the code that I use:

    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;
    

    When you want to use this, you simply write code like so:

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

    You need each entry point that does GUI work to be wrapped in such code.

    Note that in my COM add-in DLL I have taken special measures to avoid running code during DLLMain, and so my calls to InitialiseActivationContext and FinaliseActivationContext are not in unit initialization/finalization sections. However, I see no reason why this code would not be safe to place there.

    0 讨论(0)
提交回复
热议问题