我有一个用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;)可能会出现问题。但我不知道如何避免这种情况。
答案 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();
之前检查是否安装了HHprocedure HHCloseAll;
begin
If @HH.HtmlHelp <> Nil then //HH API is available
begin
HH.HtmlHelp(0, nil, HH_CLOSE_ALL, 0);
Sleep(0);
end;
end;