9

我有一个在 Delphi XE 中构建的组件,我想以下列方式使用它:

  1. 用户创建一个新的空白项目。

  2. 用户将我的组件放在表单上。

  3. 我的组件中的一些特殊设计时代码被执行,这将更改项目选项以取消选中项目选项中的“启用运行时主题”复选框。我不确定这是否可能,所以我问是否可能。

如果#3 是不可能的,那么我需要另一个解决方案来解决这个组件的“可用性”问题;我遇到的问题是,如果用户没有通过取消选中启用运行时主题来禁用静态链接的清单文件,那么链接到 EXE 的静态生成的清单似乎会覆盖我希望在 EXE 之外拥有的外部清单文件,在磁盘。我还需要在运行时修改这些清单,因此需要外部清单。当然,我可以在需要时使用这些清单启用运行时主题功能。第二个问题是关于外部和内部清单的优先级;当您选中“启用运行时主题”时,外部清单能否以某种方式优先于链接到 Delphi 应用程序的内部清单资源?

#3 以外的可接受解决方案:

A. 不知何故导致 Delphi 不生成清单。B. 以某种方式在运行时让 Windows 识别并优先考虑外部 .manifest 文件,即使找到内部文件也是如此。

C. 最差解;在运行时,在我的组件中的 CoCreateInstance 失败后,我可以枚举资源,报告存在外部清单并将我们搞砸,并依靠使用我的组件的开发人员阅读我的组件吐出的运行时错误消息,告诉他们禁用运行时主题复选框并重建他们的应用程序。提取和读取清单已在此处的另一个 stackoverflow 问题中进行了介绍,其中 C++ 代码可以轻松转换为 Delphi。

更新接受的答案完全符合我的要求,但被认为是一种黑客行为,而大卫关于激活上下文的答案更加理智,并且是推荐的方法。

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

4

2 回答 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.
于 2011-07-19T17:38:56.270 回答
6

我怀疑最好的解决方案是让组件的用户为他们的应用程序做任何他们想做的事情。否则会严重限制该组件的任何用户。

而是使用激活上下文API 在需要时激活组件所需的清单。

您当前在可执行目录中写入清单文件的想法听起来非常脆弱,并且在无法写入该目录时容易失败。另一方面,激活上下文 API 完全符合您的需求,没有任何缺点。

于 2011-07-19T19:51:16.383 回答