Виталий Курчанов дата публикации 28-05-2008 09:34 Взгляд на работу с DLL-библиотеками
При использовании в своей программе нескольких DLL-библиотек, как мне кажется, возникает определенный ряд проблем, связанных с организацией обращений к импортируемым функциям. Разумеется, можно организовать один или несколько модулей импорта, комбинируя подключение к которым в различных формах, диалогах и модулях программы, получаем неплохо структурированный подход к использованию DLL. Однако, основным подводным камнем в таком подходе является странность применения DLL-библиотек. Дело в том, что при варианте статической или даже "квазидинамической" (разъяснение см. ниже) загрузки теряется смысл использования преимуществ DLL, связанных с оптимизацией выделения-освобождения памяти. Иными словами, DLL-библиотеки подключаются на каком-то предварительном этапе загрузки программы и освобождаются при выходе из нее. При статической загрузке отсутствие библиотеки приводит к появлению окон программных ошибок и исключений, которые, как правило, аварийно завершают работу программы.
Относительно используемого термина "квазидинамическая" загрузка — при таком подходе в модуле импорта используется ссылка handle и указатели на процедурные типы, т.е. по сути, задействован режим динамической загрузки, однако собственно загрузка самой DLL-библиотеки в память производится специальным вызовом один единственный раз, перед использованием остальных функций. Для хранения handle библиотеки можем использовать глобальную переменную.
К основной выгоде "квазидинамической" загрузки я отношу возможность проверки именно загрузки библиотеки — т.е., даже если присутствует какой-то идентичный по названию файл, еще не факт, что речь идет о необходимой нам версии DLL-библиотеки. При этом возникает гибкость в работе программы: например, библиотека отвечает за экспорт данных или за генерацию отчетов — при её отсутствии в комплекте ПО, программа будет работать нормально, а в части отсутствующих веток можно организовать проверку и изменение поведения (кнопки недоступны и т.д.).
Если обобщить, то "квазидинамическая" загрузка DLL-библиотеки — это всего лишь один из режимов вызова функций импорта при динамической загрузке (по адресу процедуры и т.п.), этот режим позволяет избежать процесса многократного повторения выделения и освобождения памяти, в нем используется ссылка handle, полученная единожды.
Так или иначе, очевидным становится тот факт, что, при реализации отдельного модуля импорта, в основной программе просто вызывается набор процедур, который целиком обеспечивает должное общение с DLL-библиотекой в виде, как если бы она была чем-то вроде модуля данных, но без возможности доступа к его переменным.
При наличии нескольких DLL-библиотек, подключаемых, например, "квазидинамически", на каком-то этапе, когда мы замечаем, что в модулях импорта приходится прописывать одинаковый код, — возникает необходимость в создании некоего объекта, который бы брал на себя часть функций фабрики классов (термин из проектирования). Говоря проще, хотелось бы, чтобы он (объект, класс) отвечал за загрузку (и проверку загрузки) DLL-библиотек, хранил бы в себе (в виде коллекции объектов или записей) параметры, которые характеризуют любую библиотеку в программе (имя, вид загрузки и т.п.), отвечал бы за освобождение DLL-библиотек и ссылки на них через массив handle.
Довольно эффективным мне представляется создание компонента-менеджера DLL-библиотек, что позволит, во-первых, визуально задавать и оценивать какие-то параметры (например, вид или режим загрузки) для коллекции элементов (каждый из которых — это отдельная DLL-библиотека); во-вторых, использовать указание и проверку имен файлов непосредственно во время проектирования (что проще) и тогда же проверять возможность загрузки (логично предусмотреть и это).
Центральной же задачей такого компонента-менеджера DLL следует считать обеспечение модулей импорта ссылками handle и обеспечением загрузки и проверки DLL (т.е. операций, общих для всех библиотек). Наличие такого компонента позволяет частично унифицировать работу с библиотеками; такой компонент является общим для любой программы и должен обеспечивать различные режимы работы с библиотеками. При этом, параметры режимов, ссылки и имена конкретных файлов хранятся централизованно и могут быть просмотрены и проверены непосредственно во время проектирования (designtime); сам же компонент удобно располагать, например, в центральном модуле данных программы. Очевидно, что и использование не разрозненных функций проверки, а методов единого объекта упрощает понимание кода программы и её логики работы.
Развитием рассмотренного подхода следует считать появление еще и компонента DLL-коллектора, основная задача которого — это подключение указателя на наш компонент в конкретных диалогах программы.
Поясняю, для чего это необходимо: некоторые диалоги программы зачастую являются модулями, более общими; возможно их использование не только в контексте определенного приложения, особенно, если они подвязаны к каким-то DLL-библиотекам. Тогда крайне нежелательно использовать в данных диалогах ссылки на модуль данных конкретной программы или конкретный компонент — DLL-менеджер; с другой стороны, если требуется вызов функций из DLL, необходимо видеть свойства менеджера, который задействован в программе. Для этого, на мой взгляд, логично использовать отдельный компонент-коллектор, который будет расположен в том же модуле, что и компонент-менеджер, и будет иметь доступ к менеджеру, активному в данный момент (разумеется, вводя понятие "активности", следует предусмотреть механизм установки этого качества для менеджеров).
Тогда в диалог помещается только коллектор, который производит поиск в активном DLL-менеджере нужной библиотеки (например, по её имени) и возвращает ссылку на нее (после проверки доступности и т.д.) для модуля экспорта. В итоге получаем диалог, работающий с DLL-библиотеками (одной или несколькими), который абстрагирован от привязки к конкретным файлам, и напрямую не зависит от каких-либо модулей конкретного проекта.
К моему сожалению, мне не удалось на данный момент отыскать в каких-либо пакетах или отдельных компонентах приемлемого для меня DLL-менеджера, поэтому я написал свой вариант, обеспечивающий почти минимальный набор возможностей для работы с DLL-библиотеками. В моём варианте не содержится конкретных примеров использования, и даже есть пара вещей, которые я хотел бы непременно исправить в последующих версиях, однако вариант этот вполне работоспособен и активно мной применяется в разработке.
К материалу прилагаются файлы:
[Использование и создание DLL]
Обсуждение материала [ 31-05-2008 11:27 ] 6 сообщений |