Создание собственных динамических библиотек (dll-файлов)




 

 
 


В этом параграфе Вы узнаете, что такое 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

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-03-31 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: