hhctrl.ocx的FreeLibrary在程序关闭时挂起的原因是什么?我该如何避免这种情况?

时间:2016-01-29 15:24:34

标签: delphi delphi-2007 html-help

我有一个用Delphi 2007编写的程序,它使用html帮助。通常它会在退出时挂起(即使html帮助实际上没有被调用),我在Windows.pas的最终部分中将问题追溯到此调用

finalization
  if HtmlHelpModule <> 0 then FreeLibrary(HtmlHelpModule);
end.

主线程在此调用中挂起,因为hhctrl.ocx的卸载代码内部有一个NTWaitFormMultipleObjects。还有其他线程(我的代码没有创建)显然等待相同,所以我的程序挂起。我猜其中一些线程是由ADO和/或Microsoft SQL Server客户端库创建的。

我找到了一个解决方法:对LoadLibrary进行附加调用(&#39; hhctrl.ocx&#39;),因此在Windows.pas中对FreeLibrary的调用实际上并没有卸载dll,只是将引用计数减少到1。虽然这似乎有效,但感觉不对。

这是一个已知问题吗?有适当的解决方案吗?

(是的,我用谷歌搜索,但没有找到任何帮助。 这似乎描述了类似的问题 https://social.msdn.microsoft.com/Forums/en-US/7bce34a2-50a0-411d-872f-0626360d5415/dll-sometimes-hangs-on-unload?forum=vcgeneral 使用不同的DLL。)

编辑:更多信息:

问题显然只发生在程序中从不调用html帮助时(因此没有调用LoadLibrary(&#39; hhctl.ocx&#39;))。在关闭时,htmlhelp.pas中的终结代码尝试关闭所有htmlhelp查看器窗口(其中没有),并向HtmlHelp函数发出第一次调用。这导致在windows.pas中调用LoadLibrary。 如果我在程序中显示任何htmlhelp,一切正常。 因此,我认为在RTL的最终确定中调用LoadLibrary(&#39; hhctl.ocx&#39;)可能会出现问题。但我不知道如何避免这种情况。

2 个答案:

答案 0 :(得分:0)

finalization
  if HtmlHelpModule <> 0 then FreeLibrary(HtmlHelpModule);
end.

这是问题的主要来源。没有人应该在初始化或终结部分中使用LoadLibrary / FreeLibrary(显式或隐式)。

简而言之,这是因为初始化和终结代码在特殊条件下运行,在DllMain函数内部,其中加载程序锁处于活动状态。在这些条件下,不应该调用上面的两个函数(实际上,甚至GetModuleHandle和GetProcAddress可能会失败!),不使用锁而不启动或终止线程。您可以从this StackOverflow answer学习装载锁。我还推荐Chris Brumme的帖子Startup, Shutdown and related matters进行全面研究。

所以,Embarcadero负责这个错误,我们能做什么?最简单的解决方法是(正如您已经发现的那样)始终从您的程序中调用HtmlHelp,或者只是LoadLibrary(&#39; hhctrl.ocx&#39;),记住不要将此调用放在任何初始化部分中单元。

答案 1 :(得分:-1)

通常,当主机应用程序关闭时,Windows会自动关闭此应用程序打开的所有帮助窗口。存在问题......这可能会导致访问冲突。

我不是德尔福程序员 - 更忙于帮助创作(CHM)和VB。您可以尝试使用HH_INITIALIZE,HH_UNINITIALIZE命令。这些内容记录在HH Workshop在线帮助中。但是 - 请检查您的代码是否为HH_CLOSE或HH_CLOSE_ALL。

提前致电HH_CLOSE_ALL。在HH_CLOSE_ALL和您对UnloadLibrary的调用之间获得更多空间。在VB和Delphi中,您将在Form Close或Destroy上的Form QueryUnload 而不是上执行调用。

解决方法是手动关闭HH窗口或早些时候在CloseQuery()事件中关闭HH窗口,并使用sleep(0)为HTMLHelp提供几个周期来安定下来。

//Will close all Help windows opened by the application - no handle required
HtmlHelp(0, nil, HH_CLOSE_ALL, 0);

//This runs a little faster
if IsWindow(_HHwinHwnd) then
SendMessage( _HHwinHwnd, wm_close, 0, 0 );

样品:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   //if IsWindow(_HHwinHwnd) then
   //  SendMessage( _HHwinHwnd, wm_close, 0, 0 );
   HtmlHelpA(0, nil, HH_CLOSE_ALL, 0);
   Sleep(0);
end;

请勿在关机时盲目呼叫HH_CLOSE_ALL。如果用户未安装HTML帮助,则此调用将使您的应用程序崩溃。这是更安全的代码。注意我们在调用HtmlHelp();

之前检查是否安装了HH
procedure HHCloseAll;
begin
  If @HH.HtmlHelp <> Nil then  //HH API is available
    begin
    HH.HtmlHelp(0, nil, HH_CLOSE_ALL, 0);
    Sleep(0); 
  end;
end;