Разработка DLL (курсовая работа) (545376), страница 6
Текст из файла (страница 6)
В этом случае, помимо присвоения проекту параметров для подключениядинамических библиотек, мастер проделает некоторую дополнительную работу. В проект будутдобавлены необходимые ссылки на библиотеки mfc и файлы исходных текстов, содержащие описание иреализацию в библиотеке dll объекта класса приложения, производного от cwinapp.Иногда удобно сначала создать проект типа mfc appwizard (dll) в качестве тестового приложения, а затем- библиотеку dll в виде его составной части. В результате dll в случае необходимости будет создаватьсяавтоматически.Функция dllmain.Большинство библиотек dll - просто коллекции практически независимых друг от друга функций,экспортируемых в приложения и используемых в них. Кроме функций, предназначенных дляэкспортирования, в каждой библиотеке dll есть функция dllmain.
Эта функция предназначена дляинициализации и очистки dll. Она пришла на смену функциям libmain и wep, применявшимся впредыдущих версиях windows. Структура простейшей функции dllmain может выглядеть, например, так:bool winapi dllmain (handle hinst,dword dwreason, lpvoid ipreserved){bool ballwentwell=true;switch (dwreason){case dll_process_attach: // Инициализация процесса.break;case dll_thread_attach: // Инициализация потока.break;case dll_thread_detach: // Очистка структур потока.break;case dll_process_detach: // Очистка структур процесса.break;}if(ballwentwell) return true;else return false;}Функция dllmain вызывается в нескольких случаях.
Причина ее вызова определяется параметромdwreason, который может принимать одно из следующих значений.При первой загрузке библиотеки dll процессом вызывается функция dllmain с dwreason, равнымdll_process_attach. Каждый раз при создании процессом нового потока dllmaino вызывается с dwreason,равным dll_thread_attach (кроме первого потока, потому что в этом случае dwreason равенdll_process_attach).По окончании работы процесса с dll функция dllmain вызывается с параметром dwreason, равнымdll_process_detach. При уничтожении потока (кроме первого) dwreason будет равен dll_thread_detach.Все операции по инициализации и очистке для процессов и потоков, в которых нуждается dll, необходимовыполнять на основании значения dwreason, как было показано в предыдущем примере.
Инициализацияпроцессов обычно ограничивается выделением ресурсов, совместно используемых потоками, в частностизагрузкой разделяемых файлов и инициализацией библиотек. Инициализация потоков применяется длянастройки режимов, свойственных только данному потоку, например для инициализации локальнойпамяти.В состав dll могут входить ресурсы, не принадлежащие вызывающему эту библиотеку приложению.
Еслифункции dll работают с ресурсами dll, было бы, очевидно, полезно сохранить где-нибудь в укромномместе дескриптор hinst и использовать его при загрузке ресурсов из dll. Указатель ipreservedзарезервирован для внутреннего использования windows. Следовательно, приложение не должнопретендовать на него. Можно лишь проверить его значение. Если библиотека dll была загруженадинамически, оно будет равно null. При статической загрузке этот указатель будет ненулевым.В случае успешного завершения функция dllmain должна возвращать true. В случае возникновенияошибки возвращается false, и дальнейшие действия прекращаются.Замечание. Если не написать собственной функции dllmain(), компилятор подключит стандартнуюверсию, которая просто возвращает true.Экспортирование функций из dll.Чтобы приложение могло обращаться к функциям динамической библиотеки, каждая из них должназанимать строку в таблице экспортируемых функций dll.
Есть два способа занести функцию в эту таблицуна этапе компиляции.Метод __declspec (dllexport)Можно экспортировать функцию из dll, поставив в начале ее описания модификатор __declspec (dllexport). Кроме того, в состав mfc входит несколько макросов, определяющих __declspec (dllexport), в том числеafx_class_export, afx_data_export и afx_api_export.Метод __declspec применяется не так часто, как второй метод, работающий с файлами определениямодуля (.def), и позволяет лучше управлять процессом экспортирования.Файлы определения модуля.Синтаксис файлов с расширением .def в visual c++ достаточно прямолинеен, главным образом потому,что сложные параметры, использовавшиеся в ранних версиях windows, в win32 более не применяются.Как станет ясно из следующего простого примера, .def-файл содержит имя и описание библиотеки, атакже список экспортируемых функций:mydll.deflibrary "mydll"description 'mydll - пример dll-библиотеки'exportsmyfunction @1В строке экспорта функции можно указать ее порядковый номер, поставив перед ним символ @.
Этотномер будет затем использоваться при обращении к getprocaddress (). На самом деле компиляторприсваивает порядковые номера всем экспортируемым объектам. Однако способ, которым он это делает,отчасти непредсказуем, если не присвоить эти номера явно.В строке экспорта можно использовать параметр noname. Он запрещает компилятору включать имяфункции в таблицу экспортирования dll:myfunction @1 nonameИногда это позволяет сэкономить много места в файле dll. Приложения, использующие библитекуимпортирования для неявного подключения dll, не "заметят" разницы, поскоьку при неявномподключении порядковые номера используются автоматически.
Приложениям, загружающим библиотекиdll динамически, потребуется передавать в getprocaddress порядковый номер, а не имя функции.При использовании вышеприведенного def-файл описания экспортируемых функций dll-библиотекиможет быть,например, не таким:#define export extern "c" __declspec (dllexport)export int callback myfunction(char *str);a таким:extern "c" int callback myfunction(char *str);Экспортирование классовСоздание .def-файла для экспортирования даже простых классов из динамической библиотеки можетоказаться довольно сложным делом. Понадобится явно экспортировать каждую функцию, которая можетбыть использована внешним приложением.Если взглянуть на реализованный в классе файл распределения памяти, в нем можно заметить некоторыевесьма необычные функции. Оказывается, здесь есть неявные конструкторы и деструкторы, функции,объявленные в макросах mfc, в частности _declare_message_map, а также функции, которые написанныепрограммистом.Хотя можно экспортировать каждую из этих функций в отдельности, есть более простой способ.
Если вобъявлении класса воспользоваться макромодификатором afx_class_export, компилятор сам позаботитсяоб экспортировании необходимых функций, позволяющих приложению использовать класс,содержащийся в dll.Память dll.В отличие от статических библиотек, которые, по существу, становятся частью кода приложения,библиотеки динамической компоновки в 16-разрядных версиях windows работали с памятью несколькоиначе. Под управлением win 16 память dll размещалась вне адресного пространства задачи.
Размещениединамических библиотек в глобальной памяти обеспечивало возможность совместного использования ихразличными задачами.В win32 библиотека dll располагается в области памяти загружающего ее процесса. Каждому процессупредоставляется отдельная копия "глобальной" памяти dll, которая реинициализируется каждый раз,когда ее загружает новый процесс. Это означает, что динамическая библиотека не может использоватьсясовместно, в общей памяти, как это было в winl6.И все же, выполнив ряд замысловатых манипуляций над сегментом данных dll, можно создать общуюобласть памяти для всех процессов, использующих данную библиотеку.Допустим, имеется массив целых чисел, который должен использоваться всеми процессами,загружающими данную dll.
Это можно запрограммировать следующим образом:#pragma data_seg(".myseg")int sharedlnts[10] ;// другие переменные общего пользования#pragma data_seg()#pragma comment(lib, "msvcrt" "-section:.myseg,rws");Все переменные, объявленные между директивами #pragma data_seg(), размещаются в сегменте .myseg.Директива #pragma comment () - не обычный комментарий. Она дает указание библиотеке выполняющейсистемы С пометить новый раздел как разрешенный для чтения, записи и совместного доступа.Полная компиляция dll.Если проект динамической библиотеки создан с помощью appwizard и .def-файл модифицировансоответствующим образом - этого достаточно. Если же файлы проекта создаются вручную или другимиспособами без помощи appwizard, в командную строку редактора связей следует включить параметр /dll.В результате вместо автономного выполняемого файла будет создана библиотека dll.Если в .def-файле есть строка librart, указывать явно параметр /dll в командной строке редактора связейне нужно.Для mfc предусмотрен ряд особых режимов, касающихся использования динамической библиотекойбиблиотек mfc.