| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Для новичков > Корректная выгрузка пакеа bpl |
| Автор: Avers 16.12.2008, 17:32 | ||||||
| У меня имеется пакет bpl (Пакет1) и приложение, загружающее ("вручную") Пакет1. Получение интерфейса происходит через экспортируемую процедуру, как описано http://forum.vingrad.ru/forum/topic-238685.html (сама тема особого интереса не представляет и к делу не имеет отношения). В пакете хранится переменная-указатель на создаваемый экземпляр некоторой формы. В модуле пакета (единственном) есть код:
При выгрузке пакета (вручную из приложения)
Все нормально. Но при завершении программы вознкает ошибка.... Воспользовался EuricaLog - выяснил, что ошибка из RTL100. В графе "модуль/процедура" написано _IntfClear В чем может быть причина ошибки??? И как поступить со следующей функцией, при выгрузке пакета:
где CProcName - некоторая константа. |
| Автор: Bose 16.12.2008, 18:26 |
| Avers, а интерфейс где-нибудь освобождается? |
| Автор: Avers 17.12.2008, 12:05 | ||
Bose, подумал и об этом. И просто убрал данную переменную, т.е. код стал следующим:
В приложении интерфейс просто присваивается nil перед выгрузкой пакета. Добавлено через 11 секунд Не помогло :( |
| Автор: MetalFan 17.12.2008, 12:34 |
а накой убиваешь еще раз форму, если интерфейс ее прибьет, когда его счетчик обнулиться? |
| Автор: Avers 17.12.2008, 12:40 | ||
Не поверите - ничего. В стеке (EurikaLog'а) запись только о данном модуле. Добавлено через 3 минуты и 28 секунд
Если данную строчку убрать, то шибка становится еще жестче=))) вылетает с завидной регулярностью (пару сообщений в секунду) до тех пор, пока не будет убит процесс (само приложение). Добавлено через 5 минут и 39 секунд Есть одно решение - не выгружать пакет, тем более, что программу могут запустить еще раз, либо другую программу, которая использует этот же пакет..... Тогда и ошибок никаких не вылетает. Но это не есть выход... :( Конечно, 700 Кб памяти - в наше время ничто, но все же... не хочется, чтобы пакет висел в памяти до самого завершения работы машины... |
| Автор: CodeMonkey 17.12.2008, 12:47 | ||||||
Включите Use Debug DCUs и Stack Frames для пакета и exe и сделайте обоим Build. P.S. Кстати, было бы также неплохо выключить run-time пакеты в exe и убрать всё из requires пакета. Плюс поменять LoadPackage/UnloadPackage на LoadLibrary/UnloadLibrary. Но такое кардинальное изменение - потом. Если не получится по-другому. Вообще, в EL есть некоторые проблемы с этим делом - я уже описывал их в http://forum.vingrad.ru/index.php?showtopic=235599&view=findpost&p=1697491. Запустите под отладчиком, вызовите исключение, выпадите в отладчик - и посмотрите в окно Call Stack. Добавлено через 9 минут и 31 секунду
В обратную сторону тоже могут быть проблемы (строго говоря, вы код не покази, может у вас такой проблемы и нет, но вы всё равно рассмотрите этот сценарий). Освобождается интерфейс -> удаляется форма -> ссылка MyForm теперь указывает на мусор. Выгружается пакет -> выполняются секции finalization -> вызывается десктруктор MyForm, которая является битой ссылкой. Результат: очень интересное поведение программы. Если в деструкторе TMyForm вставить MyForm := nil; то это уберёт ошибку. Плюс, вы сможете продиагностировать: освободили ли интерфейс, перед выгрузкой пакета:
Аналогично можно сделать и для других интерфейсов. P.S. Строго говоря, в деструкторе уже стоит такая проверка. Но сообщение там, мягко говоря, маловразумительное. |
| Автор: CodeMonkey 17.12.2008, 13:02 | ||
Если я ничего не путаю, то COM, например, использует такую технологию: время от времени вызывает функцию, которая говорит, можно ли выгрузить сейчас эту DLL. Т.е. DLL выгружается не сразу, а как только будет освобождён последний её интерфейс. В принципе, так и надо делать по-правильному. Но лично мне тоже такой вариант не нравится - ну вот хочется мне сделать явную выгрузку. Но при этом нужно всё аккуратно реализовать. Потому что возможны проблемы как у вас - а именно: смешение ручного и автоматического управления жизненным циклом. Выгрузка DLL/пакета явно - это есть ручное управление. Использование интерфейсов - автоматическое. Реализация должна быть очень аккуратной, чтобы не напортачить в этом смешении. |
| Автор: Avers 17.12.2008, 13:23 | ||||
Код, касающийся интерфейса и класса (модуль пакета):
Такие имена у интерфеса и формы в реале=)) Так я получаю интерфес в приложении:
Еще раз повторюсь - выгрузку пакета пробовал разную, так же много раз правил часть финализации в пакете. Имхо, дело в экспортируемой функции.... не нравится она мне. |
| Автор: CodeMonkey 17.12.2008, 13:35 | ||
Что стоит в ... между:
??? Если в многоточиях ничего не стоит, то уберите MyForm.free и перебилдите всё. Проверьте. Функция здесь ни при чём. У вас где-то имеет место быть неаккуратное обращение с интерфейсом. |
| Автор: Avers 17.12.2008, 13:49 | ||
Между есть обращение к функции и свойству интерфейса. ... Пробовал.... Ошибки летять одна за одной.... Где-то что-то проглядел. Чем больше бьюсь над проблемой, тем больше убеждаюсь, что решение оч. простое. |
| Автор: CodeMonkey 17.12.2008, 13:56 | ||
Подробнее опишите. Пока не ясно ничего. Какая ошибка (исключение? Класс?)? В какой момент вылетает (во время работы? после выгрузки? в момент выхода?)? Сообщение? Содержимое Call Stack? Что EurekaLog говорит? Почему мы должны всё это угадывать? Может код покажете? Давайте вы всё же сделаете: а). Включите Use Debug DCUs и Stack Frames для пакета и exe и сделайте обоим Build. Запустите под отладчиком, вызовите исключение (выйдите из программы), выпадите в отладчик - и посмотрите в окно Call Stack среды IDE (View/Debug windows/Call Stack). Сообщите сюда его содержание. б). Убираете MyForm.Free, убираете все лишние действия между GetInterfaceFunction и об-nil-ением интерфейса - чтобы код был в точности такой, какой вы сюда выложили. Всё перебилдите и проверите на работоспособность. |
| Автор: Avers 17.12.2008, 14:47 | ||
Т.е. ошибки возникают при завершении работы приложения. Сама выгрузка пакета происходит вполне корректно. Ошибка: Access violation at address 2000A264 in module 'rtl100.bpl'. Read of address 05580C9C. EL Пишет подробнее: 2.3 Module Name : rtl100.bpl - (CodeGear Component Package) 2.4 Module Version: 11.0.2627.5503 2.5 Type : EAccessViolation 2.6 Message : Access violation at address 2000A264 in module 'rtl100.bpl'. Read of address 05580C9C. 2.7 ID : F599 Use Debug DCUs и Stack Frames - включил. Мало чего дало. Пишет следующее: :2000ф264 TClassHelperBse._Create + $C :7c816fd7 kernel32.RegisterWaitForInputIdle + 0x49 И все. Причем, в данном случае ошибка вылетела при запуске (!!!). Хотя, раньше вылетала при завершении...... Че-то ваще ни че не понимаю....... Дело вот еще в чем. Раньше я получал интерфейс, получая перед этим класс формы, создавая форму в приложении.... Тогда при выгрузке все было нормально. |
| Автор: CodeMonkey 17.12.2008, 15:16 | ||||
В такой схеме интерфейс не использовался для управления жизнью формы. Тут и нет места, чтобы напутать. Это вы сейчас какой случай описали? Если это начальная ситуация из самого вопроса: то почему ошибки, если раньше вы говорили про ошибку? Если же это ответ на мой вопрос про поведение после убирания MyForm.Free, то где же тут:
Если способы а и б из моего предыдущего поста не помогут, то есть ещё вариант отключить на время тестов пакеты. |
| Автор: Avers 18.12.2008, 10:56 | ||||||
Окзалось, что - не выгружать пакет - есть единственное решение. 1. Закоментил строки
2. Проверил, остается ли пакет в памяти, ибо навмеревался попытаться выгрузить его после завершения программы. Оказалось, что пакета в памяти нет..... почему, не понимаю. Но факт, кто каждый раз при запуске приложения при таком коде:
Пакет загружается заново. Напрашивается вывод, что при завершении приложения винда пыталась выгрузить пакет (что она в общем-то теперь и делает) и из-за этого ошибка и возникала..... Странно, потому что пакет загружается вручную. |
| Автор: CodeMonkey 18.12.2008, 11:25 | ||
Не обижайтесь, но сейчас вы написали полный бред. Вам серьёзно нужно занятся изучением http://wm-help.net/books-online/book/59464/59464-23.html#h1 (весь код, связанный с C/C++ можете игнорировать). В частности: http://wm-help.net/books-online/book/59464/59464-23.html#h1t3p4 или поизучайте описание http://msdn.microsoft.com/en-us/library/ms682658(VS.85).aspx. |
| Автор: Avers 18.12.2008, 11:50 |
| Благодарю за ссылки (но времени на чтение нет :(( - зачетная неделя, сессия + работа). К критике отношусь вполне адекватно (т.е. обижаться не стану). Вопрос: можете в двух словах пояснить суть загрузки и выгрузки динамических библиотек (насколько знаю bpl - почти та же dll). Где-то наталкивался на информацию, что dll, вручную загруженную, требуется и выручную выгружать (т.е. сама она ни куда не денется). Мне видится логичным, что евсли файл загружен в память, ему присвоен Handle, значит, этот Handle я могу получить и из другого приложения. Зачем же тогда вообще проверка на наличие: загружен ли пакет в память, если у меня было бы только одно приложение, использующее данный пакет. |
| Автор: Romikgy 18.12.2008, 12:09 | ||
при убийстве основного потока.процесса , все что им было загружено все тоже улетает , если не юзают данную библиотеку в другом процессе |
| Автор: CodeMonkey 18.12.2008, 12:11 |
| Короче, после ваших слов о "пакете, остающемся в памяти после завершения приложения" я понял, что вы, скорее всего, и в пакетах ничего не понимаете. Посему вот: демка в аттаче. Целых три (!) различных варианта реализации пакетов с интерфейсом, формой и ручной выгрузкой. Реализации используют различные схемы управления жизненным циклом - хорошо бы разобраться в этих примерах, прежде чем использовать их. Примеры набросал на скорую руку - подправьте там где что надо (пути загрузки пакета и т.п.). Приложение, разумеется, нужно компилировать с run-time пакетами. |
| Автор: CodeMonkey 18.12.2008, 12:17 |
| Очень грубо и кратко: в Win32 каждый процесс не зависим от другого. У каждого процесса есть своё адресное пространство. Поэтому любые адреса, дескрипторы и т.п. имеют смысл только в рамках текущего процесса. При завершении процесса все его ресурсы автоматически освобождаются. Далее, при загрузке DLL, пакета (это действительно http://gunsmoker.blogspot.com/2008/12/1.html) в адресное пространство пакет загружается в память. При этом загрузка может быть явной (LoadLibrary/LoadPackage) и неявной (procedure ... external 'mydll.bpl'). Вы можете загружать пакет/DLL и несколько раз. В этом случае просто увеличивается счётчик числа загрузок. При выгрузке - он уменьшается. Физически DLL будет выгружаться только когда счётчик равен 0. Разумеется, поскольку процессы изолированы друг от друга, то в одном процессе библиотека может быть загружена 5 раз, а в другом - и вовсе ни одного раза. Поэтому теоретически в них тоже возможны ошибки ;) Использовать только после проверки. Это просто демонстрация идеи, "proof of concept". |
| Автор: Avers 18.12.2008, 12:20 |
| Спс)) Вот еще вопрос. Приложение запущено, пакет загружен, выведена форма из пакета. Запускаю вторую копию этого же приложения, которая пытается найти уже загруженный пакет с помощью GetModuleHandle, но все равно не находит и грузит его заново..... Вапще не понимаю, почему. Смысл тогда проверять наличи пакета в памяти?..... |
| Автор: CodeMonkey 18.12.2008, 12:22 |
А я не знаю, зачем вы это делаете |
| Автор: Romikgy 18.12.2008, 12:57 |
вроде он дает ответ , был ли загружен что то в своем же процессе! |