Н. А. Литвиненко



Pdf просмотр
страница11/15
Дата28.11.2016
Размер8.29 Mb.
Просмотров3180
Скачиваний1
1   ...   7   8   9   10   11   12   13   14   15
Глава 5

Библиотеки динамической

компоновки
DLL
Одной из особенностей Windows-приложений является их способность подключать во время работы необходимые функции и ресурсы, которые размещены в так назы- ваемых библиотеках динамической компоновки (Dynamic-Link Libraries, DLL). Все функции Windows содержатся в dll-файлах, например, графические функции раз- мещены в файле gdi32.dll
. Преимущество использования библиотек динамиче- ской компоновки перед статическими библиотеками проявляется в том, что прило- жение, состоящее даже из нескольких модулей, использует лишь один экземпляр функции, тогда как из статической библиотеки каждый программный модуль при- соединяет свою копию функции на этапе компоновки. Рассмотрим подробно спо- собы создания динамических библиотек и их использование.
Создание
DLL
В среде Visual Studio создание новой DLL-библиотеки не представляет больших сложностей. Нужно при создании нового Win32-проекта выбрать тип DLL. Будет создан файл реализации dllmain.cpp
, который может служить заготовкой новой
DLL-библиотеки, а также установятся необходимые ключи компоновки:
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{ switch (ul_reason_for_call)
{ case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break;
} return TRUE;
}

Глава 5

198
Следует обратить внимание на то, что имя головной функции фиксировано —
DllMain()
. Эта функция будет вызываться при загрузке и выгрузке DLL-библиотеки.
Все, что нам пока требуется знать об этой функции, это то, что она должна вернуть
TRUE при успешной загрузке библиотеки.
Далее в файле реализации проекта должны располагаться функции, которые мы хотели бы поместить в данную библиотеку.
Чтобы процесс создания DLL-библиотеки был более ясен, рассмотрим его на при- мере (листинг 5.1), в котором создается библиотека, состоящая всего лишь из од- ной функции, строящей треугольник. Поскольку пример небольшой, то приведем его полностью.
Листинг 5.1. Описание
DLL-
библиотеки (dllmain.cpp)

#include "stdafx.h"
__declspec(dllexport) BOOL WINAPI Triangle(HDC, POINT*);
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{ return TRUE; //переключатель switch нам пока не нужен
}
BOOL WINAPI Triangle(HDC hdc, POINT *pt)
{
MoveToEx(hdc, pt[0].x, pt[0].y,NULL);
LineTo(hdc, pt[1].x, pt[1].y);
LineTo(hdc, pt[2].x, pt[2].y); return LineTo(hdc, pt[0].x, pt[0].y);
}
Функция
Triangle() принимает контекст устройства hdc и массив из трех точек типа
POINT
. Треугольник строится вызовом стандартных функций
MoveToEx() и
LineTo()
. В качестве возвращаемого значения используется значение, которое вер- нет последний вызов функции
LineTo()
. Разумно предположить, что если этот вы- зов был удачным, то и наша функция должна сообщить об удачном завершении.
Спецификатор
WINAPI означает стандартное соглашение Windows для порядка пе- редачи параметров GDI-функций.
Особый интерес вызывает описание прототипа функции:
__declspec(dllexport) BOOL WINAPI Triangle(HDC, POINT*);
Директива
__declspec(dllexport)
используется компоновщиком для построения раздела экспорта dll-файла.

Библиотеки динамической компоновки
DLL
199
П
Р И МЕ Ч А Н И Е

DLL
, как и
EXE- файл, имеет несколько разделов, в частности разделы экспорта и им- порта, которые используются при его загрузке для поиска экспортируемых из библио- теки функций, а также функций, импортируемых из других библиотек.
При обнаружении хотя бы одного экспортируемого элемента компоновщик кроме dll строит и lib-файл, структура которого отличается от соответствующего файла статической библиотеки. Здесь lib-файл содержит только имена экспортируемых функций и их адреса в библиотеке динамической компоновки.
Функция
Triangle() использует две стандартные функции, прототипы которых размещены в файле включений wingdi.h
:
WINGDIAPI BOOL WINAPI MoveToEx(HDC, int, int, LPPOINT);
WINGDIAPI BOOL WINAPI LineTo(HDC, int, int);
Если посмотреть, что такое
WINGDIAPI
, то можно найти определение:
#define WINGDIAPI DECLSPEC_IMPORT
Отслеживая определение
DECLSPEC_IMPORT
, мы находим:
#define DECLSPEC_IMPORT __declspec(dllimport)
Фактически перед функциями
MoveToEx() и
LineTo() расположен спецификатор
__declspec(dllimport)
Он служит указанием компоновщику для создания раздела импорта, куда помеща- ются ссылки на эти функции.
Использование
DLL
Итак мы создали DLL-библиотеку из одной функции, но это не принципиально — функций может быть сколько угодно много. Библиотеку нужно скомпилировать и убедиться, что папка проекта содержит lib- и dll-файлы. Теперь возникает вопрос, как все это применить?
Существует два способа использования DLL-библиотек: неявное (implicit linking) и явное (explicit linking) связывание.
Неявное связывание

Неявное связывание (Implicit linking) — самый распространенный в настоящее время способ загрузки DLL-библиотек. Суть его заключается в том, что компонов- щик при построении исполняемого exe-файла не включает код функций в тело про- граммы, а создает раздел импорта, где перечисляются символические имена функ- ций и переменных для каждой из DLL-библиотек. Для этого к проекту необходимо подключить соответствующий lib-файл. При запуске программы загрузчик опера- ционной системы анализирует раздел импорта, загружает все необходимые DLL- библиотеки и, что важно, проецирует их на адресное пространство загружаемого приложения. Причем, если в загружаемой DLL-библиотеке существует свой раздел импорта, то загружаются и те dll-файлы, которые там указаны. Однако если биб-

Глава 5

200
лиотека один раз уже загружена, то второй раз она не загружается, а строится ссылка на уже существующий экземпляр.
Поиск DLL-библиотек осуществляется по стандартной схеме:
1.
В папке, откуда запущено приложение.
2.
В текущей папке.
3.
В системной папке Windows\system32.
4.
В папке Windows.
5.
В папках, которые перечислены в переменной окружения PATH.
Если библиотека не найдена, загрузка приложения прекращается и выводится со- общение об ошибке.
Посмотрим, как же будет загружаться наша DLL-библиотека (листинг 5.1).
Поскольку она имеет раздел экспорта, то библиотека загружается в память, но функция
Triangle()
использует еще две стандартные функции, которые помещены в разделе импорта DLL-библиотеки. Если говорить точнее, то в нашей библиотеке хранятся не коды этих функций, а лишь ссылка на них из библиотеки gdi32.dll
Таким образом, при загрузке приложения, когда дело дойдет до загрузки DLL- библиотек, вначале загрузится библиотека
Triangle.dll
, затем будет загружена gdi32.dll
. Эта библиотека, как правило, уже загружена, поскольку используется операционной системой, поэтому будут использованы ссылки на функции
MoveToEx()
и
LineTo()
из уже имеющейся в памяти библиотеки.
Рассмотрим задачу для демонстрации разработанной нами библиотеки DLL (лис- тинг 5.2). Создадим новый проект, скопируем в папку проекта файлы
Triangle.dll и
Triangle.lib
. Последний файл необходимо явно подключить к проекту, что можно сделать в меню Project | Properties… на вкладке Linker | Input с помощью диалогового окна Additional Dependencies (см. рис. 2.3).
Листинг 5.2. Пример неявного связывания
DLL-
библиотеки

WINGDIAPI BOOL WINAPI Triangle(HDC, POINT*);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc; static HPEN hPen; static int sx, sy;
POINT pt[3]; switch (message)
{ case WM_CREATE: hPen = CreatePen(PS_SOLID, 4, RGB(0, 0, 255)); break;

Библиотеки динамической компоновки
DLL
201
case WM_SIZE: sx = LOWORD(lParam); sy = HIWORD(lParam); break; case WM_COMMAND: switch (LOWORD(wParam))
{ case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps);
SelectObject(hdc, hPen); pt[0].x = sx/4; pt[0].y = pt[1].y = sy/4; pt[1].x = sx*3/4; pt[2].x = sx/2; pt[2].y = sy/2;
Triangle(hdc, pt);
EndPaint(hWnd, &ps); break; case WM_DESTROY:
DeleteObject(hPen);
PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Для созданной нами DLL-библиотеки желательно подготовить заголовочный h-файл с прототипами функций. Однако можно описать прототип функции в тексте программы. Обратите внимание, что в качестве директивы компоновщика исполь- зуется
WINGDIAPI
, поскольку, как мы выяснили ранее, это то же самое, что и
__declspec(dllimport)
Из файла
Triangle.cpp мы приводим лишь оконную функцию, в которой опишем дескриптор пера hPen
, две переменных целого типа для хранения размеров окна sx
,
sy
, а также массив точек pt[3]
Сплошное синее перо толщиной 4 пиксела создадим в сообщении
WM_CREATE
: hPen = CreatePen(PS_SOLID, 4, RGB(0, 0, 255));
В сообщении
WM_SIZE определим размер клиентской области окна sx
, sy

Глава 5

202
Все рисование сосредоточим в сообщении
WM_PAINT
, где получим контекст устрой- ства и выберем для него перо:
SelectObject(hdc, hPen);
Теперь заполним поля массива pt для рисования треугольника в верхней части окна: pt[0].x = sx/4; pt[0].y = pt[1].y = sy/4; pt[1].x = sx*3/4; pt[2].x = sx/2; pt[2].y = sy/2;
Обращаемся к функции
Triangle() как к обычной GDI-функции:
Triangle(hdc, pt);
Задача решена. Нужно не забыть уничтожить перо при закрытии окна.
После запуска программы будет выведено окно с изображением треугольника
(рис. 5.1). При изменении размеров окна треугольник будет перестраиваться.
П
Р И МЕ Ч А Н И Е

Если запустить созданный exe- файл в командной строке, то приложение не сможет выполниться, поскольку в текущей папке не будет
DLL- библиотеки. Решением про- блемы может быть размещение библиотеки динамической компоновки в той же папке, где находится файл приложения, либо в одной из папок поиска
DLL- библиотек.

Рис.
5.1.
Графические построения с применением пользовательской
DLL- библиотеки
DLL
общего использования

Созданная нами DLL-библиотека работает с программами, написанными на языке
С++. Если же попытаться использовать ее в программе, написанной на другом язы- ке программирования, мы выясним, что в ней отсутствует функция с именем
Triangle
. Но имеется функция
? Triangle@@YGHPAUHDC__@@PAUtagPOINT@@@Z.
Дело в том, что по соглашению языка С++ функция идентифицируется не только именем, но и типами формальных параметров, что называется сигнатурой функции.
Нетрудно сообразить, что именно эта особенность С++ позволяет определять несколько функций с одним именем, но с разным числом и типом параметров.

Библиотеки динамической компоновки
DLL
203
В нашей ситуации это является скорее помехой, поскольку вряд ли кому понравит- ся строить такие специфические имена функций.
Однако когда приложение создается в С++, то имя так и строится, поэтому проблем с поиском функций не возникает. Другое дело, если приложение пишется на дру- гом языке, который "ничего не знает" о соглашениях С++.
Одним из выходов в данной ситуации является использование спецификатора ком- поновки для классического языка C, где искажения имени функции не происходит: extern "язык" прототип функции
Такая конструкция позволяет строить обращение к функции в соответствии со стандартом выбранного языка. Например, в нашем случае функцию можно было бы описать так: extern "C" __declspec(dllexport) BOOL Triangle(HDC, POINT*);
П
Р И МЕ Ч А Н И Е

К сожалению, имеющиеся в настоящее время компиляторы фирмы
Microsoft поддер- живают спецификатор компоновки только для языка С.
Обратите внимание, что в этом случае функция
Triangle() использует стандарт- ный для классического С порядок передачи параметров. Если необходимо создать библиотеку функций обратного вызова
CALLBACK
, необходимо использовать иной подход.
Более приемлемый путь решения данной задачи заключается в использовании def- файла (файла определений с расширением def — от англ. definition). Его можно до- бавить к проекту в меню Project | Add New Item… | Code | Module-Definition File
(.def) (рис. 5.2).

Рис.
5.2.
Добавление к проекту файла определений

Глава 5

204
Если при разработке проекта DLL-библиотеки включить в него def-файл с парой строк, которые представляют раздел экспорта:
EXPORTS
Triangle то компоновщик помещает в раздел экспорта библиотеки функцию с именем
Triangle без искажений. После этого библиотека станет доступной для использо- вания любому Windows-приложению.
П
Р И МЕ Ч А Н И Е

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

Неявное связывание, несмотря на свою очевидную простоту, имеет и определенные недостатки. Основным из них является одновременная (с приложением) загрузка и последующее удаление из памяти всех DLL-библиотек, независимо от того, что большая часть из них может и не понадобиться. Однако имеется возможность во время работы приложения загружать только необходимые DLL, освобождая зани- маемую ими память, когда потребность в них отпала. Такой способ называют
явным связыванием (explicit linking).
Каждое приложение имеет свое адресное пространство, и чтобы получить доступ к функциям и ресурсам, размещенным в DLL-библиотеке, нужно спроецировать библиотеку на адресное пространство приложения. Это можно сделать функцией
LoadLibrary()
:
HMODULE WINAPI LoadLibraryW(LPCWSTR lpLibFileName); аргументом которой является имя DLL-библиотеки.
Функция возвращает дескриптор спроецированного в память dll-файла.
HMODULE
— тип возвращаемого значения, это просто другое обозначение дескрип- тора приложения
HINSTANCE
Функция
FreeLibrary() выгружает DLL-библиотеку из памяти:
BOOL WINAPI FreeLibrary(HMODULE hLibModule);
Эта функция принимает дескриптор, полученный при загрузке DLL, и возвращает
TRUE
при успешном завершении.
П
Р И МЕ Ч А Н И Е

На самом деле функция FreeLibrary() лишь уменьшает счетчик пользователей библиотеки на 1, а выгружается библиотека из памяти тогда, когда счетчик пользова- телей становится равным 0.
Рассмотрим реализацию библиотеки для загрузки ее явно. Мы должны обеспечить неизменность имен функций, входящих в библиотеку. Этого проще всего добиться использованием def-файла. Добавим к проекту библиотеки def-файл и перекомпи- лируем проект.

Библиотеки динамической компоновки
DLL
205
Создадим новый проект ExplicitLinking (листинг 5.3) для использования этой биб- лиотеки и скопируем в этот проект единственный файл
Triangle.dll
. При явной загрузке lib-файл нам не понадобится, и не нужно включать его в проект.
Листинг 5.3. Явная загрузка
DLL-
функций

typedef BOOL (WINAPI* PFN) (HDC, POINT*);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc; static HINSTANCE hLibrary; static PFN pfnTriangle; static HPEN hPen; static int sx, sy;
POINT pt[3]; switch (message)
{ case WM_CREATE: hLibrary = LoadLibrary(_T("Triangle")); if (hLibrary)
{ pfnTriangle = (PFN)GetProcAddress(hLibrary, "Triangle"); if (pfnTriangle == NULL)
{

MessageBox(hWnd, _T("Функция Triangle не найдена"),

_T("LoadLibrary"), MB_OK | MB_ICONQUESTION);

DestroyWindow(hWnd);
return 0;
} hPen = CreatePen(PS_SOLID, 4, RGB(0, 0, 255));
} else
{
MessageBox(hWnd, _T("Библиотека не найдена"), _T("LoadLibrary"),

MB_OK | MB_ICONQUESTION);
DestroyWindow(hWnd); return 0;
} break; case WM_SIZE: sx = LOWORD(lParam); sy = HIWORD(lParam); break;

Глава 5

206
case WM_COMMAND: switch (LOWORD(wParam))
{ case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps);
SelectObject(hdc, hPen); pt[0].x = sx/4; pt[0].y = pt[1].y = sy/4; pt[1].x = sx*3/4; pt[2].x = sx/2; pt[2].y = sy/2; pfnTriangle(hdc, pt);
EndPaint(hWnd, &ps); break; case WM_DESTROY:
FreeLibrary(hLibrary);
DeleteObject(hPen);
PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
При создании окна в сообщении
WM_CREATE
загружаем библиотеку: hLibrary = LoadLibrary(_T("Triangle"));
П
Р И МЕ Ч А Н И Е

В аргументе функции LoadLibrary() не следует указывать расширение имени dll, поскольку в этом случае поиск библиотеки будет осуществляться, начиная с каталога "известных
DLL
", прописанного в разделе
KnownDLLs реестра
Windows.
Возвращаемое значение сохраняем в переменной hLibrary типа
HINSTANCE
. В слу- чае успешной загрузки hLibrary отлично от нуля, и мы можем получить адрес библиотечной функции при помощи
GetProcAddress()
:
FARPROC WINAPI GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
Здесь второй параметр lpProcName
— С-строка.
П
Р И МЕ Ч А Н И Е

Адрес библиотечной функции отыскивается по ее имени, поэтому, даже после моди- фикации библиотеки, если имя и параметры функции не изменились, она будет най- дена. Кодировка
Unicode в именах функций
DLL- библиотеки не используется.

Библиотеки динамической компоновки
DLL
207
Для возвращаемого значения необходимо создать новый тип данных
PFN
— указа- тель на функцию с двумя параметрами. Воспользуемся оператором typedef и на глобальном уровне определим этот тип: typedef BOOL (WINAPI* PFN) (HDC, POINT*);
Переменную pfnTriangle типа
PFN опишем как статическую, это и будет указа- тель на функцию
Triangle из динамической библиотеки. Сейчас ее можно исполь- зовать как имя функции, что мы и сделаем в сообщении
WM_PAINT
: pfnTriangle(hdc, pt);
Директивой
FreeLibrary(hLibrary)
выгрузим библиотеку перед закрытием окна.
П
Р И МЕ Ч А Н И Е

Функция LoadLibrary()
, перед тем как загрузить библиотеку, проверяет наличие такой библиотеки в памяти
, и, если библиотека уже загружена, просто увеличивает счетчик обращений к ней на 1
и отображает ее на адресное пространство приложения.
Загрузка ресурсов из
DLL
Помимо функций из DLL-библиотеки можно загрузить и ресурсы. Для примера создадим простенькую DLL, содержащую в ресурсе одну иконку, и рассмотрим два способа ее извлечения при явном и неявном связывании.
Создадим проект DLL-библиотеки и импортируем туда иконку
IDI_ICON1
Листинг 5.4. Библиотека динамической компоновки, содержащая ресурсы

#include "stdafx.h"
#include "resource.h"
__declspec(dllexport) HICON hIcon;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved

)
{ switch (ul_reason_for_call)
{ case DLL_PROCESS_ATTACH: hIcon = LoadIcon(hModule, MAKEINTRESOURCE(IDI_ICON1)); break; case DLL_PROCESS_DETACH:
DestroyIcon(hIcon); break;
} return TRUE;
}

Глава 5

208
Программный код библиотеки не содержит ни одной функции, а только описание глобальной переменной hIcon
, объявленной как "экспортируемая":
__declspec(dllexport) HICON hIcon;
Функция
DllMain()
, которую называют функцией входа/выхода, вызывается операци- онной системой при загрузке и выгрузке DLL-библиотеки и имеет три параметра:

HModule
— дескриптор библиотеки, присваивается при загрузке;

ul_reason_for_call
— код уведомления;

lpReserved
— зарезервировано для дальнейшего применения.
Код уведомления ul_reason_for_call может принимать одно из четырех значений:

DLL_PROCESS_ATTACH
— при создании нового процесса;

DLL_PROCESS_DETACH
— при завершении процесса;

DLL_THREAD_ATTACH
— при создании нового потока;

DLL_THREAD_DETACH
— при завершении потока.
Обычно при обработке этих уведомлений в DLL-библиотеке осуществляются опе- рации по выделению и освобождению необходимых для работы ресурсов операци- онной системы. Например, при загрузке библиотеки требуется выделить память, а при ее выгрузке — освободить.
Иконку можно загрузить из ресурса библиотеки функцией
LoadIcon()
: hIcon = LoadIcon(hModule, MAKEINTRESOURCE(IDI_ICON1));
Мы сохраняем в глобальной переменной дескриптор иконки, чтобы при выгрузке библиотеки из памяти по уведомлению
DLL_PROCESS_DETACH
освободить память:
DestroyIcon(hIcon);
Поскольку потоков создавать не планируется, то уведомления
DLL_THREAD_ATTACH
и
DLL_THREAD_DETACH
обрабатывать не будем.
Рассмотрим два способа загрузки иконки:
1.
Создадим проект демонстрационной задачи. В папку этого проекта скопируем созданные файлы библиотеки
DllIcon.dll и
DllIcon.lib
В свойствах проекта добавим имя библиотеки
DllIcon.lib
. На глобальном уровне опишем импортируемую переменную:
__declspec(dllimport) HICON hIcon; а в сообщении
WM_CREATE переопределим малую иконку класса окна:
SetClassLong(hWnd, GCL_HICONSM, (LONG)hIcon);
Поскольку окно прорисовывается после обработки этого сообщения, мы увидим в заголовке окна новую пиктограмму приложения.
2.
Для явной загрузки библиотеки создадим новый проект, нам понадобится лишь dll-файл созданной библиотеки, подключать lib-файл не нужно. В сообщении
WM_CREATE
необходимо получить дескриптор библиотеки: hDll = LoadLibrary(_T("DllIcon"));

Библиотеки динамической компоновки
DLL
209
передавая ей в качестве параметра имя DLL-файла. Далее, функцией
GetProcAddress()
находим дескриптор иконки, уже загруженной в библиотеке, передавая ей дескриптор иконки как текстовую строку. hIcon = *((HICON*)GetProcAddress(hDll, "hIcon"));
Переменная hIcon описана как
HICON
После этого мы можем, как и в предыдущем случае, изменить малую иконку класса окна:
SetClassLong(hWnd, GCL_HICONSM, (LONG)hIcon);
Результат работы этого варианта программы будет идентичен предыдущему. lib- файл в проекте нам не нужен, но нужно иметь в виду, что DLL-библиотека должна быть создана с использованием def-файла:
EXPORTS hIcon
П
Р И МЕ Ч А Н И Е

В
Visual Studio имеется специальная утилита dumpbin
,
которая позволяет просмот- реть содержимое разделов dll- файла. Протокол будет выглядеть так:
dumpbin /exports DllIcon.dll
Section contains the following exports for DllIcon.dll
00000000 characteristics
48F621AA time date stamp Wed Oct 15 23:00:26 2008 0.00 version
1 ordinal base
1 number of functions
1 number of names ordinal hint RVA name
1 0 00017138 hIcon
Весь набор ключей dumpbin можно получить командой: dumpbin /?. Нужно только иметь в виду, что для работы данной утилиты требуется, чтобы в пределах доступных путей поиска находились два модуля: link.exe
, mspdb100.dll
При использовании командного процессора
Total

либо
Windows Commander
, можно вос- пользоваться стандартным просмотрщиком, который "умеет видеть" структуру dll
- файла.
DLL,
содержащие только ресурсы

Можно использовать DLL-библиотеку, как контейнер для хранения ресурсов, на- пример: курсоров, иконок и пр. В этом случае головная функция
DllMain() ничего не делает и будет выглядеть так:
BOOL APIENTRY DllMain( HMODULE, DWORD, LPVOID)
{ return TRUE;
}
Поскольку в такой библиотеке не будет экспортируемых переменных, lib-файл не создается и библиотека может быть загружена только явно. Для доступа к ресурсам необходимо сохранить значение их идентификаторов, которые определены в файле resource.h
, например:
#define IDI_ICON1 101

Глава 5

210
Эти определения лучше всего разместить в h-файле и поставлять вместе с dll- файлом.
Если создать такую библиотеку, то обработчик сообщения
WM_CREATE
мог бы вы- глядеть так:
HMODULE hDll;
HICON hIcon; case WM_CREATE: hDll = LoadLibrary(_T("DllIcon")); hIcon = LoadIcon(hDll, MAKEINTRESOURCE(IDI_ICON1));
SetClassLong(hWnd, GCL_HICONSM, (LONG)hIcon); break;
Вопросы к главе

1.
В чем отличие библиотек динамической компоновки от статических библиотек?
2.
Какие действия выполняет функция
DllMain()
, коды уведомлений?
3.
Объявление экспортируемых переменных и функций. Проблема "искажения" имен, спецификатор компоновки extern "C" и def-файл.
4.
Явное и неявное связывание DLL-библиотек.
5.
Пути поиска dll-файла.
6.
Формат функций
LoadLibrary()
,
GetProcAddress()
,
FreeLibrary()
7.
Как найти адрес функции и переменной в DLL-библиотеке?
8.
Как создать DLL-библиотеку для хранения ресурсов?
Задания

для самостоятельной работы

1.
Создайте DLL-библиотеку
UserString для работы с С-строкой, включив в нее аналоги стандартных функций: strlen()
, strcpy()
, strcat()
, strrev()

2.
Постройте демонстрационную задачу для использования созданной библиотеки при неявном и явном связывании.
3.
Создайте библиотеку, содержащую набор ресурсов: иконку, курсор, растровое изображение. Постройте демонстрационную задачу, использующую ресурсы этой DLL-библиотеки.
4.
Создайте DLL-библиотеку с галереей рисунков одинакового размера. В число экспортируемых переменных включите название галереи и количество изобра- жений. Окно создаваемого приложения заполните этими рисунками.
5.
При помощи утилиты dumpbin просмотрите разделы экспорта и импорта создан- ных библиотек.
1   ...   7   8   9   10   11   12   13   14   15


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал