Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2 курс / Лекции / Лекция 11.pptx
Скачиваний:
95
Добавлен:
18.02.2023
Размер:
105.67 Кб
Скачать

Создание динамически компонуемых библиотек

Процесс создания DLL во многом подобен созданию обычных исполняемых программ Windows. Исходный код DLL идентичен исходному коду любого исполняемого модуля. Ниже показан исходный код очень простой DLL-библиотеки.

#include <windows.h> // AddNumbers — это подпрограмма DLL, // которую приложение может вызывать.

int AddNumbers( int nl, int n2 ) { return ( nl + n2 );

}

Этот же код может служить подпрограммой обычного приложения. Различия существуют в файле описания модуля (.DEF). Модуль подключения использует файл описания модуля с целью предоставления информации, необходимой для создания DLL. Следующий фрагмент кода иллюстрирует минимальный файл описания модуля для представленного выше примера.

LIBRARY MyDll

EXPORTS

AddNumbers @1

Функция AddNumbers определена как экспортируемая (с точки зрения DLL), и ей присвоен порядковый номер 1. Задавать порядковый номер необязательно. Когда номер опущен, он присваивается компоновщиком автоматически. Если файл объекта, созданный из предыдущего примера исходного кода, связать с данным файлом описания модуля, получится динамически компонуемая библиотека.

При условии, что для доступа к этой DLL приложение будет использовать динамическое связывание во время выполнения, процесс создания можно считать завершенным. Если планируется использование динамическое связывание во время загрузки, потребуется реализовать еще несколько задач.

Поскольку динамическое связывание во время загрузки требует разрешения всех ссылок на этапе компиляции/компоновки, сначала необходимо создать файл заголовков (.Н), который описывает подпрограммы из DLL. Обычно файл заголовков содержит описания прототипов всех общедоступных точек входа в DLL, а также любые структуры и определения. Для предыдущего фрагмента кода файл заголовков имеет следующий вид:

// Файл заголовков для MYDLL.DLL — описывает точки входа int AddNumbers (int nl, int n2) ;

Приложения, реализующие динамическое связывание с DLL во время загрузки, должны включать этот файл, чтобы иметь описание подпрограммы AddNumbers. Для компилятора этого достаточно. Однако компоновщику необходимо знать местонахождение подпрограммы. Обычно это осуществляется путем связывания приложения с библиотекой импорта (import library), созданной из файла описания модуля.

Библиотека импорта указывает компоновщику, что подпрограмма

AddNumbers находится в файле MYDLL.DLL.

Многие платформы разработки создают библиотеки импорта автоматически. Если же требуется создать такую библиотеку самостоятельно, можно воспользоваться утилитой LIB.EXE:

lib /machine:IX86 /OUT:mydll.lib /DEF:mydll.def

В этом примере создается библиотека импорта, пригодная для платформ на базе процессора х86. Приложение будет включать библиотеку MYDLL.LIB. Она служит входной информацией для компоновщика и обеспечивает корректное разрешение ссылок на DLL.

Если DLL не сопровождается библиотекой импорта, для информирования модуля подключения о местонахождении подпрограмм из DLL приложение должно использовать файл определений модуля. Этой цели служит раздел IMPORTS файла определений модуля. Для рассматриваемого примера приложения файл определений модуля должен содержать следующие строки:

IMPORTS

MyDll.AddNumbers

Пример создания простой DLL

Пример ниже - исходный код, необходимый для создания простой DLL, Myputs.dll. Она определяет простую печатающую строку функцию, называемую myPuts. DLL Myputs не определяет функцию точки входа, потому что она связана с С- библиотекой периода выполнения программы и не имеет каких- либо выполняемых своих собственных функции инициализации или очистки.

Функция myPuts записывает строку с символом конца ('0') в стандартное устройство вывода данных.

Для механизма экспорта здесь используется метод __declspec(export), поддерживаемый Microsoft Visual Studio, однако и любым другим методом экспорта, поддерживаемым вашим разработанным окружением он может быть заменен.

#include <windows.h>

#define EOF (-1)

#ifdef __cplusplus // Если используется код C++, extern "C" { // мы должны экспортировать C-интерфейс

#endif __declspec(dllexport) int myPuts(LPTSTR lpszMsg) {

DWORD cchWritten; HANDLE hStdout; BOOL fRet;

//Получим дескриптор стандартного устройства вывода. hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (INVALID_HANDLE_VALUE == hStdout) return EOF;

//Запишем строку с нулем в конце для стандартного устройства вывода данных. while (*lpszMsg != '\0') {

fRet = WriteFile(hStdout, lpszMsg, 1, &cchWritten, NULL);

if( (FALSE == fRet) || (1 != cchWritten) ) return EOF;

lpszMsg++;

}return 1;

}#ifdef __cplusplus

}#endif

Использование динамического связывания выполняемого при загрузке

После того, как Вы создали DLL, то можете использовать функции, которые ee определяют в приложении. Ниже следует простая консольная программа, которая использует функцию myPuts, экспортируемую из Myputs.dll.

Поскольку в этом примере функция DLL вызывается явно, модуль приложения должен быть скомпонован с импортирующей библиотекой Myputs.lib. Дополнительную информацию о разработке DLL, см. в документации, включенной ваши инструментальные средства разработки.

#include <windows.h>

 

int myPuts(LPTSTR);

// функция из DLL

int main(VOID) { int Ret = 1;

Ret = myPuts(TEXT("Message printed using the DLL function\

n"));

return Ret;

}

Использование динамического связывания периода выполнения

Вы можете использовать одну и ту же DLL, динамически связывая ее и в период выполнения загрузки и в период выполнения программы.

Пример ниже использует функцию LoadLibrary, чтобы получить дескриптор MyputsDLL. Если LoadLibrary завершается успешно, программа использует возвращенный дескриптор в функции GetProcAddress, чтобы получить адрес функции myPuts в DLL. После вызова этой функции DLL, программа вызывает функцию FreeLibrary, чтобы выгрузить DLL.

Поскольку программа использует динамическую связь периода выполнения, нет необходимости компоновать модуль с импортируемой библиотекой для DLL.

Этот пример иллюстрирует важное различие между динамической связью периода выполнения и выполняемую при загрузке. Если DLL не доступна, то приложение, использующее выполняемую при загрузке динамическую связь, должно просто завершиться. Однако, пример динамической связи периода

выполнения, может просто отреагировать на ошибку.

//Простая программа, которая использует функции LoadLibrary и GetProcAddress,

//чтобы получить доступ к myPuts из Myputs.dll.

#include <stdio.h> #include <windows.h>

typedef int (*MYPROC)(LPTSTR); VOID main(VOID) {

HINSTANCE hinstLib;

MYPROC ProcAdd;

BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;

//Получим дескриптор модуля DLL. hinstLib = LoadLibrary(TEXT("myputs"));

//Если дескриптор допустимый, то попробуем получить адрес функции. if (hinstLib != NULL) {

ProcAdd = (MYPROC) GetProcAddress(hinstLib, TEXT("myPuts"));

// Если адрес функции правильный, то вызываем функцию. if (NULL != ProcAdd) {

fRunTimeLinkSuccess = TRUE;

(ProcAdd) (TEXT("Message via DLL function\n")); } // Освобождаем модуль DLL.

fFreeResult = FreeLibrary(hinstLib);

} // Если вы не в состоянии вызывать функцию DLL, используйте альтернативу. if (! fRunTimeLinkSuccess)

printf("Message via alternative method\n");

}

Доступ к данным в DLL

Когда DLL динамически распределяет память, это выполняется от имени процесса, который обращается к DLL. Доступ к распределенной памяти возможен только для потоков, принадлежащих данному процессу. Это относится к памяти, выделяемой с использованием GlobalAlloc,

LocalAlloc, HeapAlloc, VirtualAlloc или любой из стандартных подпрограмм библиотеки С, таких как malic и new.

Если DLL динамически распределяет память таким образом, что она может быть доступной всем процессам, использующим библиотеку в данный момент, DLL должна применять функции отображения файлов (file-mapping) для создания поименованной обшей памяти.

Глобальные и статические переменные, объявленные в исходном коде DLL, находятся в сегменте данных DLL. Каждому процессу, присоединенному к DLL, предоставляется собственная копия этого сегмента данных.

Соседние файлы в папке Лекции