在Delphi XE中取消勾选“启用运行时主题”或删除内部清单?

9
我正在使用Delphi XE构建一个组件,希望它能以以下方式使用:
1. 用户创建一个新的空项目。 2. 用户将我的组件拖放到表单上。 3. 我的组件中执行一些特殊的设计时代码,将更改项目选项以取消选中“启用运行时主题”复选框。我不确定这是否可能,因此我想知道是否可能。
如果#3不可行,则需要另一种解决方案来解决此组件的“可用性”问题。我遇到的问题是,如果用户不通过取消选中“启用运行时主题”来禁用静态链接的清单文件,则链接到EXE中的静态生成的清单将覆盖我希望在磁盘上外部拥有的清单文件。我还需要在运行时修改这些清单文件,因此需要外部清单。当需要时,我当然可以使用这些清单启用运行时主题功能。第二个问题是关于外部和内部清单的优先级;当您勾选“启用运行时主题”时,外部清单是否可以优先于链接到Delphi应用程序中的内部清单资源?
除#3之外的可接受解决方案:

A. 某种方式使Delphi不生成清单。 B. 在运行时,即使找到内部清单,让Windows识别和优先使用外部.manifest文件。

C. 最不好的解决方案; 在运行时,在我的组件中的CoCreateInstance失败后,我可以枚举资源,报告存在外部清单并且会干扰我们,依靠使用我的组件的开发人员读取运行时错误消息,告诉他们禁用运行时主题复选框并重新构建他们的应用程序。提取和阅读清单已经在另一个stackoverflow问题here中得到了涵盖,其中包含可轻松转换为Delphi的C++代码。

更新 接受的答案正是我所要求的,但被认为是一个hack,而David关于Activation Contexts的答案则更加合理,并且是推荐的方法。

更新2 内置清单通常会在Delphi的后续版本(XE5及更高版本)中通过在项目设置中明确指定要链接的清单来覆盖。


2
#3 绝对是可能的,项目选项通过 IOTAProjectOptions 接口暴露。 - Ondrej Kelle
4
在Windows XP上,外部清单文件的优先级高于内嵌的清单文件;而在Vista及以上版本中,情况则相反。以上链接是一篇有趣的阅读材料。 - Ondrej Kelle
2个回答

11
我认为我已经找到了您所要求的有效解决方案,即在创建组件实例(放置在窗体上或包含其实例的窗体/模块在IDE中打开时)时从项目选项中禁用运行时主题。这不会阻止用户后来手动重新启用运行时主题,但对您可能仍然有用。 顺便说一句,在这种情况下,IOTAProjectOptions似乎没有帮助;看起来需要IOTAProjectResource。 TestComponentU.pas(运行时包的一部分):
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是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.

已接受,因为它做了我要求的事情。我应该指出,我认为David是正确的,我正在尝试进行黑客攻击(通过使用外部清单文本文件),当我的组件应该尝试使用激活上下文时,但如果Windows在某些方面似乎太破碎而无法使用激活上下文,则这是解决方法。我认为这个问题和答案很有价值,因为它们作为使用IOTAProjectResource的示例,这仅仅是一个简单的情况,其中获取它将会很方便。 - Warren P

6
我认为最好的解决方案是让组件的用户自行处理其应用程序的清单。否则,这将对任何使用此组件的用户造成严重限制。
相反,使用激活上下文API在需要时激活组件所需的清单。
您目前的想法是在可执行目录中编写清单文件,听起来非常脆弱,当该目录无法写入时可能会失败。另一方面,激活上下文API正是您真正需要的,没有任何缺点。

这是我在Excel COM插件中使用的机制,用于启用运行时主题。 - David Heffernan
你知道Win7 UAC是否会阻止常规或受限用户访问这些API吗? - Warren P
没有UAC问题。这就是你应该解决问题的方法。 - David Heffernan
我本来会把这个标记为已接受的,但事实上这个做了正确的事情,而另一个则只是按照我的要求做了。当别人提出愚蠢的想法时,我总是很烦躁,请在未来继续指出人们有问题或邪恶的黑客想法。 - Warren P
是的,你的答案是正确的,我同意。我从来没有意识到我在 SO 上有这么多激活上下文的答案。它们无处不在。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接