Стоит Windows 8 Pro, Visual Studio 2010 Express Edition . |
Динамически загружаемые библиотеки MFC
Создание и использование MFC extension DLL
Создание программ с использованием объектно-ориентированного подхода - это практика программирования, в основу которой положены те же принципы, что используются при любом техническом проектировании. И это существенно облегчает разработку, снижает затраты, уменьшает число ошибок, сокращает сроки и является приемом борьбы со сложностью в больших проектах.
DLL (Dynamic-Link Library) - динамически загружаемые библиотеки. Существуют два типа DLL
- Обычные DLL MFC - библиотеки ( MFC regular DLL )
- Расширенные DLL MFC - библиотеки ( MFC extension DLL )
Динамически подключаемая библиотека - это специального вида исполняемый файл с расширением .dll, используемый для хранения классов, функций и ресурсов отдельно от исполняемого файла.
Существует и другой способ хранения скомпилированного кода отдельно от исходного кода приложения - в библиотечных файлах с расширением .lib. Этот код включается в приложение линковщиком в процессе создания приложения. Такой процесс называется статическим связыванием. Код библиотеки хранится в исполняемом файле приложения. И если библиотеку использует несколько приложений, экземпляр кода будет включен в исполнимый файл каждого приложения.
Использование DLL обеспечивает динамическое связывание, когда код библиотеки загружается в память и присоединяется к приложению при его запуске на выполнение. Приложение, запущенное на выполнение, называется процессом. Если одновременно выполняются несколько процессов, то каждый процесс может загружать из DLL и присоединять к себе нужную функцию или даже все процессы одну и ту же функцию одновременно.
Применение DLL позволяет реконструировать и сопровождать функцию (или класс) только в одном экземпляре и все внесенные в нее изменения будут немедленно учтены любыми использующими ее приложениями без предварительной их перекомпиляции. Можно считать, что DLL является источником кода, а все упакованные в нее функции и классы - экспортируемыми функциями и классами.
Создание программ с использованием объектно-ориентированного подхода - это практика программирования, в основу которой положены те же принципы, что используются при любом техническом проектировании
Достоинства DLL
- Поскольку в DLL можно разместить функции, к которым обращаются многие приложения, то уменьшаются размеры исполняемых файлов приложений на этапе компиляции
- Если интерфейс DLL не изменяется, то ее можно заменить более новой, причем сами приложения обновлять не придется
- При надлежащем выборе типа DLL ее можно использовать в приложениях, написанных на других языках программирования для Windows
Динамически загружаемые библиотеки - это файлы с заранее откомпилированными кодами функций или классов, которые могут использоваться другими приложениями на этапе выполнения. Функция или класс добавляются в специальную таблицу, являющуюся неотъемлемой частью DLL. В таблице указывается местоположение всех функций и классов, содержащихся в DLL, что обеспечивает быстрый их поиск и вызов исполняемым внешним приложением при его загрузке в память.
Приложение может вызывать код из DLL двумя способами:
- По таблице определяется местоположение нужной функции, создается указатель на найденную функцию, с помощью указателя вызывается сама функция
- Приложение линкуется с обычным библиотечным LIB -файлом, в котором содержатся фиктивные модули программного кода (заглушки). При запуске приложения фиктивный псевдокод замещается реальным кодом из DLL
Заглушка - это псевдофункция, у которой имя и список аргументов совпадают с именем и списком аргументов реальной функции. Внутри функции-заглушки имеется незначительное количество кода, вызывающего реальную функцию из DLL, причем реальной функции при вызове передаются все те аргументы, которые были переданы заглушке. В этом смысле функции DLL можно рассматривать как часть кода приложения, а не как отдельный файл.
Второй способ значительно проще. Библиотечный LIB -файл при компиляции DLL создается автоматически. Для его создания не приходится предпринимать каких-либо особых действий. При использовании LIB -файлов все используемые в приложении DLL загружаются в память в момент запуска приложения и если некоторые из них отсутствуют, то Windows автоматически предупреждает об этом и приложение не выполняется.
Когда используется первый способ, разработчику приложения необходимо самостоятельно организовывать загрузку DLL в память и обрабатывать все ошибочные ситуации при отсутствии нужных DLL.
DLL создаются с помощью оболочки Visual Studio специальным мастером DLL Wizard.
MFC extension DLL
MFC extension DLL - это динамически загружаемые библиотеки, расширяющие библиотеку базовых классов MFC. Такой тип DLL легче всего создавать и кодировать. Чтобы сделать класс экспортируемым DLL, нужно при его объявлении указать макрос AFX_EXT_CLASS примерно так
class AFX_EXT_CLASS MyClass // Объявление класса { .................. };
Далее в приложении, использующем класс, достаточно будет включить этот макрос, и при запуске приложения будет импортирование из DLL нужный класс.
Одним из недостатков MFC extension DLL является то, что их нельзя использовать в программах, написанных на других языках программирования или в программах C++, компиляторы которых не поддерживают MFC. Но компиляторы фирмы Borland и Symantec поддерживают MFC extension DLL.
MFC regular DLL
MFC regular DLL - это обычные динамически загружаемые библиотеки. Этот тип DLL поддерживает стандартные функции, а не классы, поэтому планировать их приходится несколько тщательнее, чем MFC extension DLL. Внутри самой DLL классы можно использовать, но вызов кода внешним приложением нужно организовать через функцию.
Для обеспечения возможности экспорта функции библиотекой следует эту функцию объявить экспортируемой по такому синтаксису
extern "C" тип_возвращаемого_значения PASCAL EXPORT объявление_функции
Необходимо включить все эти дополнительные слова как в заголовочный файл прототипа функции, так и в исходный код ее реализации.
- Выражение extern "C" объявляет, что это вызов стандартной функции C, поэтому корректировщик имен C++ не должен корректировать имя функции
- Ключевое слово PASCAL сообщает компилятору, что все параметры функции передаются в порядке, принятом в языке PASCAL, т.е. параметры в стеке размещаются в порядке, обратном обычному
- Ключевое слово EXPORT информирует компилятор о том, что эта функция будет экспортироваться динамически загружаемой библиотекой во внешние приложения
Для создания DLL применяется специальный тип проекта оболочки Visual Studio. Чтобы сделать функции экспортируемыми DLL, следует добавить их имена в файл определений DEF.lib этого проекта. Файл DEF.lib представляет собой библиотечный (т.е. с расширением LIB ) файл заглушек и таблицу экспортируемых функций, содержащихся в DLL. В него включается имя DLL, ее краткое описание и имена всех экспортируемых библиотекой функций. Файл DEF проекта DLL автоматически создается мастером "DLL Wizard" и имеет определенный формат. Формат файла изменять нельзя, в него можно только добавлять имена экспортируемых функций.
Примерный вид файла определений DEF.lib
LIBRARY "mydll" DESCRIPTION 'mydll Windows Dynamic Link Library' EXPORTS ; Explicit exports can go here MyFun1 MyFun2 MyFun3
Если в MFC regular DLL используются классы из MFC, то первой строчкой тела всех экспортируемых функций должен быть вызов макроса AFX_MANAGE_STATE. Это диктуется необходимостью обеспечить многопоточную поддержку в функциях, благодаря которой можно вызывать функции класса одновременно несколькими потоками процесса. Макрос AFX_MANAGE_STATE имеет единственный аргумент - указатель на структуру AFX_MANAGE_STATE, который можно получить в результате вызова функции AfxGetStaticModuleState(). Типичная экспортируемая функция, использующая библиотеку MFC, имеет следующий вид
extern "C" void PASCAL EXPORT MyFun(список_аргументов) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Далее идет обычное тело функции ..................... }
При проектировании функций для DLL нужно соблюдать правила:
- Поскольку некоторые функции могут одновременно вызываться несколькими потоками процесса, то следует в них предусмотреть поддержку многопоточности
- Функции не должны обрабатывать глобальные переменные, а все необходимые значения для локальных переменных должны передаваться функциям через аргументы. Если же все-таки необходимо обрабатывать глобальные переменные функциями, то следует предусмотреть для них защиту с помощью механизма синхронизации потоков
Создание и использование MFC extension DLL
Создадим одну библиотеку DLL, расширяющую MFC, в которую включим два класса. Работа будет опираться на результаты работы Lab10. Первым пусть будет класс MyLine из этой работы. Второй класс будет создавать случайные рисунки на поверхности рисования. В него войдет массив объектов MyLine, который он будет создавать и заполнять данными во время рисования.
В этом классе должна находиться функция для сохранения и восстановления рисунков, а также для удаления уже не нужных рисунков, чтобы можно было создавать новые. Кроме того, класс должен знать размеры поверхности рисования, так как ему придется генерировать рисунки в границах поверхности рисования.
Создание заготовки проекта MFC extension DLL
- Создайте новый проект, указав, что это проект MFC extension DLL. Для этого выберите тип проекта MFC DLL, задайте имя проекта ModArtDll и разместите проект в своей папке
- Установите настройку мастера MFC DLL Wizard так
- Щелкните по кнопке Finish, после чего заготовка проекта мастером будет создана
- Скопируйте файлы MyLine.h и MyLine.cpp из папки проекта Lab10 - DrawSDI (VS.NET 2003) в папку текущего проекта ModArtDll
- Присоедините файлы MyLine.h и MyLine.cpp к текущему проекту. Для этого выполните в меню оболочки команду Project/Add Existing Item (Добавить существующий элемент)
После добавления обоих файлов к проекту класс MyLine появится во вкладке Class View.
Создание нового класса генерации случайных рисунков
Теперь основа проекта готова, пришло время заняться наполнением модуля. Класс MyLine представляет собой вполне удобный механизм для повторного использования некоторых созданных ранее функций и параметров. Имеющиеся в этом модуле функции можно использовать для генерации случайных рисунков.
Для реализации нужных нам функциональных возможностей необходимо создать новый класс. Для этого выполните следующие шаги:
-
Добавьте новый класс к создаваемому проекту. Для этого во вкладке Class View выделите верхний узел
и через контекстное меню выберите команду Add/Add Class (Добавить класс). Выберите шаблон Generic C++ Class
-
Присвойте новому классу имя MyModArt, базовому классу - имя CObject, а в качестве модификатора доступа укажите public
-
Щелкните по кнопке Finish
После создания нового класса необходимо добавить в него две переменные: m_oaLines и m_rDrawArea.
-
Во вкладке Class View выделите класс MyModArt
и через контекстное меню выполните команду Add/Add Variable
- Для каждой переменной заполните мастер так
После этих действий в классе появится код
Код, добавленный мастером в файл MyModArt.h #pragma once #include "afx.h" #include "afxcoll.h" #include "atltypes.h" class MyModArt : public CObject { public: MyModArt(void); ~MyModArt(void); private: // Для хранения массива объектов линий CObArray m_oaLines; // Для хранения размеров области рисования CRect m_rDrawArea; };