TMS Components в DLL
Опубликовано 12.01.2011 г. 16:22
В конце прошлого года столкнулся с проблемой использования компонентов TMS Software в динамических библиотеках. На самом деле эта проблема почти взорвала мой мозг, о чем я писал в одной из предыдущих статей.
Если кратко, предыстория такова: Создаем группу проектов: ехе и dll. В библиотеку добавляем форму, использующую компоненты TMS. никакого кода библиотека при этом не содержит, никакие функции не экспортирует. Вызывающий ехе содержит следующий код при создании главной формы:
libFileName := ExtractFilePath(application.ExeName) + 'kernel.dll'; kernelLibHandle := LoadLibrary(PChar(libFileName)); if kernelLibHandle > 0 then FreeLibrary(kernelLibHandle)Т.е просто загружает и выгружает библиотеку. В качестве результата получается AV. Весьма интересно программа ведет себя в win7x64. Если в x32 мы сразу получаем AV, то x64 может вполне нормально отработать и закрыться, однако если мы откроем например окно свойств системы, или окно проводника, то опять же получаем AV. Была создана тема на форуме поддержки, и написано письмо в суппорт. И перед новым годом я таки получил ответ на него: как ни странно у товарищей из TMS все прекрасно работало, и они прислали мне мой откомпилированный тестовый проект. И у меня он собственно тоже выполнялся правильно. Но опять же перекомпилировав его я получал ошибки. В ответе суппорта был весьма резонный вопрос: использую ли я последнюю версию компонентов. На что я мог ответить разве что то, что наша лицензия не позволяет обновить компоненты до последней версии. Последняя версия Advanced String Grid на текущий момент является v.5.6.0.1 (согласно скачанной триал версии с сайта). Кстати с установленной триал версией этот код и правда нормально работает, без ошибок. У меня же использовалась версия 5.5.2.0 после чего она была обновлена до 5.6.0.0. Ошибка таки не исчезла. Новое письмо в суппорт: в 5.6.0.0 ошибка есть, в триал 5.6.0.1 ошибки нет. Ответ был весьма простой: Please use the latest version of the component. (с) Отлично подумал я. Если запускать приложение вне среды, то Windows строит отчет об ошибке, из которого мы можем узнать, что имя модуля, вызвавшего ошибку - gdiplus.dll Ну раз так, и при условии что никакого кода в библиотеки моего нет, а сами компоненты, в частности этот грид весьма насыщены графикой, то надо предположить, что какая то часть работы с GDI проходит в секции инициализации/финализации модулей, вызывая ошибку. О чем я, впрочем, и написал в суппорт. Чем мне нравится windows 7 дак это удобным поиском. *.pas файлы очень удобно добавляются в список индексирования для полнотекстового поиска, можно также добавить их для предварительного просмотра как текста в проводнике. Поисковый запрос вида "GDI *.pas initialization" выдал мне около 30 файлов. и здесь все наше внимание приковывается к файлу advgdip.pas приводя выдержки исходного кода из initialization/finalization частей модуля можно увидеть следующее:
initialization begin GdiplusStartup(gdiplusToken, @StartupInput, @StartupOutput); end; finalization begin if not IsLibrary then begin GdiplusShutdown(gdiplusToken); end; end;А это значит следующее: загрузка GDI+ с помощью GDIPlusStartup() происходит всегда, независимо от типа исполняемого модуля, а вот его его выгрузка с использованием GDIPlusShutdown() происходит только в том случае, если наш исполняемый модуль не является библиотекой. Внимание вопрос: что же происходит в случае, если мы поместим компонент в библиотеку? Ответ кажется достаточно прост: получим AV при выгрузке. Весьма интересно узнать когда же появилась эта версия компонента 5.6.0.1. Не в начале ли января этого года. Как вообще мог набор компонентов дожить до такой версии (сам Component Pack имеет версию 5.8.3) имея такую ошибку. Документация весьма четко говорит
The GdiplusShutdown function cleans up resources used by Microsoft Windows GDI+. Each call to GdiplusStartup should be paired with a call to GdiplusShutdown.С таким же успехом в примере TMS по использованию компонентов в DLL присутствует 2 вызова LoadLibrary() с одним вызовом FreeLibrary(), хотя они также должны быть парными. Еще есть такой момент, я вот не знаток к сожалению разработки DLL, и нахожусь только вначале изучения. Я так понимаю, что секции Initialization/finalization вызываются из DLLMain. Однако документация гласит:
Do not call GdiplusStartup or GdiplusShutdown in DllMain or in any function that is called by DllMainЭто я к тому, что правильно ли проводить инициализацию GDI+ в секции Initialize.
12.01.2011 в 18:32
Сама Delphi имеет аналогичные проблемы:
qc.embarcadero.com/wc/qcmain.aspx?d=36652
qc.embarcadero.com/wc/qcmain.aspx?d=57442
Themes.pas:
finalization
if not IsLibrary then
InternalServices.Free;
end.
Появилось после:
qc.embarcadero.com/wc/qcmain.aspx?d=4167
12.01.2011 в 22:52
13.01.2011 в 11:07
в предыдущих этого нет, и судя по всему тоже связано с gdiplus. Суппорт меня послал, сказав что у них всё в порядке и найти баг слишком сложно. Все последние версии дают эту проблему, приходится мириться.
18.03.2011 в 19:01
Разрабатываю COM Add-in`s для офиса (.dll), в часности для Word-а.
В этой COM интеграции я использую TMS taskdialog (на котором есть AdvGlowButtons для отрисовки которых используется GDI+ в AdvGDIP.pas).
Так вот даже если мой код не исполняется (dll просто подгружается в Word), Word 2003 при закрытии крэшается!!!
После долгих вычислений нашёл причину - инициализационная секция модуля AdvGDIP.pas.
Имею TMS Component Pack v5.7.2.0.
initialization уже немного модифицирован по сравнению с примером вверху.
initialization
begin
// Initialize StartupInput structure
StartupInput.DebugEventCallback := nil;
StartupInput.SuppressBackgroundThread := True;
StartupInput.SuppressExternalCodecs := False;
StartupInput.GdiplusVersion := 1;
StartupOutput.NotificationHook := nil;
StartupOutput.NotificationUnhook := nil;
// Initialize GDI+
GdiplusStartup(gdiplusToken, @StartupInput, @StartupOutput);
StartupOutput.NotificationHook(gdiplusToken);
end;
finalization
begin
// Close GDI +
if not IsLibrary then
begin
StartupOutput.NotificationUnhook(gdiplusToken);
GdiplusShutdown(gdiplusToken);
end;
end;
Так вот если не использовать SuppressBackgroundThread и NotificationHook - всё работает отлично.
Но опять же вопрос:
Как можно вызывать инициализацию (переинициализацию) GDI и потом её не финализировать в DLL-ке? Если руководствоваться MSDN-ом, то тут полный абсурд.
К примеру приложение грузит мою dll-ку и на момент загрузки application message loop уже работает и GdiplusStartup, NotificationHook уже нельзя вызывать.
Конечно можно сделать условие don`t SuppressBackgroundThread if IsLibrary =true, но как это отразится на работе TMS компонентов не знаю.
P.S. наверное стоит отказаться от использования этих компонентов.
P.S.S. Кстати баг получается очень редко и не на всех Word-ах (скорее всего зависит от установленных в Word других эдинов или компонентов использующих GDI).
21.03.2011 в 13:40
я в суппорт об этом им писал, сказали юзать последние версии компонентов. мб данный вопрос там решен уже. я самую последнюю не ставил. счас там вроде 6я версия компонент пака вышла.
зы: а я из компонентов их юзаю только грид. Он в действительно весьма функционален и полезен.правда багов имеет не мало тоже, и из за своих размеров немного медлителен.
24.04.2011 в 02:48
begin
// Initialize StartupInput structure
StartupInput.DebugEventCallback := nil;
StartupInput.SuppressBackgroundThread := True;
StartupInput.SuppressExternalCodecs := False;
StartupInput.GdiplusVersion := 1;
StartupOutput.NotificationHook := nil;
StartupOutput.NotificationUnhook := nil;
// Initialize GDI+
GdiplusStartup(gdiplusToken, @StartupInput, @StartupOutput);
if not IsWinVista then
StartupOutput.NotificationHook(gdiplusToken);
end;
finalization
begin
// Close GDI +
if not IsLibrary then
begin
if not IsWinVista then
StartupOutput.NotificationUnhook(gdiplusToken);
GdiplusShutdown(gdiplusToken);
end;
end;
25.04.2011 в 10:31
зачем условие на isWinVista? в чем суть его?
26.04.2011 в 10:24
Исправилось после указания
для GDI+
29.08.2017 в 20:04
Do you have any suggestions on how to get listed in Yahoo News?
I've been trying for a while but I never seem to get there!
Thanks