Разработка DLL (курсовая работа) (545376), страница 5
Текст из файла (страница 5)
В этом случаенеобходимо создать копию файла заголовка и включить в нее модификатор extern "c" к объявлению всехиспользуемых функций библиотеки. Модификатор extern "c" можно применить и к целому блоку, ккоторому с помощью директивы #include подключен файл старого заголовка С. Таким образом, вместомодификации каждой функции в отдельности можно обойтись всего тремя строками:extern "С"{#include "myclib.h"}В программах для старых версий windows использовались также соглашения о вызове функций языкаpascal для функций windows api. В новых программах следует использовать модификатор winapi,преобразуемый в _stdcall. Хотя это и не стандартный интерфейс функций С или c++, но именно ониспользуется для обращений к функциям windows api. Однако обычно все это уже учтено в стандартныхзаголовках windows.Загрузка неявно подключаемой dllПри запуске приложение пытается найти все файлы dll, неявно подключенные к приложению, ипоместить их в область оперативной памяти, занимаемую данным процессом.
Поиск файлов dllоперационной системой осуществляется в следующей последовательности.Каталог, в котором находится ЕХЕ-файл.Текущий каталог процесса.Системный каталог windows.Если библиотека dll не обнаружена, приложение выводит диалоговое окно с сообщением о ее отсутствиии путях, по которым осуществлялся поиск. Затем процесс отключается.Если нужная библиотека найдена, она помещается в оперативную память процесса, где и остается до егоокончания.
Теперь приложение может обращаться к функциям, содержащимся в dll.Динамическая загрузка и выгрузка dllВместо того, чтобы windows выполняла динамическое связывание с dll при первой загрузке приложения воперативную память, можно связать программу с модулем библиотеки во время выполнения программы(при таком способе в процессе создания приложения не нужно использовать библиотеку импорта). Вчастности, можно определить, какая из библиотек dll доступна пользователю, или разрешитьпользователю выбрать, какая из библиотек будет загружаться.
Таким образом можно использоватьразные dll, в которых реализованы одни и те же функции, выполняющие различные действия. Например,приложение, предназначенное для независимой передачи данных, сможет в ходе выполнения принятьрешение, загружать ли dll для протокола tcp/ip или для другого протокола.Загрузка обычной dll.Первое, что необходимо сделать при динамической загрузке dll, - это поместить модуль библиотеки впамять процесса.
Данная операция выполняется с помощью функции ::loadlibrary, имеющейединственный аргумент - имя загружаемого модуля. Соответствующий фрагмент программы долженвыглядеть так:hinstance hmydll;::if((hmydll=::loadlibrary("mydll"))==null) { /* не удалось загрузить dll */ }else { /* приложение имеет право пользоваться функциями dll через hmydll */ }Стандартным расширением файла библиотеки windows считает .dll, если не указать другое расширение.Если в имени файла указан и путь, то только он будет использоваться для поиска файла.
В противномслучае windows будет искать файл по той же схеме, что и в случае неявно подключенных dll, начиная скаталога, из которого загружается exe-файл, и продолжая в соответствии со значением path.Когда windows обнаружит файл, его полный путь будет сравнен с путем библиотек dll, уже загруженныхданным процессом.
Если обнаружится тождество, вместо загрузки копии приложения возвращветсядескриптор уже подключенной библиотеки.Если файл обнаружен и библиотека успешно загрузилась, функция ::loadlibrary возвращает еедескриптор, который используется для доступа к функциям библиотеки.Перед тем, как использовать функции библиотеки, необходимо получить их адрес. Для этого сначаласледует воспользоваться директивой typedef для определения типа указателя на функцию и определитьпеременую этого нового типа, например:// тип pfn_myfunction будет объявлять указатель на функцию,// принимающую указатель на символьный буфер и выдающую значение типа inttypedef int (winapi *pfn_myfunction)(char *);::pfn_myfunction pfnmyfunction;Затем следует получить дескриптор библиотеки, при помощи которого и определить адреса функций,например адрес фунции с именем myfunction:hmydll=::loadlibrary("mydll");pfnmyfunction=(pfn_myfunction)::getprocaddress(hmydll,"myfunction");::int icode=(*pfnmyfunction)("hello");Адрес функции определяется при помощи функции ::getprocaddress, ей следует передать имя библиотекии имя функции.
Последнее должно передаваться в том виде, в котором эксаортируется из dll.Можно также сослаться на функцию по порядковому номеру, по которому она экспортируется (при этомдля создания библиотеки должен использоваться def-файл, об этом будет рассказано далее):pfnmyfunction=(pfn_myfunction)::getprocaddress(hmydll,makeintresource(1));После завершения работы с библиотекой динамической компоновки, ее можно выгрузить из памятипроцесса с помощью функции ::freelibrary:::freelibrary(hmydll);Загрузка mfc-расширений динамических библиотекПри загрузке mfc-расширений для dll (подробно о которых рассказывается далее) вместо функцийloadlibraryи freelibrary используются функции afxloadlibrary и afxfreelibrary.
Последние почти идентичныфункциям win32 api. Они лишь гарантируют дополнительно, что структуры mfc, инициализированныерасширением dll, не были запорчены другими потоками.Ресурсы dll.Динамическая загрузка применима и к ресурсам dll, используемым mfc для загрузки стандартныхресурсов приложения. Для этого сначала необходимо вызвать функцию loadlibrary и разместить dll впамяти. Затем с помощью функции afxsetresourcehandle нужно подготовить окно программы к приемуресурсов из вновь загруженной библиотеки. В противном случае ресурсы будут загружаться из файлов,подключенных к выполняемому файлу процесса.
Такой подход удобен, если нужно использоватьразличные наборы ресурсов, например для разных языков.Замечание. С помощью функции loadlibrary можно также загружать в память исполняемые файлы (незапускать их на выполнение!). Дескриптор выполняемого модуля может затем использоваться приобращении к функциям findresource и loadresource для поиска и загрузки ресурсов приложения.Выгружают модули из памяти также при помощи функции freelibrary.Пример обычной dll и способов загрузки.Приведем исходный код динамически подключаемой библиотеки, которая называется mydll и содержитодну функцию myfunction, которая просто выводит сообщение.Сначала в заголовочном файле определяется макроконтстанта export.
Использование этого ключевогослова при определении некоторой функции динамически подключаемой билиотеке позволяет сообщитькомпоновщику, что эта функция доступна для использования другими программами, в результате чего онзаносит ее в библилтеку импорта. Кроме этого, такая функция, точно так же, как и оконная процедура,должна определяться с помощью константы callback:mydll.h#define export extern "c" __declspec (dllexport)export int callback myfunction(char *str);Файл библиотеки также несколько отличается от обычных файлов на языке c для windows.
В нем вместофункции winmain имеется функция dllmain. Эта функция используется для выполнения инициализации, очем будет рассказано позже. Для того, чтобы библиотека осталась после ее загрузки в памяти, и можнобыло вызывать ее функции, необходимо, чтобы ее возвращаемым значением было true:mydll.c#include#include "mydll.h"int winapi dllmain(hinstance hinstance, dword fdreason, pvoid pvreserved){return true;}export int callback myfunction(char *str){messagebox(null,str,"function from dll",mb_ok);return 1;}После трансляции и компоновки этих файлов появлятся два файла - mydll.dll (сама динамическиподключаемая библиотека) и mydll.lib (ее библиотека импорта).Пример неявного поключения dll приложениемПриведем теперь исходный код простого приложения, которое использует функцию myfunction избиблиотеки mydll.dll:#include#include "mydll.h"int winapi winmain(hinstance hinstance, hinstance hprevinstance,lpstr lpcmdline, int ncmdshow){int icode=myfunction("hello");return 0;}Эта программа выглядит как обычная программ для windows, чем она в сущности и является.
Тем неменее, следует обратить внимание, что в исходный ее текст помимо вызова функции myfunction из dllбиблиотеки включен и заголовочный файл этой библиотеки mydll.h. Также необходимо на этапекомпоновки приложения подключить к нему библиотеку импорта mydll.lib (процесс неявногоподключения dll к исполняемому модулю).Чрезвычайно важно понимать, что сам код функции myfunction не включается в файл myapp.exe. Вместоэтого там просто имеется ссылка на файл mydll.dll и ссылка на функцию myfunction, которая находится вэтом файле.
Файл myapp.exe требует запуска файла mydll.dll.Заголовочный файл mydll.h включен в файл с исходным текстом программы myapp.c точно так же, кактуда включен файл windows.h. Включение библиотеки импорта mydll.lib для компоновки аналогичновключению туда всех библиотек импорта windows. Когда програма myapp.exe работает, онаподключается к библиотеке mydll.dll точно так же, как ко всем стандартным динамически подключаемымбиблиотекам windows.Пример динамической загрузки dll приложением.Приведем теперь полностью исходный код простого приложения, которое использует функциюmyfunction из библиотеки mydll.dll, используя динамическую загрузку библиотеки:#includetypedef int (winapi *pfn_myfunction)(char *);int winapi winmain(hinstance hinstance, hinstance hprevinstance,lpstr lpcmdline, int ncmdshow){hinstance hmydll;if((hmydll=loadlibrary("mydll"))==null) return 1;pfn_myfunction pfnmyfunction;pfnmyfunction=(pfn_myfunction)getprocaddress(hmydll,"myfunction");int icode=(*pfnmyfunction)("hello");freelibrary(hmydll);return 0;}Создание dll.Теперь, познакомившись с принципами работы библиотек dll в приложениях, рассмотрим способы ихсоздания.
При разработке приложении функции, к которым обращается несколько процессов, желательноразмещать в dll. Это позволяет более рационально использовать память в windows.Проще всего создать новый проект dll с помощью мастера appwizard, который автоматически выполняетмногие операции. Для простых dll, таких как рассмотренные в этой главе, необходимо выбрать типпроекта win32 dynamic-link library. Новому проекту будут присвоены все необходимые параметры длясоздания библиотеки dll. Файлы исходных текстов придется добавлять к проекту вручную.Если же планируется в полной мере использовать функциональные возможности mfc, такие какдокументы и представления, или намерены создать сервер автоматизации ole, лучше выбрать тип проектаmfc appwizard (dll).