15 мая 2013 г.

Использование HTMLHelpViewer в DLL

В модуле HTMLHelpViewer со времени его появления в Delphi 2005 и до выхода Update 4 для Delphi XE2 была ошибка, не позволяющая использовать этот модуль в DLL. Ошибка состоит в том, что в секции finalization всегда выполняется вызов HtmlHelp(HH_CLOSE_ALL), что может приводить к загрузке библиотеки HHCTRL.OCX. Для обычного приложения это не страшно, просто теряется немного времени на загрузку-выгрузку библиотеки. Но если мы пишем DLL (например, COM-объект или плагин для другого приложения), то выполнение секции finalization будет происходить внутри DllMain, и попытка загрузить HHCTRL.OCX приведет либо к зависанию приложения, либо к ошибке Access Violation (в зависимости от версии Windows).

В статье MSDN вполне понятно написано, что можно и что нельзя делать в DllMain:
The entry-point function should perform only simple initialization or termination tasks. It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions).
Similarly, the entry-point function must not call the FreeLibrary function (or a function that calls FreeLibrary) during process termination.
Несмотря на это, вызов LoadLibrary и FreeLibrary широко распространен в секциях initialization и finalization. Обычно это работает, так как загрузчик Windows достаточно умен, и умеет обходить большинство создаваемых разработчиками приложений проблем. Но иногда, как в данном случае, не справляется. О другой подобной ошибке можно почитать в статье How you might be loading a DLL during DLL_PROCESS_DETACH without even realizing it.

Эта же ошибка присутствует и в других модулях, сделанных на основе HTMLHelpViewer, например в описанном в статье Как использовать справку в программах Delphi модуле HTMLHelpViewerEx.

С богатой историей ошибки можно ознакомится по ссылкам ниже:

Номер бага Дата добавления Описание
QC23172 06.01.2006 Unload HTMLHelpViewer
QC36810 20.11.2006 HTMLHelpViewer hangs dll
QC48983 13.07.2007 HTMLHelpViewer causing problems in DLLs
QC67463 02.10.2008 On unload vcl bpl application stopped
QC68973 15.11.2008 Adding HTMLHelpViewer prevents Excel from shutting down
QC78998 26.10.2009 HTMLHelpViewer Initialization/Finalization bug
QC89616 12.11.2010 HTMLHelpViewer.pas bug in initialization finalization methods
QC102083 24.12.2011 HTMLHelpViewer.pas critical issue in initialization and finalization methods

Исправление ошибки, сделанное в Update 4 для Delphi XE2, состоит из одной строчки кода:

--- Vcl.HtmlHelpViewer.pas_dxe2u3
+++ Vcl.HtmlHelpViewer.pas_dxe2u4
@@ -301,6 +301,7 @@
 procedure THtmlHelpViewer.SoftShutDown;
 begin
+  if FInitialized then
     HtmlHelp(0, nil, HH_CLOSE_ALL, 0);
 end;

Решаем проблему в DLL


Если вы используете версию Delphi младше XE2, то просто скопируйте файл HTMLHelpViewer.pas из папки установки Delphi в папку с исходным кодом приложения, добавьте строку if FInitialized then в процедуру THtmlHelpViewer.SoftShutDown, как показано выше, и добавьте этот модуль в проект.

Решаем проблему в EXE


Описанная ошибка не страшна для приложения, так как загрузка-выгрузка HHCTRL.OCX выполняется в ходе обычного выполнения программы, когда загрузчик операционной системы свободен от блокировок.

Решать проблему в приложении может понадобиться, если вы загружаете DLL, собранную в Delphi с модулем HTMLHelpViewer, содержащим описанную ошибку, и у вас нет возможности исправить эту DLL.
В этом случае необходимо добиться, чтобы библиотека HHCTRL.OCX была загружена во время выгрузки проблемной DLL. Это можно сделать, добавив к проекту такой модуль:

unit VCLPatch;

interface

implementation

uses
    Windows;

var
    HtmlHelpModule: HModule;

initialization
    HtmlHelpModule := LoadLibrary('hhctrl.ocx');

finalization
    FreeLibrary(HtmlHelpModule);

end.
Модуль нужно поместить в секцию uses проекта как можно раньше, чтобы библиотека HHCTRL.OCX оставалась загруженной во время выгрузки проблемной DLL.

Об общих вопросах использования справки в Delphi можно почитать в уже упоминавшейся выше статье Как использовать справку в программах Delphi.

Комментариев нет:

Отправить комментарий