Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.02.25;
Скачать: [xml.tar.bz2];

Вниз

Если еще кому не надоело про DLL   Найти похожие ветки 

 
DarkUser   (2004-02-12 15:30) [0]

Доброго времени суток мастера и адепты.
В процессе работы возникла проблема, которая требует ответа если не "как?" то хотя-бы "почему?!"

И так в чем собссна проблема:
Была задача... была идея... задача - самая обыкновенная - бухгалтерская. Идея - то-же ничего гениального - реализовать программу в виде одного ЕХЕ-шника, и кучи ДЛЛ-ок, что в виду того что над задачей работает не один человек, а даже 4-5, было наиболее актуально...
И был создан ЕХЕ-шник, и были созданы 2-3 модуля на ДЛЛ...
Нет, проблемы начались не после подключения, и даже не во время... проблемы начались еще раньше, когда я передаваемый в ДЛЛ Хэндл базы(IntegBase... если точнее - FireBird 1.0) пытался подключить не к IBX-компонентам(как делали все остальные участники проекта), а к FIBPlus(выпендрится решил... блин)... так вот, данный финт вызывал стойкое, ничем не перебиваемое сообщение - Access Violation at ...... сразу после присвоение Хэндла... причем подобные перации в ЕХЕ-шнике проходили на ура и даже не пикали... плюнул... перешёл на IBX... забыл... но на этом к сожалению все не закончилось...
Программа работала, ДЛЛ-ки цеплялись... примерно 5-10% вызовов форм из ДЛЛ-ок заканчивались Access Violation-ами... мы списывали это на случайные факторы и недоотлаженность кода... пока однажды(совершенно случайно), не заметилась тенденция - если в ДЛЛ-ошнй форме нет RXSplitter-а или его не трогать все нормально... если же его хоть слегка дернуть - Access Violation гарантирован...
Задумались... в результате долгих размышлений, пары пачек сигарет и пол-литра спирта, родилась новая идея:
в изначальном варианте в ДЛЛ передавались ЕХЕ-шниковский Application и порядковый № формы, которую нужно было вызвать, а ДЛЛ забирала себе Application и создовало соответствующую форму в Модальном или MDI стиле(в зависимости от проставленного стиля формы).
в новом варианте ДЛЛ опять-же забирала Application(без этого почему-то не обошлось) и возвращала ЕХЕ-шнику класс формы, который тот принимал как TForm и уже сам делал с ним что хотел... проблемы исчезли... нормально работает RXSplitter, нормально работает FIBPlus...
ВСЕ РАБОТАЕТ!!! ну или почти всё :) вот только никак не могу понять - и там и там один Application передаваемый в из ЕХЕ-шника в ДЛЛ, и там и там один и тот-же код... и там и там, Application.MainForm.MDIChildren в ЕХЕ не видит MDIChild форм созданных в ДЛЛ(насколько смогли понять - сообщения винды не работают)...
Так в чем-же разница?? почему некоторые компоненты, которые глючили или вообще не работали в 1-м случае, на ура работают во 2-м???
КТО НИБУДЬ МОЖЕТ ОБЪЯСНИТЬ??? НУ ПОЖАЛУСТАААААА!!!!!

ЗЫ сорри за слишком длинные и пространные объяснения...
ЗЗЫ заранее спасибо если вдруг кто отклинется, или хотя-бы ткнет носом где искать...
ЗЗЗЫ если вдруг кто-нть таки заинтересуется - код за мной...


 
Sandman25   (2004-02-12 15:47) [1]

Если и dll, и host пишутся в Delphi, то рекомендуется использовать BPL, а не dll. Тогда таких проблем не будет.


 
KSergey   (2004-02-12 15:49) [2]

Я правда не знаю как вы именно все это организовали, однако надо четко понимать, что объект Application для exe и каждой dll - свой.
Причем как экземпляр, так и класс этих Application в общем случае - разный!

А потому очень настораживает фраза "передавались ЕХЕ-шниковский Application" - в смысле ссылка на экземпляр? При этом принимающая (ссылку на экземпляр) сторона предполагала, что имеет дело со своим классом TApplication, что в общем случае совершенно не верно.

Подозреваю, что причина всех этих глюков именно в этом.

Если есть желание вот так вот смело гонять туды-сюды именно ссылки на экземпляры объектов - то надо переходить на bpl.


 
just me   (2004-02-12 15:59) [3]

На bpl переходить необязательно, но собрать все с run-time packages прийдется (выбрать только необходимые для уменьшения состава дистрибутива). И еще лучше - оформить все загружаемые модули не просто как dll, а как ActiveX dll


 
DarkUser   (2004-02-12 16:01) [4]

2 Sandman25 пасиб большое кнешна, но оно и сейчас работает(тьфу-тьфу-тьфу) без проблем(более-менее), интересует именно разница между 1-м и 2-вариантами.

2 KSergey

<in DLL>
var DLLApp:TApplication;
....(в процедуре инициализации ДЛЛ)
Application:=AApplication;
...
procedure DLLEntryPoint(dwReason:DWORD);
begin
case dwReason of
DLL_PROCESS_DETACH:Application:=DLLApp;
end;
end;
...

begin
DLLApp:=Application;
DllProc:=@DLLEntryPoint;
end.
(все в главном модуле ДЛЛ)

естессна передавался екземпляр.
не могли-бы вы объяснить почему именно не верно??
(во всех документах по работе с формами в ДЛЛ на дельфи описывается именно этот метод, как я понимаю он необходим, что-бы MDIChild формы могли найти MDI-главную форму)


 
DarkUser   (2004-02-12 16:04) [5]

2 just me а вот не подскажете, где-нть вообще написанно про такую особенность run-time packages, шо прям все проблемы с ДЛЛ решает?
(шо решает уже догодался, но вот какой прынцып?? :-)) )


 
just me   (2004-02-12 16:28) [6]

Наверное, в книжках хороших или в доках..
а Суть в том, что если собрать хост-апликацию и ДЛЛ run-time packages, то начинает честно работать механизм RTTI и классы TForm, например, в хосте и в ДЛЛ являются одним и тем же классом. Если не собирать с run-time packages, то это будут 2 разных класса со своими отдельными реализациями. Во многих случаях это не очень страшно, но есть и проблемы - о некоторых ты и сам уже упомянул


 
icWasya   (2004-02-12 16:32) [7]

вся проблема в том, как выполняется следующий код

var
O:TObject;

if O is TObject then ....

Для проверки принадлежности обекта какому-нибудь классу сравниваются адреса таблиц виртуальных методов. В рамках одного приложения это срабатывает, а EXE и DLL изпользуют каждая свои.
и по этому в приведённом тобой коде если напишешь

....(в процедуре инициализации ДЛЛ)
Application:=AApplication;

if Application is TApplication then
showmessage("OK!")
else
showmessage("Error!")
...

то получишь сообщение Error!

При использовании BPL эта проблема решается следующим образом -
ни приложение, ни DLL сами не содержат объявления базовых классов - они выносятся в отдельные пакеты.


 
KSergey   (2004-02-13 07:09) [8]

Да, вспомнил еще одну прибабаху с DLL: если не используется ShareMem, то, кроме всего прочего, еще и менеджеры памяти будут у exe и каждой dll свой! Вот где поле для глюкищев!!!!

(в одном модуле ссылки на некие объекты ушли из поля видимости, менеджер памяти с чистой совестью память из под объектов освободил; а мы в другой модуль передавали ссылку на сей объект - но кто об этом догадывается???)

А вообще я еще ни разу не встречал, чтобы так вот передавали ссылку на сам объект Application. Как я понял, вы делаете обычную дельфийскую DLL, т.е. используете модуль Forms? Так вот объект Application для приложений и DLL тоже по разному функциклирует! (В Пачеке с соавтором написано даже "эти объекты различны", хотя мне и не понятно чем различны именно объекты.) А вы в DLL просто так вот ссылку на него передаете - как там барахтается созданный экземпляр Application для DLL и как DLL управляется с подсунутым ей объектом Application из приложения - не знает никто...

Классический путь - в DLL передается Application.Handle, в DLL это значение присваивается собственному Application.Handle. Не более.


 
DarkUser   (2004-02-13 07:33) [9]

2 icWasya я может чего не понимаю, но у меня выдает строго "ОК"
(для вышеприведенной конструкции)

2 KSergey передачи Application.Handle в ДЛЛ или самого экземпляра Application дела не меняют(я имею в виду что поведение программы ни в 1-м ни во 2-м случае не изменяются)


 
KSergey   (2004-02-13 07:44) [10]

> DarkUser © (13.02.04 07:33) [9]
> 2 icWasya я может чего не понимаю, но у меня выдает строго
> "ОК"
> (для вышеприведенной конструкции)

Сам не проверял, но соласно литературе этого не может быть.
И в данном случае я склонен доверять литературе, опасаясь некорректности эксперимента.


 
DarkUser   (2004-02-13 07:54) [11]

2 KSergey
хммм, я кнешно то-же обычно доверяюсь литературе... вот тока:

function GETFORMCLASS(AApplication:TApplication;
AFormNumber:Integer; ADLLName:string):TFormClass; stdcall;
begin
if AApplication is TApplication
then showmessage("OK")
else ShowMessage("Error");
Application:=AApplication;
<Здесь экспорт соотв. класа формы, содержащегося в ДЛЛ>
end;

так вот, а где некорректность??

ЗЫ И еще раз, передоватся может не сам Application, а Application.Handle! поведение программы не изменяется, и вопрос остается!! почему в 1-м случае не работают некоторые компоненты, в частности RXSplitter и FIBPlus-компоненты, а во 2-м усе на ура?!?


 
Verg   (2004-02-13 08:21) [12]


> 2 just me а вот не подскажете, где-нть вообще написанно
> про такую особенность run-time packages, шо прям все проблемы
> с ДЛЛ решает?
> (шо решает уже догодался, но вот какой прынцып?? :-)) )


А принцип такой. Глянь в исходники VCL и ты увидишь, что каждый экземпляр модуля, пусть даже classes, имеет туево число статических переменных, управление динамическими конструкциями тоже получается базируется на разных "экземплярах" менеджера кучи в DLL-ке и в exe-нике (классика жанра - Huge String). И если в одном случае тебе помогла передача экз. Application, то в другом каком-нибудь и этого будет недостаточно. Мало того, получается, что и применение ShareMem тоже решает далеко не все проблемы. Недавно тут обсуждался эффект неработающего метода Synchronize. Так пот - это из той же оперы.
И чтобы ростоянно не латать эти дыры можно все это решить одним махом - Build with RTP.


 
DarkUser   (2004-02-13 09:15) [13]

2 Verg Пасиб... на Build with RTP не пойдем, и так неплохо работает :). и чессна говоря не совсем понятна шо такое "экземпляр модуля", но лана...

и так, вариянт 1-й

{в головном модуле Dll}
var DLLApp:TApplication;

procedure InitDLL(AApplication:TApplication)
begin
Application := AApplication;
end;

procedure DLLEntryPoint(dwReason:DWORD);
begin
case dwReason of
DLL_PROCESS_DETACH:Application:=DLLApp;
end;
end;

begin
DLLApp:=Application;
DllProc:=@DLLEntryPoint;
end.
.........................
{экспортируемая функция}
function GetForm(AOwner:TComponent; AIBDB:TIBDatabase;
AFormId:Integer):TForm;
begin
case AFormId of
1, 2: Result := TfrmMain.Create(AOwner, AIBDB);
101: Result := TShowOnlySPR.Create(Owner, AIBDB);
else Result := nil;
end;
end;
.........................
{В самом классе формы}
procedure TLxSimpleSprFrm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action := caFree;
end;


вариянт 2-й

{в головном модуле Dll практисски то-же самое}
.........................
{експортируемая процедура}
function GETFORMCLASS(AFormNumber:Integer;
ADLLName:string):TFormClass; stdcall;
begin
case AFormNumber of
1, 2: Result := TfrmMain;
101: Result := TShowOnlySPR;
else Result := nil;
end;
end;


а теперь уважаемые знатоки, внимание вопрос!!! :-)
почему в 1-м случае не работают некоторые компоненты, в частности RXSplitter и FIBPlus-компоненты, а во 2-м усе на ура?!?
в чем разница-то??? ведь и там и там код формы находится в Dll, и там и там для того что-б нормально отображались MDIChild формы нужно передавать Application.Handle(либо напрямую либо через екземпляр Application) и почему ни там, ни там не доходит из ЕХЕ в Dll виндовское сообщение WM_MDIGETACTIVE


 
Verg   (2004-02-13 09:34) [14]


> и чессна говоря не совсем понятна шо такое "экземпляр модуля",
> но лана...


Чего ж тут непонятного-то?
Например, classes, который у тебя использует и exe-шеник и DLL-ка линкуется к каждому из модулей отдельно. Соответственно при работе, обращаясь к функциям из classes exe-шник работает со своим экземпляром classes, а dll-ка со своим. Это же касается и Forms и многих других модулей.
Ну "выровнял" ты Application, а может еще чего надо выравнивать? Screen, например. А в модуле classes - там еще гора переменных, которые получаются физически разными для разных экземпляров модуля. А менеджер кучи, который в System....

> почему в 1-м случае не работают некоторые компоненты, в
> частности RXSplitter и FIBPlus-компоненты, а во 2-м усе
> на ура?!?


Разобраться можно, но почти уверен, что причина будет именно где-то в том, о чем я тебе пытаюсь сказать.


 
Mystic   (2004-02-13 11:15) [15]

> на Build with RTP не пойдем, и так неплохо работает :).

А зря. BPL + Build with RTP на 99% решило бы проблему. Иначе не исключаю вариант, что заставить все это работать не получиться.


> Идея - то-же ничего гениального - реализовать программу
> в виде одного ЕХЕ-шника, и кучи ДЛЛ-ок, что в виду того
> что над задачей работает не один человек, а даже 4-5, было
> наиболее актуально...


А средства коллективной разработки уже отменили?


 
DarkUser   (2004-02-13 12:17) [16]

2 Mystic так в том-та и дело, что проблемы уже нету... т.е. есть одна, в том что главная MDI-форма, не видит дочерние, созданные в Dll-ках, но нам это собсна и не нада... нужда заставит, перейдем на Build with RTP, а пока и так хорошо...

>А средства коллективной разработки уже отменили?
нууу... эээ... вощем так удобнее :-)
к тому-же причина несколько шире... каждый из разработчиков, в идеале отвечает за какую-то свою часть. программа будет распостронятся в туеву хучу областей и районов, и при каких-либо доработках и исправлениях, проще отправить одну Dll-ку весом от пол-метра до до полутора, чем весь Exe-шник, который и за 10 метров может перевалить :-)


 
Mystic   (2004-02-13 13:16) [17]

> что проблемы уже нету...

Будут ;)

> проще отправить одну Dll-ку весом от пол-метра до до полутора,
> чем весь Exe-шник, который и за 10 метров может перевалить
> :-)


1) Build with runtime packages и размер (EXE,DLL) стремительно падает. Правда вначале прийдется перенести больше

2) Рискуешь напороться на ошибку, когда DLL работает правильно с вашей версией EXE- DLL- файлов, но неправильно работает у заказчика.

Впрочем, думать надо было раньше... На будущее --- при крупных проектов использовать
а) средства коллективной разработки
б) не DLL, а пакеты (COM-сервера)
в) Build with runtime packages


 
KSergey   (2004-02-13 13:37) [18]

> [13] DarkUser © (13.02.04 09:15)

Не поверил. Проверил.
Да, либо я что-то недопонимаю, либо в книгах что-то недоговаривают (но скорее первое, а копать глыбже ужо лень.

Вот мои результаты:

// экспортированная функция из DLL

procedure DllFunc(const App: TApplication); stdcall;
begin
with TForm1.Create(Application) do
try
if App is TApplication then label1.Caption := "YES!!!"
else label1.Caption := "NO!!!";
{1} ShowModal;
if TObject(App) is TApplication then label1.Caption := "YES!!!"
else label1.Caption := "NO!!!";
{2} ShowModal;
if App = Application then label1.Caption := "YES!!!"
else label1.Caption := "NO!!!";
{3} ShowModal;
finally
Free;
end;
end;


В первом случае печатает "Yes!!!" (!!!!) Почему - не понятно мне. Думал оптимизация может - при отключенной та же фигня. Почему - вникать уже лень.
Утешает лишь, что 2 и 3 печатают "No".

> [13] DarkUser © (13.02.04 09:15)
> почему в 1-м случае не работают некоторые компоненты, в
> частности RXSplitter и FIBPlus-компоненты, а во 2-м усе
> на ура?!?


В первом создается экземпляр объекта по правилам DLL (ее менеджером памяти), а юзается внутри приложения. Это грубейшая ошибка. Как сей гибрид себя поведет - не знает никто.
Во втором возвращается лишь ссылка класс, создается и уничтожается бъект в рамках одного менеджера памяти (приложения).
Вообще-то, есть подозрения, что и это может выйти боком, хотя тут думать надо. Книжки читать. Возможно, при определенных условиях вариант 2 даже позволителен.
Хотя все равно это все подозрительно.

Классическим вариантом работы с DLL в дельфи считается присвоение Application.Handle из приложения в DLL (что, вообще говоря, позволяет лишь привязать формы, создаваемые в DLL к общему главному (невидимому!) окну приложения, дабы кнопочка на панели задач была одна.
Все, на этом все связи между DLL и EXE должны прекратится, никакие другие дельфийские ссылки (кроме Pointer"ов) передавать нельзя! (дельфийские подчеркнуты, т.к. ссылки на объекты Windows передавать вполне можно, лишь бы время их жизни было не меньше, чем требуется в DLL; в частности можно передавать внутренний объект Connection, создаваемый внутри дельфийских TxxConnection для доступа к БД из форм DLL через один коннекшен)
После формы должны создаваться и уничтожаться в рамках самой DLL - и все будет тип-топ.

PS
Ох, сколько матов будет от пользователей, у которых все это будет плющить и таращить...
Не, у вас явные ошибки проектирования
Советую избавится от них, пока не поздно


 
KSergey   (2004-02-13 13:40) [19]

Да, забыл добавить по своему "исследованию": внутри exe во всех случаях, как и ожидалось, печатает Yes


 
KSergey   (2004-02-13 13:42) [20]

Дополнение:

> (кроме Pointer"ов)

имелось в виду "кроме поинтеров на выделенныю память, либо на объекты Windows"


 
DarkUser   (2004-02-13 14:27) [21]

2 Mystic
> Будут ;)
НЕ БУДУТ!!! :-))
а будут, поставим галочку "бюилд виз ран-тай пакэйжэс" или будем решать по другому...


> 2) Рискуешь напороться на ошибку, когда DLL работает правильно
> с вашей версией EXE- DLL- файлов, но неправильно работает
> у заказчика.

проверялась на всех виндах, кроме 95-й и ХР(на них скорее всего программа ставится и не будет)

> Впрочем, думать надо было раньше... На будущее --- при крупных
> проектов использовать
> а) средства коллективной разработки
рассматривается
> б) не DLL, а пакеты (COM-сервера)
интересный вариант, буде время и желание может и реализуем
> в) Build with runtime packages
см. выше :)


2 KSergey
> Утешает лишь, что 2 и 3 печатают "No".
ну што-ж, хороший повод для утешения...
(в 1-м похоже действительна оптимизатор старается...)
кстати 3-й вариант, это што шутка???


> Классическим вариантом работы с DLL в дельфи считается присвоение
> Application.Handle из приложения в DLL (что, вообще говоря,
> позволяет лишь привязать формы, создаваемые в DLL к общему
> главному (невидимому!) окну приложения, дабы кнопочка на
> панели задач была одна.

угу-угу, тока вот "классический" вариянт не позволяет работать с MDIChild формами в dll-ках :(


> В первом создается экземпляр объекта по правилам DLL (ее
> менеджером памяти), а юзается внутри приложения. Это

СПА-СИ-БА!!!! хоть што-та проливающее свет на темное таинство програмирования :-) (а гланое звучит вполне правдоподобна и понятна!!)


> Ох, сколько матов будет от пользователей, у которых все
> это будет плющить и таращить...

пока работает совсем не плоха... (по крайней мере ни одного непонятного АВ за последний месяц)

> Не, у вас явные ошибки проектирования
> Советую избавится от них, пока не поздно

не-а, они(ошибки) нам даже очень нравятся...

ЗЫ А ведь существуют программы написанные на дельфях, которые весьма успешно создают и MDI и не MDI формы из dll-ок, и при этом безо всяких пакетов...


 
KSergey   (2004-02-13 15:21) [22]

Раз уж занимаетесь крупными проектами - куите книжку ;)
Посмотрел Пачеку с Тексейрой - там описана методика экспорта объектов из DLL, перечислены ограничения.
Правда про MDI не попалось, но вроде на форумах проскакивало. Но не помню что...



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2004.02.25;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.036 c
6-80075
Makep
2003-12-15 23:38
2004.02.25
Прослушивание UDP порта


6-80085
Cure
2003-12-04 23:11
2004.02.25
Звук по локалке


1-79889
Сергей Петров
2004-02-10 01:20
2004.02.25
Проблема с midas.dll


14-80297
Alone
2004-02-03 11:26
2004.02.25
Глюки Windows не являются ошибками


1-80021
8ung
2004-02-11 21:13
2004.02.25
Отсортировать TStringGrid.





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский