【问题标题】:Helpfile opened from modal window unresponsive从模式窗口打开的帮助文件无响应
【发布时间】:2013-02-07 09:13:18
【问题描述】:

使用 Delphi XE2、Win64。

所以我有一个包含许多表单的大型应用程序,如果我从主表单打开帮助文件并打开模式窗口,然后按 F1 以在模式窗口上触发上下文相关帮助,帮助文件窗口将显示正确的信息,但在我关闭模式窗口之前无法关闭帮助文件。如果我在模式窗口关闭之前返回应用程序,我什至无法让帮助文件再次获得焦点。

从旧版本的应用程序(使用 Delphi 6 构建)中调用此完全相同相同的帮助文件,该帮助文件位于与新版本(使用 Delphi XE2 构建)相同的文件夹中,当F1 键是从模态窗口中按下的,并且是响应式的,可以像我期望的那样关闭。

帮助文件是.chm类型的文件。

总结一下。

启动应用程序 F1打开帮助文件 跳转到应用程序并在应用程序中打开模态窗口 通过按 F1 从模式窗口启动帮助 在我跳回我的应用程序并关闭模式窗口之前,无法关闭帮助文件窗口。

有人知道为什么会这样吗?

我已经在网上搜索过,没有发现任何类似的问题。

我们被难住了。

干杯 TJ

----编辑----

以下是同样表现出此行为的示例两表单应用程序的一些代码。

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.

这是Form1代码:

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 之前无法关闭帮助文件。

希望这会有所帮助。 - TJ

【问题讨论】:

  • 是启动 winhlp64 还是 32 位版本?
  • @Tony 也没有。这是一个.chm。它将与 chm ocx 组件一起处理。
  • @DavidHeffernan - 是否明确设置 popupparent 没有明显效果。你需要什么帮助?这甚至发生在一个简单的两个表单测试应用程序中。
  • @DavidHeffernan - 我会试试你的想法...
  • 呸,ocx。不要做 64 位 delphi,但我的一位同事在 64 位 oses 上的 32 位应用程序的帮助下获得了一些乐趣。早上要去采摘他的大脑,看看他是否有线索。

标签: delphi delphi-xe2 helpfile


【解决方案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 在下面的 cmets 中挖掘出细节。总之,这里是HtmlHelpViewer 代码出错的地方:

  1. 它在帮助系统启动时发送HH_INITIALIZE命令。
  2. documentation 中所述,此将 HTML 帮助配置为在与调用应用程序相同的线程而不是辅助线程上运行
  3. 当您调用 ShowModal 时调用 DisableTaskWindows 会禁用调用线程中的窗口。
  4. 因为帮助查看器窗口是由应用程序的主线程创建的(由于 HH_INITIALIZE 命令),它被禁用。

这就是为什么在 Delphi 模态表单处于活动状态时无法与预先存在的帮助窗口进行交互的原因。

【讨论】:

  • 如果您传递主窗体的句柄而不是桌面窗口,它的行为如何?
  • @Sertac 好吧,我希望当模式窗口禁用其所有者时会出现窗口所有权问题。但是不,这也很好。 HtmlHelpViewer 吹大块。
  • 那是+1,因为如果您需要传递表单的句柄,它不会妨碍您。顺便说一句,我认为它是 ShowModal 通过 DisableTaskWindows 禁用线程窗口。
  • 好搭档!奇迹般有效。哦,这消除了在项目中使用 HTMLHelpViewer 的需要。
  • @Sertac 在某些操作模式下,帮助控件从不同的线程运行其窗口
猜你喜欢
  • 2015-02-11
  • 1970-01-01
  • 2012-11-23
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2015-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多