logo
Конспект по ООПиП ч

55.Создание и использование 32-битовых динамически связываемых библиотек

Динамически связываемые библиотеки (DLL-модули) являются базовым компонентом операционных систем Windows 95 и Windows NT. Windows 95 использует модули Kernel32.dll, User32.dll и Gdi32.dll для выполнения абсолютного большинства своих функций. И вы тоже можете ими пользоваться. Электронная документация Microsoft Visual C++ может служить хорошим источником информации о функциях API, реализованных в этих трех модулях DLL.

Модули DLL Windows можно использовать в любой разрабатываемой программе. Кроме того, интегрированная среда Visual Studio позволяет создавать и свои собственные модули DLL.

Создание 32-битового модуля DLL

В Visual C++ существует два вида модулей DLL — использующие библиотеку MFC и не использующие ее. Для каждого из видов модулей DLL имеется свой собственный мастер.

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

Импорт и экспорт функций

Для определения экспортируемой функции используется следующий синтаксис:

__declspec(dllexport) типДанных int идентификаторПеременной; //Для переменных, или

__declspec(dllexport) возвращаемыйТип имяфункции ([списокАргументов]); //Для функций.

Импортирование функций организуется практически так же — просто замените ключевое слово, например __declspec(dllexport), словом __declspec(dllimport). Используя реальные функцию и переменную для демонстрации синтаксиса, получим следующее:

_declspec(dllimport) int referenceCount;

_declspec(dllimport) void DiskFree(lpStr Drivepath );

Ключевому слову __ declspec предшествуют два знака подчеркивания.

Чтобы упростить описание DLL-модулей, Microsoft использует файл заголовка и макросы препроцессора. Эта методика всего лишь требует, чтобы вы использовали уникальную лексему препроцессора — проще всего для этого использовать имя файла заголовка — и написали макрос, который будет замещать эту лексему корректными операторами импорта и экспорта. Предположим, что имеется файл заголовка с именем DISKFREE.H, тогда макрос препроцессора в нем может выглядеть так, как показано в листинге 28.3.

//DISKFREE.H содержит простую функцию, возвращающую размер

// свободного дискового пространства.

#ifndef __DISKFREE_H

#define __DISKFREE_H

#ifndef __DISKFREE__

#define DISKFREELIB __declspec(dllimport)

#else

#define DISKFREELIB __declspec(dllexport)

#endif

//Макрос используется для выбора описания импорта или экспорта.

DISKFREELIB unsigned long DiskFree( unsigned int drive );

//Например, 0 = A:, 1=B:, 2 = С:

#endif

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

Создание модуля DLL DiskFree

Утилита DiskFree позволяет легко определять размер свободного пространства на указанном диске. Ее работа основана на использовании функции GetDiskFreeSpace(), находящейся в модуле Kernel32.Dll.

Для того чтобы создать не использующий MFC модуль DLL, выберите команду File--New, щелкните на корешке вкладки Project и выберите в расположенном слева списке значение Win32Dll. Назовите проект DiskFree и щелкните на кнопке ОК. На экране появится окно мастера AppWizard, как показано на рис. 28.4. Выберите переключатель An empty DLL project, и в результате будет создан проект, пока еще не имеющий собственных файлов.

Добавьте в проект файл заголовка DiskFree.h и поместите в него текст, представленный в листинге 28.4. Добавьте в проект файл текста программы DiskFree.cpp и поместите в него текст, представленный в листинге 28.5.

Рис. 28.4. Создание проекта модуля DLL, не использующего MFC, вы­полняется за один шаг

|Листинг28.4.

#ifndef __DISKFREE_H

#define __DISKFREE_H

#ifndef __DISKFREE__

#define __DISKFREELIB__ __declspec(dllimport)

#else

#define __DISKFREELIB__ __declspec(dllexport)

#endif

//Возвращает размер свободного дискового пространства на диске,

//заданном его номером (например, 0=A:, 1=B:, 2=C:)

__DISKFREELIB__ unsigned long DiskFree( unsigned int drive );

#endif

Лиcтинг28.5. Фaйл иcxoдного тeкcтa DiskFгee.cpp

#include <afx.h>

#include <winbase.h> //Содержит объявление функции GetDiskFreeSpace(),

// находящейся в модуле kernel32.dll.

#define __DISKFREE__ //Определяет лексему перед подключением //библиотеки.

#include "diskfree.h"

// Возвращает размер свободного дискового пространства на диске,

// заданном его номером (например, 0 = А:, 1= В:, 2 = С:).

__DISKFREELIB__ unsigned long DiskFree( unsigned int drive )

{

unsigned long bytesPerSector, sectorsPerCluster,

freeClusters, totalClusters;

char DrivePath[4] = { char (drive + 65), ‘:’, ‘\\’, ‘\0’ };

if( GetDiskFreeSpace( DrivePath, &sectorsPerCluster,

&bytesPerSector, &freeClusters, &totalClusters )) {

return sectorsPerCluster * bytesPerSector * freeClusters; )

}

else

{

return 0;

}

}

Теперь можно оттранслировать модуль DLL. В следующем разделе речь будет идти oб использовании 32-битовых модулей DLL и о том, как Windows находит модули DLL в системе.

Наиболее общим назначением модулей DLL является обеспечение повторного использования и расширение функциональной поддержки программного обеспечения при возможности их неявной загрузки в среде Windows

В этой главе при компиляции модуля DiskFree использовались установки по умолчанию. Это означает, что модуль DLLMain будет применяться неявно (его добавляет компилятор) и что используется режим неявной загрузки модулей DLL, причем Windows будет автоматически управлять загрузкой и выгрузкой этих библиотек.

Использование 32-битовых модулей DLL

Большинство модулей DLL загружается неявно, и их загрузкой и выгрузкой управляет Windows. Процедура поиска библиотек, загружаемых подобным образом, аналогична процедуре поиска выполняемых файлов — сначала поиск происходит в папке, из которой было загружено использующее их приложение, затем выполняется поиск в текущей папке, затем — в папке Windows\System, затем — в папке Windows и, наконец— в каждой из папок, указанных в переменой PATH.

Обычно при установке приложения модули DLL принято помещать в папку Windows или Windows\System. Но в процессе разработки приложения в качестве временного хранилища для этой цели можно использовать папку выполняемых файлов проекта. Однако нужно следить за тем, чтобы в итоге в разных папках, включая папки Windows, Windows\System и папку проекта, не оказались разные версии одного и того же модуля DLL.

Использование модуля DLL

Неявная загрузка и использование модуля DLL осуществляются так же просто, как и работа с обычными функциями. При компиляции модуля DLL транслятор Microsoft Visual C++ создает файл с расширением .LIВ. (Таким образом, помимо файла DISKFREE.DLL, существует еще и файл DISKFREE.LIB, созданный компилятором.) Файл библиотеки (.LIB) используется для разрешения адресов загрузки в модуле DLL и содержит полное имя динамически подключаемой библиотеки, тогда как файл заголовка содержит его описание.

Все, что вам необходимо сделать, — это подключить файл заголовка к исходному файлу, использующему функции из модуля DLL, и указать имя файла библиотеки (.LIB) в поле Object/library modules на вкладке Link диалогового окна Project Settings, доступ к которому можно получить, выбрав команду Project--Settings

Для проверки работы модуля DLL DiskFree создайте консольное приложение с именем TestDiskFree и добавьте в него файл TestDiskFree.cpp исходного текста на C++. Внесите в файл текст программы, представленный в листинге 28.6. Скопируйте в папку этого проекта файл DiskFree.h и добавьте его к проекту, выбрав команду Project--Add To Project--Files, a затем выбрав файл DiskFree.h. Скопируйте в папку TestDiskFree файлы DiskFree.Dll и DiskFree.Lib. (Они находятся в папке DiskFree\Debug.) Выполните все указанные выше изменения в установках проекта и скомпилируйте его.

Файл TestDiskFree.Cpp

#include <afx.h>

#include <iostream.h>

#include "diskfree. h"

#define CodeTrace(arg) \

cout << #arg << endl;\

arg

int main()

{

CodeTrace( cout << DiskFree(2) << endl );

return 0;

}

Эта программа неявно загружает модуль DLL посредством подключения файла diskfree.h, а затем использует его в работе. При выполнении программы макрос CodeTrace просто печатает оператор программы перед его выполнением. Фактически вся работа приложения состоит в вызове функции DiskFree() с целью определения имеющегося свободного дискового пространства на диске 2. Диск 0— это А:, диск 1 — В: и диск 2— С:. Результаты работы скомпилированной и запущенной программы должны быть подобны тому, что представлено на рис. 28.6.

Cout << DiskFree(2) << endl

36258777

Согласно программе TestDiskFree на диске С: компьютера, использованного для запуска этой программы, находится почти 37 Мбайт свободного дискового пространства