delphimanifestwinsxsotatoolsapi

Uncheck "Enable Runtime Themes" or remove the internal manifest in Delphi XE?


I have a component that I am building in Delphi XE that I want to be used in the following way:

  1. User creates a new blank project.

  2. User drops my component on the form.

  3. Some special Designtime code in my component is executed, that will change Project Options to uncheck the "Enable runtime themes" checkbox in the project options. I am not sure this is even possible, so I'm asking if it's possible.

If #3 is not possible, then I need another solution to my "usability" problem with this component; The problem I have is that if users do not disable the statically linked manifest file by unchecking Enable Runtime Themes, then that statically generated manifest that is linked into the EXE seems to override the external manifest files that I want to have outside the EXE, on disk. I also need to modify these manifests at runtime, thus the need for external manifests. I can of course, enable the Runtime Theme functionality using these manifests, when it is desirable to do so. A secondary question is about the priority of external and internal manifests; Can an external manifest somehow take priority over the internal manifest resource that is linked into Delphi apps when you check "Enable Runtime Themes"?

Acceptable solutions other than #3:

A. Somehow cause Delphi to not generate a manifest. B. Somehow at runtime, have Windows recognize and prioritize external .manifest files even when an internal one is found.

C. Least good solution; At runtime, after CoCreateInstance in my component fails, I can enumerate resources, report that an external manifest is present and is messing us up, and rely on developers who use my component reading the runtime error messages my component spits out, telling them to disable runtime themes checkbox and rebuild their app. Extracting and reading a manifest is already covered in another stackoverflow question here, with C++ code that could easily be converted to Delphi.

Update The accepted answer does exactly what I asked, but is considered a hack, and David's answer about Activation Contexts, is much more sane, and is the Recommended Approach.

Update2 The built-in manifest is normally overridden in later versions of Delphi (XE5 and later) by specifying explicitly which manifest you want to link, via the project settings.


Solution

  • I think I've found a working solution for what you asked for, ie. to disable the runtime themes from project options when an instance of your component is created (dropped on a form, or a form/module containing an instance of it is opened in the IDE). This doesn't prevent the user from re-enabling the runtime themes manually later but maybe it's still useful to you.

    BTW, IOTAProjectOptions doesn't seem to help in this case; it looks like IOTAProjectResource is needed.

    TestComponentU.pas (part of the runtime package):

    unit TestComponentU;
    
    interface
    
    uses
      Windows, Classes;
    
    type
      ITestComponentDesign = interface
        function DisableRuntimeThemes: Boolean;
      end;
    
      TTestComponent = class(TComponent)
      public
        constructor Create(AOwner: TComponent); override;
      end;
    
    var
      TestComponentDesign: ITestComponentDesign = nil;
    
    implementation
    
    uses
      Dialogs;
    
    constructor TTestComponent.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      if (csDesigning in ComponentState) and Assigned(TestComponentDesign) and
        TestComponentDesign.DisableRuntimeThemes then
        ShowMessage('Project runtime themes disabled');
    end;
    
    end.
    

    TestComponentRegU.pas (part of the design package installed in the IDE):

    unit TestComponentRegU;
    
    interface
    
    procedure Register;
    
    implementation
    
    uses
      Windows, Classes, SysUtils, TestComponentU, ToolsAPI;
    
    type
      TTestComponentDesign = class(TInterfacedObject, ITestComponentDesign)
      public
        function DisableRuntimeThemes: Boolean;
      end;
    
    procedure Register;
    begin
      RegisterComponents('Test', [TTestComponent]);
    end;
    
    function GetProjectResource(const Project: IOTAProject): IOTAProjectResource;
    var
      I: Integer;
    begin
      Result := nil;
      if not Assigned(Project) then
        Exit;
    
      for I := 0 to Project.ModuleFileCount - 1 do
        if Supports(Project.ModuleFileEditors[I], IOTAProjectResource, Result) then
          Break;
    end;
    
    function GetProjectResourceHandle(const ProjectResource: IOTAProjectResource; ResType, ResName: PChar): TOTAHandle;
    var
      I: Integer;
      ResEntry: IOTAResourceEntry;
    begin
      Result := nil;
      if not Assigned(ProjectResource) then
        Exit;
    
      for I := 0 to ProjectResource.GetEntryCount - 1 do
      begin
        ResEntry := ProjectResource.GetEntry(I);
        if Assigned(ResEntry) and (ResEntry.GetResourceType = ResType) and (ResEntry.GetResourceName = ResName) then
        begin
          Result := ResEntry.GetEntryHandle;
          Break;
        end;
      end;
    end;
    
    function DisableProjectRuntimeThemes(const Project: IOTAProject): Boolean;
    var
      ProjectResource: IOTAProjectResource;
      ResHandle: TOTAHandle;
    begin
      Result := False;
      ProjectResource := GetProjectResource(Project);
      if not Assigned(ProjectResource) then
        Exit;
    
      ResHandle := GetProjectResourceHandle(ProjectResource, RT_MANIFEST, CREATEPROCESS_MANIFEST_RESOURCE_ID);
      if Assigned(ResHandle) then
      begin
        ProjectResource.DeleteEntry(ResHandle);
        Result := True;
      end;
    end;
    
    function TTestComponentDesign.DisableRuntimeThemes: Boolean;
    var
      Project: IOTAProject;
    begin
      Project := GetActiveProject;
      Result := Assigned(Project) and DisableProjectRuntimeThemes(Project);
    end;
    
    initialization
      TestComponentDesign := TTestComponentDesign.Create;
    
    finalization
      TestComponentDesign := nil;
    
    end.