Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Helpfile opened from modal window unresponsive

Using Delphi XE2, Win64.

So I have a large application with many forms and if I open the help file from the main form and open a modal window and then hit F1 to fire off context-sensitive help on the modal window the help file window shows with the correct info but the help file cannot be closed until I close the modal window. I cannot even get the help file to have focus again if I go back to the application until the modal window closes.

Calling this exact same help file from the old version of our application (built with Delphi 6) sitting within the same folder as the new version (built with Delphi XE2) the help file displays when the F1 key is hit from the modal window and is responsive and can be closed like I expect.

The help file is .chm type file.

To summarize.

Launch application Open help file by F1 Jump to application and open modal window in application Launch help from modal window by hitting F1 Help file window cannot be closed until I jump back to my application and close the modal window.

Does anybody have any idea at all why this would be?

I have searched the Internet and have not found any similar issue.

We are stumped.

Cheers TJ

----EDIT----

Here is some code for a sample two form app that also exhibits this behavior.

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.

Here is Form1 code:

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.

I set the helpcontext property on the two forms to two valid contexts within my help file.

Run the app - F1 to open help file Hit button so Form2 is created and shown F1 to call help file Cannot close help file until I close Form2.

Hope this helps. - TJ

like image 822
TJ Asher Avatar asked Feb 21 '13 21:02

TJ Asher


1 Answers

This is a serious design flaw in HtmlHelpViewer. And it's easy to reproduce the behaviour you describe. Well done for specifying the problem so clearly. The issue afflicts both 32 and 64 bit programs equally.

Personally I don't use HtmlHelpViewer because it just doesn't work. I implement a handler for TApplication.OnHelp. It looks like this:

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;

Put that in in a class and assign it to Application.OnHelp on startup:

Application.OnHelp := THelpWindowManager.ApplicationHelp;

I've just tested that out on the trivial two form application and it works well. In real code you may wish to embellish this. For example, my actual code is more complex. It stores in user settings the position and window state of the help window when it is closed. And then when shown again, that position and window state are restored. So that the help window appears to remember where it last was on the screen.


Thanks to @Sertac for dredging out the details in the comments below. In summary here's where the HtmlHelpViewer code goes wrong:

  1. It makes sends the HH_INITIALIZE command at help system startup.
  2. As described in the documentation this configures HTML Help to run on the same thread as the calling application instead of a secondary thread.
  3. When you call ShowModal that calls DisableTaskWindows which disables windows in the calling thread.
  4. Because the help viewer window was created by your app's main thread (because of the HH_INITIALIZE command), it gets disabled.

And that's why you cannot interact with the a pre-existing help window whilst a Delphi modal form is active.

like image 123
David Heffernan Avatar answered Oct 24 '22 17:10

David Heffernan