Файл справки, открытый из модального окна, не отвечает

Использую Delphi XE2, Win64.

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

При вызове этого точно файла справки из старой версии нашего приложения (созданного с помощью Delphi 6), находящегося в той же папке, что и новая версия (созданная с помощью Delphi XE2), файл справки отображается при нажатии клавиши F1. попал из модального окна, реагирует и может быть закрыт, как я ожидаю.

Файл справки представляет собой файл типа .chm.

Обобщить.

Запустите приложение. Откройте файл справки, нажав F1. Перейти к приложению и откройте модальное окно в приложении. Запустите справку из модального окна, нажав F1.

Кто-нибудь вообще знает, почему это может быть?

Я искал в Интернете и не нашел ни одной подобной проблемы.

Мы в тупике.

Ура ТиДжей

----РЕДАКТИРОВАТЬ----

Вот некоторый код для примера приложения с двумя формами, которое также демонстрирует такое поведение.

program Project1;

uses
  Vcl.Forms,
  HTMLHelpViewer,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.HelpFile := 'C:\helpfile.chm';
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Вот код формы 1:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form2 := TForm2.Create(Application);
  try
    Form2.ShowModal;
  finally
    Form2.Free;
  end;
end;

end.

Я установил свойство helpcontext в двух формах на два допустимых контекста в моем файле справки.

Запустите приложение — F1, чтобы открыть файл справки. Нажмите кнопку, чтобы создать и показать Form2. F1 для вызова файла справки. Невозможно закрыть файл справки, пока я не закрою Form2.

Надеюсь это поможет. - Т.Дж.


person TJ Asher    schedule 21.02.2013    source источник
comment
Запускается winhlp64 или 32-битная версия?   -  person Tony Hopkinson    schedule 22.02.2013
comment
@ Тони Ни то, ни другое. Это .chm. Это будет в процессе работы с компонентом chm ocx.   -  person David Heffernan    schedule 22.02.2013
comment
@DavidHeffernan - Явная установка popupparent или нет не имеет очевидного эффекта. Что вам нужно, чтобы помочь? Это происходит даже с простым тестовым приложением из двух форм.   -  person TJ Asher    schedule 22.02.2013
comment
@DavidHeffernan - я попробую твою идею...   -  person TJ Asher    schedule 22.02.2013
comment
Гах, ок. Не делайте 64-битную Delphi, но мой коллега немного повеселился с помощью 32-битного приложения на 64-битной ОС. Утром собирался поковыряться в его мозгу, посмотреть, есть ли у него зацепка.   -  person Tony Hopkinson    schedule 22.02.2013
comment
@TonyHopkinson 32/64, здесь нет никакой разницы. Поведение у обоих одинаковое.   -  person David Heffernan    schedule 22.02.2013
comment
Это хороший ответ, и еще кое-что я узнал от вас. Если бы я еще не был женат, я бы сделал предложение. Заметьте, она тоже всегда права. :)   -  person Tony Hopkinson    schedule 22.02.2013


Ответы (1)


Это серьезный недостаток дизайна HtmlHelpViewer. И легко воспроизвести поведение, которое вы описываете. Молодец, что так четко обозначил проблему. Проблема в равной степени затрагивает как 32-битные, так и 64-битные программы.

Лично я не использую HtmlHelpViewer, потому что он просто не работает. Я реализую обработчик для TApplication.OnHelp. Это выглядит так:

class function THelpWindowManager.ApplicationHelp(Command: Word; 
  Data: THelpEventData; var CallHelp: Boolean): Boolean;
begin
  CallHelp := False;
  Result := True;
  //argh, WinHelp commands
  case Command of
  HELP_CONTEXT,HELP_CONTEXTPOPUP:
    HtmlHelp(GetDesktopWindow, Application.HelpFile, HH_HELP_CONTEXT, Data);
  end;
end;

Поместите это в класс и назначьте его Application.OnHelp при запуске:

Application.OnHelp := THelpWindowManager.ApplicationHelp;

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


Спасибо @Sertac за подробности в комментариях ниже. Подводя итог, вот где код HtmlHelpViewer работает неправильно:

  1. Он отправляет команду HH_INITIALIZE при запуске справочной системы.
  2. Как описано в документации, этот настраивает HTML-справку для запуска на тот же поток, что и вызывающее приложение, а не дополнительный поток.
  3. Когда вы вызываете ShowModal, это вызывает DisableTaskWindows, который отключает окна в вызывающем потоке.
  4. Поскольку окно просмотра справки было создано основным потоком вашего приложения (из-за команды HH_INITIALIZE), оно отключается.

Вот почему вы не можете взаимодействовать с уже существующим окном справки, пока активна модальная форма Delphi.

person David Heffernan    schedule 21.02.2013
comment
Как это поведет себя, если вы передадите дескриптор своей основной формы вместо окна рабочего стола? - person Sertac Akyuz; 22.02.2013
comment
@Sertac ‹strike›Ну, я ожидаю, что у этого будут проблемы с владением окном, когда модальное окно отключит своего владельца.‹/strike› Но нет, это тоже нормально. HtmlHelpViewer выбрасывает куски. - person David Heffernan; 22.02.2013
comment
Тогда это +1, так как он не будет мешать, если вам нужно передать дескриптор формы. Кстати, я думаю, что ShowModal отключает окна потоков с помощью DisableTaskWindows. - person Sertac Akyuz; 22.02.2013
comment
Блестящий товарищ! Работает как шарм. О, и это избавляет от необходимости использовать HTMLHelpViewer в проекте. - person TJ Asher; 22.02.2013
comment
@Sertac в некоторых режимах работы элемент управления справкой запускает свои окна из другого потока. - person David Heffernan; 22.02.2013
comment
@David - это команда HH_INITIALIZE через HtmlHelp в ValidateHelpViewer из THtmlHelpViewer. Обход вызова приводит к запуску родительского окна справки в другом потоке. - person Sertac Akyuz; 22.02.2013
comment
@SertacAkyuz Да, это звонит в колокола. Итак, HtmlHelpViewer вызывает HH_INITIALIZE и запускается в вызывающем потоке. И затем, когда вызывается ShowModal, окно отключается DisableTaskWindows. Это оно? - person David Heffernan; 22.02.2013
comment
@ Дэвид - Точно. Легко смоделировать обратное, изменив FInitialized из THtmlHelpViewer на «истину» через отладчик перед его тестированием. Позже это вызывает AV, но достаточно увидеть, что окно справки находится в другом потоке. - person Sertac Akyuz; 22.02.2013
comment
Спасибо @Сертак. Я уверен, что когда-то знал эти подробности, но в старости я становлюсь таким забывчивым! Я собираюсь проверить это, но, вероятно, это пустая трата времени. HtmlHelpViewer, вероятно, не подлежит искуплению. - person David Heffernan; 22.02.2013