![]() |
В этом параграфе Вы узнаете, что такое DLL (Dynamic Link Library – динамически присоединяемая библиотека), как создать DLL с помощью Visual C++, и как использовать DLL в своих прикладных программах.
Рис. 13.1. Результаты работы программы с Проводником
Что такое DLL? DLL является библиотечным файлом, содержащим функции. Программист может включить dll -файл в свою программу и вызывать его функции. Например, можно создать dll -файл с именем Circle.dll, содержащий функции, относящиеся к операциям с кругами, такие как DrawCircle(), CalculateCirdeArea() и т.д. После этого Вы можете передать файл Circle.dll другим программистам, а они включат эти функции в свои программы.
Как и подразумевает название, DLL является библиотекой, которая динамически присоединяется к использующей ее программе. Это означает, что, когда Вы создаете ехе -файл программы, вам не нужно компоновать dll -файл со своей программой и этот файл будет динамически скомпонован с вашей программой во время ее выполнения. Таким образом, если Вы пишете программу, которая использует DLL, то должны распространять dll -файл вместе с ЕХЕ-файлом программы.
Если программа использует для работы dll -файл, он должен находиться в одной из следующих папок:
· в \Windows\System (при использовании Windows Me/98/95);
· в \Windows\System32 (для Windows XP/2000/NT);
· в текущей папке, откуда запускается ехе -файл Вашей программы.
Обычно программа Install (установка) копирует dll -файл в системный каталог пользователя \Windows\System32/ В этом случае другие программы также могут обращаться к этому dll -файлу, а работа вашей программы не будет зависеть от текущих установок маршрута.
DLL-файл может использоваться любым языком программирования, который поддерживает такие файлы (например, Visual C++ и Visual Basic). Далее Вы создадите простой dll -файл и напишете программу Visual C++, которая к нему обращается.
Следующим шагом будет написание этого служебного кода для двух файлов: исходного текста библиотеки MyDLL.cpp и файла определений MyDLL.def.
Создайте новую папку для проекта (например, Library-13-2), куда поместите эти два файла. В этой же папке будет создаваться проект (назовем его MyDLL), в рамках которого разрабатывается библиотека; этот проект не создает программного приложения.
С помощью Visual Studio проект создается следующим образом (путь из главного меню Visual Studio):
File → New → Win32 Dinamic Link Library → Location (указывается папка \Library-13-2) → Project Name (указывается MyDLL).
Исходный текст библиотеки MyDLL.cpp состоит из двух частей: служебной и функциональной. Служебная часть связана с инициализацией работы программ библиотеки и с прекращением работы этих программ. Она имеет следующий вид:
// Пример MyDLL.cpp
//
// DllEntryPoint(): Входная точка инициализации DLL
#include <Windows.h>
DllEntryPoint(HINSTANCE hDLL,
DWORD dwReason, LPVOID Reserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
return TRUE;
}
Код, который мы написали файле MyDLL.cpp, содержит единственную функцию: DllEntryPoint(), которая является входной точкой DLL. Когда ехе -программа, которая обращается к DLL, загружает DLL, функция DllEntryPoint() выполняется автоматически. (Вы напишете ехе -программу, загружающую MyDLL.dll, позже в этом параграфе.)
Функция DllEntryPoint() состоит из оператора switch:
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
Оператор switch оценивает значение dwReason (второй параметр функции DllEntryPoint()).
Код в первом блоке переключателя case DLL_PROCESS_ATTACH выполняется, когда DLL присоединяется к ехе -файлу (когда ехе -файл загружает DLL). Следовательно, в блоке case DLL_PROCESS_ATTACH Вы можете написать инициализирующий код.
Код во втором блоке переключателя case DLL_PROCESS_DETACH выполняется, когда DLL отсоединяется от ехе -файл. Например, когда ехе -файл, который использует DLL, завершается, то выполняется код во втором блоке переключателя case DLL_PROCESS_DETACH. Следовательно, в блоке case dll_process_detach можно написать код, выполняющий процедуру очистки.
Теперь Вы добавите две простых функции в файл MyDll.cpp:
1) MyBeep() – вызывает звуковой сигнал;
2) MyDelay() – осуществляет задержку процесса выполнения приложения.
Текст MyBeep():
int MyBeep(DWORD frequency, // Звуковой сигнал
DWORD duration)
{
Beep(frequensy,duration);
return 1;
}
Текст MyDelay():
int MyDelay(long wait) // Задержка
{
Sleep(wait);
return 1;
}
Теперь нужно собрать весь файл MyDll.cpp:
// Пример MyDLL.cpp
//
#include <Windows.h>
// Объявление прототипов функций DLL
int MyBeep(DWORD, DWORD);
int MyDelay(long);
// DllEntryPoint(): Входная точка инициализации DLL
DllEntryPoint(HINSTANCE hDLL,
DWORD dwReason, LPVOID Reserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
return TRUE;
}
// Звуковой сигнал
int MyBeep(DWORD frequency, // Звуковой сигнал
DWORD duration)
{
Beep(frequency, duration);
return 1;
}
// Задержка
int MyDelay(long wait) // Задержка
{
Sleep(wait);
return 1;
}
Итак, файл с исходным текстом библиотеки подготовлен. Переходим к созданию файла определений.
Файл определений MyDLL.def имеет одинаковую структуру для всех библиотек. Он также имеет две части: служебную и функциональную.
Служебная часть:
;
; def-файл для библиотеки MyDLL.dll
;
LIBRARY mydll
CODE PRELOAD MOVEBLE DISCARDABLE
DATA PRELOAD SINGLE
EXPORTS
; Имена (точки входа) DLL функций
Здесь имена DLL функций – это имена тех функций, которые включены в библиотеку и «экспортируются» после загрузки библиотеки (оператор EXPORTS). Заметим, что комментарии в def -файлах начинаются не с двойной наклонной черты, а с точки с запятой.
Функциональная часть этого файла выглядит очень просто:
MyBeep
MyDelay
В целом файл MyDLL.def в нашем случае выглядит так:
;
; def-файл для библиотеки MyDLL.dll
;
LIBRARY mydll
CODE PRELOAD MOVEBLE DISCARDABLE
DATA PRELOAD SINGLE
EXPORTS
; Имена (точки входа) DLL функций
MyBeep
MyDelay
Создание прикладной программы, использующей DLL. Прикладная программа создается в рамках проекта, не связанного с библиотекой. Назовем этот проект Ex_13_02. Все файлы этого проекта будут собираться в папке Example-13-2. Для создания этой программы нам потребуются следующие переменные:
1) переменная-дескриптор для регистрации экземпляра библиотеки
HINSTANCE gLibMyDLL = NULL;
2) объявление функции MyBeep() библиотеки MyDLL.dll
typedef int(*MYBEEP)(DWORD, DWORD);
MYBEEP MyBeep = NULL;
3) объявление функции MyDelay() библиотеки MyDLL.dll
typedef int(*MYDELAY)(long);
MYDELAY MyDelay = NULL;
Загрузка DLL выполняется с помощью функции LoadLibrary():
gLibMyDLL = LoadLibrary("MYDLL.DLL");
Напомним, что функция LoadLibrary() будет искать библиотеку в одном из следующих мест:
· в \Windows\System (при использовании Windows Me/98/95);
· в \Windows\System32 (для Windows XP/2000/NT);
· в текущей папке, откуда запускается ехе -файл Вашей программы.
Если там нет библиотеки, то значение переменной gLibMyDLL останется NULL. Это обстоятельство можно использовать на практике.
После успешной загрузка библиотеки необходимо определить адреса функций в библиотеке. Для этого применяется функция
FARPROC GetProcAddress(HMODULE hModule,
LPCSTR lpProcName);
Первый аргумент этой функции hModule – дескриптор загруженной библиотеки, а второй lpProcName – имя функции, адрес которой нужно получить. Если в библиотеке запрашиваемой функции нет, то возвращается значение NULL.
Адреса обращений к нашим двум функциям получаются так:
MyBeep =(MYBEEP)GetProcAddress(gLibMyDLL,"MyBeep");
MyDelay=(MYDELAY)GetProcAddress(gLibMyDLL,"MyDelay");
Пример 13-2. Программа работы с DLL-библиотекой
В следующем примере в качестве каркаса также используется программа, рассмотренная в примере 8-2. К ней дописаны описанные выше средства, демонстрирующие возможность работы с DLL-библиотекой. Дополним меню программы двумя новыми кнопками: Загрузить DLL и Звуковой сигнал.
Файл ресурсов Mydialog.rc после включения этих кнопок выглядит следующим образом:
#include <Windows.h>
#include "Text.h"
MYMENU MENU
{
MENUITEM "&Текст", ID_SHOW
MENUITEM "&Сначала", ID_RESET
MENUITEM "&Загрузить DLL", ID_LOAD
MENUITEM "&Звуковой сигнал", ID_BEEP
MENUITEM "Помощь", ID_HELP
}
MYMENU ACCELERATORS
{
VK_F2, ID_SHOW, VIRTKEY
VK_F3, ID_RESET, VIRTKEY
VK_F4, ID_LOAD, VIRTKEY
VK_F5, ID_BEEP, VIRTKEY
VK_F1, ID_HELP, VIRTKEY
}
Соответственно, изменится и вспомогательный файл Text.h:
#define ID_SHOW 100
#define ID_RESET 101
#define ID_LOAD 102
#define ID_BEEP 104
#define ID_HELP 105
Распишем команду Загрузить DLL:
case ID_LOAD:
// Если файл MyDLL.DLL уже загружен,
// сообщить
// пользователю и завершить эту функцию
if(gLibMyDLL!= NULL)
MessageBox(hwnd,
"Библиотека MyDLL уже загружена",
"Внимание:",MB_OK);
else
{
MessageBox(hwnd,"Загрузить DLL",
"Команда:",MB_OK);
// Загрузить MyDLL.dll
gLibMyDLL=LoadLibrary("MYDLL.DLL");
// Если DLL не был загружен, вывести
// окно сообщения об ошибке
if(gLibMyDLL == NULL)
{
MessageBox(hwnd,
"Невозможно загрузить MyDLL. "
"Убедитесь, что она находится"
" в соответствующей папке"
"Ошибка:",MB_OK);
return 0;
}
}
// Получить адрес функции МуВеер()
// библиотеки MyDLL.dll
MyBeep=(MYBEEP)GetProcAddress(gLibMyDLL,
"MyBeep");
// Получить адрес функции MyDelay()
// библиотеки MyDLL.dll
MyDelay=(MYDELAY)GetProcAddress(gLibMyDLL,
"MyDelay");
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
Распишем команду Звуковой сигнал:
case ID_BEEP:
if(MyDelay!= NULL && MyBeep!= NULL
&& gLibMyDLL!= NULL)
{
// Вызвать функцию MyBeep()
MyBeep(600,250); // 600 Гц, 0,25 сек
// Вызвать функцию MyDelay()
MyDelay(500); // 0,5 сек
// Вызвать функцию MyBeep()
MyBeep(300,250); // 300 Гц, 0,25 сек
MessageBox(hwnd,"Звуковой сигнал",
"Команда:", MB_OK);
}
else
MessageBox(hwnd,
"Причины: библиотека MyDLL "
"не загружена, либо "
"отсутствуют функции "
"MyBeep и MyDelay ",
"Ошибка:", MB_OK);
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
Полный текст примера программы, демонстрирующей работу с библиотекой, представлен ниже.
// Демонстрация работы с библиотекой MyDLL,
// с использованием виртуального окна
#include <Windows.h>
#include <String.h>
#include <Stdio.h>
#include "Text.h"
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
char szWinName[] = "МоеОкно"; // Имя класса окна
char str[255]; // Буфер строки вывода
int X=0, Y=0; // Текущие координаты строки
int maxX, maxY; // Размеры экрана
HDC memdc; // DC виртуального окна
HBITMAP hbit; // Растр - это виртуальное окно
HBRUSH hbrush; // Дескриптор кисти
HINSTANCE gLibMyDLL = NULL;
typedef int(*MYBEEP)(DWORD, DWORD);
MYBEEP MyBeep = NULL;
typedef int(*MYDELAY)(long);
MYDELAY MyDelay = NULL;
int WINAPI WinMain (HINSTANCE hThisInst,
HINSTANCE hPrevInst,
LPSTR lpszArgs,
int nWinMode)
{
HWND hwnd;
MSG msg;
WNDCLASS wcl;
HACCEL hAccel;
// Определить класс окна
wcl.hInstance=hThisInst; // Дескриптор приложения
wcl.lpszClassName=szWinName; // Имя класса окна
wcl.lpfnWndProc=WindowFunc; // Функция окна
wcl.style=0; // Стиль по умолчанию
wcl.hIcon=LoadIcon(NULL,IDI_APPLICATION); // Иконка
wcl.hCursor=LoadCursor(NULL,IDC_ARROW); // Курсор
wcl.lpszMenuName="MYMENU"; // Меню
wcl.cbClsExtra=0; // Без дополнительной
wcl.cbWndExtra=0; // информации
// Определить заполнение окна белым цветом
wcl.hbrBackground=
(HBRUSH)GetStockObject(WHITE_BRUSH);
if(!RegisterClass(&wcl)) // Зарегистр. класс окна
return 0;
// Создать окно
hwnd=CreateWindow(szWinName, // Имя класса
" Демонстрация работы с библиотекой MyDLL ",
WS_OVERLAPPEDWINDOW,// Стиль окна
CW_USEDEFAULT, // Х-координата
CW_USEDEFAULT, // Y-координата
CW_USEDEFAULT, // Ширина окна
CW_USEDEFAULT, // Высота окна
HWND_DESKTOP, // Нет родит. окна
NULL, // Нет меню
hThisInst, // Дескрип. приложения
NULL); // Без дополит. аргументов
// Загрузить акселераторы
hAccel=LoadAccelerators(hThisInst,"MYMENU");
ShowWindow(hwnd,nWinMode); // Показать окно и
UpdateWindow(hwnd); // перерисовать содержимое
// Запустить цикл обработки сообщений
while(GetMessage (&msg,NULL,0,0))
if(!TranslateAccelerator(hwnd,hAccel,&msg))
{
TranslateMessage(&msg); // Использ.Клавиатуры
DispatchMessage (&msg); // Возврат к Windows
}
return msg. wParam;
}
// Следующая функция вызывается операционной системой
// Windows и получает в качестве параметров сообщения
// из очереди сообщений данного приложения
LRESULT CALLBACK WindowFunc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT paintstruct;
TEXTMETRIC tm;
SIZE size;
switch(message)
{
case WM_CREATE: // Получаем размеры экрана
maxX=GetSystemMetrics(SM_CXSCREEN);
maxY=GetSystemMetrics(SM_CYSCREEN);
hdc=GetDC(hwnd); // Совмест. с окном растр
memdc=CreateCompatibleDC(hdc);
hbit=CreateCompatibleBitmap(hdc,maxX,maxY);
SelectObject(memdc,hbit);
hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(memdc,hbrush);
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);
ReleaseDC(hwnd,hdc);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_SHOW:
SetTextColor(memdc,RGB(0,0,0)); // Черн
SetBkColor(memdc,RGB(0,255,255)); //Син
GetTextMetrics(memdc,&tm); // Метрики
sprintf(str,
"Высота шрифта %ld пикселей",
tm.tmHeight);
TextOut(memdc,X,Y,str,strlen(str));
Y=Y+tm.tmHeight // Следующая
+tm.tmExternalLeading; // строка
strcpy(str,"Это следующая строка.");
TextOut(memdc,X,Y,str,strlen(str));
GetTextExtentPoint32(memdc,str, //Длина
strlen(str),
&size);
sprintf(str,
"Длина предыдущей строки %ld",
size.cx);
X=size.cx; // В конец предыдущей строки
TextOut(memdc,X,Y,str,strlen(str));
Y=Y+tm.tmHeight // Следующая
+tm.tmExternalLeading; // строка
X=0; // X опять в начало
sprintf(str,"Размеры экрана %d x %d",
maxX,maxY);
TextOut(memdc,X,Y,str,strlen(str));
Y=Y+tm.tmHeight // Следующая
+tm.tmExternalLeading; // строка
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_RESET:
X=Y=0; // Стереть перерисовкой фона
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_LOAD:
// Если файл MyDLL.DLL уже загружен,
// сообщить
// пользователю и завершить эту функцию
if(gLibMyDLL!= NULL)
MessageBox(hwnd,
"Библиотека MyDLL уже загружена",
"Внимание:",MB_OK);
else
{
MessageBox(hwnd,"Загрузить DLL",
"Команда:",MB_OK);
// Загрузить MyDLL.dll
gLibMyDLL=LoadLibrary("MYDLL.DLL");
// Если DLL не был загружен, вывести
// окно сообщения об ошибке
if(gLibMyDLL == NULL)
{
MessageBox(hwnd,
"Невозможно загрузить MyDLL. "
"Убедитесь, что она находится"
" в соответствующей папке",
"Ошибка:",MB_OK);
return 0;
}
}
// Получить адрес функции МуВеер()
// библиотеки MyDLL.dll
MyBeep=(MYBEEP)
GetProcAddress(gLibMyDLL,
"MyBeep");
// Получить адрес функции MyDelay()
// библиотеки MyDLL.dll
MyDelay=(MYDELAY)
GetProcAddress(gLibMyDLL,
"MyDelay");
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_BEEP:
if(MyDelay!= NULL && MyBeep!= NULL
&& gLibMyDLL!= NULL)
{
// Вызвать функцию MyBeep()
MyBeep(600,250); // 600 Гц, 0,25 сек
// Вызвать функцию MyDelay()
MyDelay(500); // 0,5 сек
// Вызвать функцию MyBeep()
MyBeep(300,250); // 300 Гц, 0,25 сек
MessageBox(hwnd,"Звуковой сигнал",
"Команда:", MB_OK);
}
else
MessageBox(hwnd,
"Причины: библиотека MyDLL "
"не загружена, либо "
"отсутствуют функции "
"MyBeep и MyDelay ",
"Ошибка:", MB_OK);
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_HELP:
MessageBox(hwnd,"F2: Вывести текст \n"
"F3: В начала экрана\n"
"F4: Загрузить DLL \n"
"F5: Звуковой сигнал",
"Помощь",MB_OK);
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
}
break;
case WM_PAINT: // Перерисовка окна
hdc=BeginPaint(hwnd,&paintstruct); // Пол. DC
// Теперь копируем растр из памяти на экран
BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);
EndPaint(hwnd,&paintstruct); // Освободить DC
break;
case WM_DESTROY: // Завершение программы
DeleteDC(memdc); // Удалить виртуальное окно
PostQuitMessage(0);
break;
default:
// Все сообщения, не обрабатываемые в данной
// функции, направляются на обработку по
// умолчанию
return DefWindowProc(hwnd,message,
wParam,lParam);
}
return 0;
}
![]() |
Результаты выполнения функций из динамической библиотеки показаны на рис. 13.2.
Рис. 13.2. Демонстрация работы с библиотекой MyDLL