Снимите флажок «Включить темы выполнения» или удалите внутренний манифест в Delphi XE? - PullRequest
9 голосов
/ 19 июля 2011

У меня есть компонент, который я создаю в Delphi XE, который я хочу использовать следующим образом:

  1. Пользователь создает новый пустой проект.

  2. Пользователь удаляет мой компонент в форме.

  3. Выполнен некоторый специальный код Designtime в моем компоненте, который изменит параметры проекта, чтобы снять флажок «Включить темы времени выполнения» в параметрах проекта. Я не уверен, что это даже возможно, поэтому я спрашиваю, возможно ли это.

Если № 3 невозможен, тогда мне нужно другое решение моей проблемы «юзабилити» с этим компонентом; У меня проблема в том, что если пользователи не отключают статически связанный файл манифеста, сняв флажок Включить темы времени выполнения, то этот статически сгенерированный манифест, связанный с EXE, похоже, переопределяет внешние файлы манифеста, которые я хочу иметь вне EXE, в диск. Я также должен изменить эти манифесты во время выполнения, таким образом, потребность во внешних манифестах. Конечно, я могу включить функциональность Runtime Theme, используя эти манифесты, когда это необходимо. Второстепенный вопрос касается приоритета внешних и внутренних проявлений; Может ли внешний манифест каким-либо образом иметь приоритет над внутренним ресурсом манифеста, который связан с приложениями Delphi, когда вы устанавливаете флажок «Включить темы времени выполнения»?

Допустимые решения, отличные от # 3:

A. Каким-то образом заставить Delphi не генерировать манифест. B. Каким-то образом во время выполнения заставьте Windows распознавать и устанавливать приоритеты внешних файлов .manifest, даже если найден внутренний.

C. Наименее хорошее решение; Во время выполнения, после сбоя CoCreateInstance в моем компоненте, я могу перечислять ресурсы, сообщать, что внешний манифест присутствует и мешает нам, и полагаться на разработчиков, которые используют мой компонент, читающий сообщения об ошибках во время выполнения, которые выкладывает мой компонент, предлагая им отключить установите флажок «Темы выполнения» и перестройте их приложение. Извлечение и чтение манифеста уже рассматривается в другом вопросе stackoverflow с кодом C ++, который можно легко преобразовать в Delphi.

Обновление Принятый ответ делает именно то, что я просил, но считается хаком, и ответ Дэвида о контекстах активации гораздо более вменяемый и рекомендуемый подход.

Update2 Встроенный манифест, как правило, переопределяется в более поздних версиях Delphi (XE5 и более поздних), явно указывая, какой манифест вы хотите связать, через настройки проекта.

Ответы [ 2 ]

11 голосов
/ 19 июля 2011

Я думаю, что нашел рабочее решение для того, что вы просили, т.е. отключить темы выполнения из параметров проекта, когда создается экземпляр вашего компонента (отбрасывается на форму, или форма / модуль, содержащий его экземпляр, открывается в 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.
6 голосов
/ 19 июля 2011

Я подозреваю, что лучшее решение - позволить пользователям компонента делать все, что они захотят, с манифестами для своих приложений. В противном случае это наложит серьезные ограничения на пользователей этого компонента.

Вместо этого используйте API-интерфейс активации , чтобы активировать манифесты, которые нужны вашему компоненту, как и когда это необходимо.

Ваша текущая идея записать файлы манифеста в исполняемый каталог звучит чрезвычайно хрупко и может провалиться, когда в этот каталог невозможно записать. С другой стороны, API контекста активации делает именно то, что вам действительно нужно, без каких-либо недостатков.

...