【问题标题】:Launch HTML Help as Separate Process将 HTML 帮助作为单独的进程启动
【发布时间】:2015-07-31 21:28:20
【问题描述】:

我正在使用 XE7 64 并且我正在寻找一种策略来解决在我的应用程序中显示 HTMLHelp 文件时遇到的几个问题(我已将 HTMLHelpViewer 添加到我的 uses 子句中)。问题如下: 1) Ctrl-c 不从主题复制文本; 2) 当模式对话框处于活动状态时,无法访问帮助查看器。

问题的根源可能是由于 htmlhelpviewer 与应用程序在同一进程中运行。有没有办法让内置的 htmlhelpviewer 启动一个新进程?如果没有,那我需要用 Createprocess 启动 HH.EXE 吗?

【问题讨论】:

  • 在我的 64 位 delphi 进程中没有这样的问题。如果您想用完进程,则需要使用 CreateProcess 启动进程。这将使整个体验变得不那么流畅。如果我是你,我会解决你在处理过程中遇到的问题。
  • 是的,有一种方法,但您将无法打开特定主题的帮助文件 - 它始终会单独打开默认主题。它是在同一个过程中加载的,因为如果您在一个带有上下文的主题上按下F1,例如1000,然后再次按下F1 以获取上下文2000,它需要重新使用相同的帮助显示。
  • @David 我也遇到过这个问题,不过我不知道 64 位。也许 HTML 帮助查看器只有 32 位,所以它的工作方式不同?
  • @Jerry 您可以在特定项目的新流程中打开帮助文件。那里没有问题。不,html 查看器 ocx 控件有 32 位和 64 位版本。
  • FWIW 我不使用内置的 html 帮助单元,因为传统上它一直是垃圾。我拦截应用程序帮助事件并直接调用 HtmlHelp。这真的很微不足道。我已经完成了这一切,以便我的应用程序可以记住并从会话到会话恢复帮助文件位置,但这只是一个很好的选择。

标签: delphi delphi-xe7 chm html-help


【解决方案1】:

您可以将帮助文件查看器作为一个单独的进程启动,但我认为这会使控制它变得更加复杂。我的猜测是提供的 HTML 帮助查看器代码是您的问题的根本原因。我一直发现代码质量极差。

我通过实现一个附加到Application 对象的OnHelp 事件处理程序来处理这个问题。此事件处理程序直接调用HtmlHelp API。我当然没有遇到您描述的任何问题。

我的代码如下所示:

unit Help;

interface

uses
  SysUtils, Classes, Windows, Messages, Forms;

procedure ShowHelp(HelpContext: THelpContext);
procedure CloseHelpWindow;

implementation

function RegisterShellHookWindow(hWnd: HWND): BOOL; stdcall; external user32;
function DeregisterShellHookWindow(hWnd: HWND): BOOL; stdcall; external user32;

procedure ShowHelp(HelpContext: THelpContext);
begin
  Application.HelpCommand(HELP_CONTEXTPOPUP, HelpContext);
end;

type
  THelpWindowManager = class
  private
    FMessageWindow: HWND;
    FHelpWindow: HWND;
    FHelpWindowLayoutPreference: TFormLayoutPreference;
    function ApplicationHelp(Command: Word; Data: THelpEventData; var CallHelp: Boolean): Boolean;
  protected
    procedure WndProc(var Message: TMessage);
  public
    constructor Create;
    destructor Destroy; override;
    procedure RestorePosition;
    procedure StorePosition;
    procedure StorePositionAndClose;
  end;

{ THelpWindowManager }

constructor THelpWindowManager.Create;

  function DefaultRect: TRect;
  var
    i, xMargin, yMargin: Integer;
    Monitor: TMonitor;
  begin
    Result := Rect(20, 20, 1000, 700);
    for i := 0 to Screen.MonitorCount-1 do begin
      Monitor := Screen.Monitors[i];
      if Monitor.Primary then begin
        Result := Monitor.WorkareaRect;
        xMargin := Monitor.Width div 20;
        yMargin := Monitor.Height div 20;
        inc(Result.Left, xMargin);
        dec(Result.Right, xMargin);
        inc(Result.Top, yMargin);
        dec(Result.Bottom, yMargin);
        break;
      end;
    end;
  end;

begin
  inherited;
  FHelpWindowLayoutPreference := TFormLayoutPreference.Create('Help Window', DefaultRect, False);
  FMessageWindow := AllocateHWnd(WndProc);
  RegisterShellHookWindow(FMessageWindow);
  Application.OnHelp := ApplicationHelp;
end;

destructor THelpWindowManager.Destroy;
begin
  StorePositionAndClose;
  Application.OnHelp := nil;
  DeregisterShellHookWindow(FMessageWindow);
  DeallocateHWnd(FMessageWindow);
  FreeAndNil(FHelpWindowLayoutPreference);
  inherited;
end;

function THelpWindowManager.ApplicationHelp(Command: Word; Data: THelpEventData; var CallHelp: Boolean): Boolean;
var
  hWndCaller: HWND;
  HelpFile: string;
  DoSetPosition: Boolean;
begin
  CallHelp := False;
  Result := True;

  //argh, WinHelp commands
  case Command of
  HELP_CONTEXT,HELP_CONTEXTPOPUP:
    begin
      hWndCaller := GetDesktopWindow;
      HelpFile := Application.HelpFile;

      DoSetPosition := FHelpWindow=0;//i.e. if the window is not currently showing
      FHelpWindow := HtmlHelp(hWndCaller, HelpFile, HH_HELP_CONTEXT, Data);
      if FHelpWindow=0 then begin
        //the topic may not have been found because the help file isn't there...
        if FileExists(HelpFile) then begin
          ReportError('Cannot find help topic for selected item.'+sLineBreak+sLineBreak+'Please report this error message to Orcina.');
        end else begin
          ReportErrorFmt(
            'Cannot find help file (%s).'+sLineBreak+sLineBreak+'Reinstalling the program may fix this problem.  '+
            'If not then please contact Orcina for assistance.',
            [HelpFile]
          );
        end;
      end else begin
        if DoSetPosition then begin
          RestorePosition;
        end;
        HtmlHelp(hWndCaller, HelpFile, HH_DISPLAY_TOC, 0);//ensure that table of contents is showing
      end;
    end;
  end;
end;

procedure THelpWindowManager.RestorePosition;
begin
  if FHelpWindow<>0 then begin
    RestoreWindowPosition(FHelpWindow, FHelpWindowLayoutPreference);
  end;
end;

procedure THelpWindowManager.StorePosition;
begin
  if FHelpWindow<>0 then begin
    StoreWindowPosition(FHelpWindow, FHelpWindowLayoutPreference);
  end;
end;

procedure THelpWindowManager.StorePositionAndClose;
begin
  if FHelpWindow<>0 then begin
    StorePosition;
    SendMessage(FHelpWindow, WM_CLOSE, 0, 0);
    FHelpWindow := 0;
  end;
end;

var
  WM_SHELLHOOKMESSAGE: UINT;

procedure THelpWindowManager.WndProc(var Message: TMessage);
begin
  if (Message.Msg=WM_SHELLHOOKMESSAGE) and (Message.WParam=HSHELL_WINDOWDESTROYED) then begin
    //need cast to HWND to avoid range errors
    if (FHelpWindow<>0) and (HWND(Message.LParam)=FHelpWindow) then begin
      StorePosition;
      FHelpWindow := 0;
    end;
  end;
  Message.Result := DefWindowProc(FMessageWindow, Message.Msg, Message.wParam, Message.lParam);
end;

var
  HelpWindowManager: THelpWindowManager;

procedure CloseHelpWindow;
begin
  HelpWindowManager.StorePositionAndClose;
end;

initialization
  if not ModuleIsPackage then begin
    Application.HelpFile := ChangeFileExt(Application.ExeName, '.chm');
    WM_SHELLHOOKMESSAGE := RegisterWindowMessage('SHELLHOOK');
    HelpWindowManager := THelpWindowManager.Create;
  end;

finalization
  FreeAndNil(HelpWindowManager);

end.

在您的项目中包含该单元,您将被连接到处理帮助上下文请求。代码上的一些cmets:

  1. OnHelp 事件处理程序的实现仅限于我的需要。如果您需要更多功能,则必须自己添加。
  2. 你不会有TFormLayoutPrefernce。这是我管理每个用户偏好的偏好类之一。它存储了窗口的边界矩形,以及窗口是否被最大化。这用于确保帮助窗口显示在与上一个会话中相同的位置。如果您不想要此类功能,请将其删除。
  3. ReportErrorReportErrorFmt 是我用来显示错误对话框的辅助函数。您可以将其替换为致电 MessageBox 或类似名称。

【讨论】:

  • 发现这篇文章有助于了解您所做的事情:*.com/questions/21912686/…。就我而言,我想让它非常简单(即不保存/恢复帮助窗口位置)。
  • David (Heffernan),您能给我们详细介绍一下您的 TFormLayoutPrefernce 课程吗?
  • @mimmo 它只是持久化用户配置文件的位置和窗体的窗口状态
【解决方案2】:

根据大卫直接调用HtmlHelp的cmets,没有遇到上述问题,我尝试了这种方法,它解决了问题。直接调用HTMLHelp通过id打开话题示例:

HtmlHelp(Application.Handle,'d:\help study\MyHelp.chm', 
         HH_HELP_CONTEXT, 70);

【讨论】:

    最近更新 更多