Исходные тексты DLL-библиотеки DLLDEMO
В качестве примера приведем исходные тексты простейшей DLL-библиотеки DLLDemo.DLL, в которой определены всего две функции. Первая из них - это функция инициализации DLLEntryPoint, а вторая - функция FindApplicationWindow.
Функция инициализации DLLEntryPoint в нашем случае не выполняет никакой работы, однако когда она получает управление, на экране появляется одно из четырех сообщений (в зависимости от значения кода причины вызова). Таким образом, вы сможете проследить ход инициализации DLL-библиотеки при ее отображении в адресное пространство процессов, а также при отключении DLL-библиотеки от процессов.
В задачу функции FindApplicationWindow входит поиск главного окна приложения по заголовку этого окна. В случае успеха функция FindApplicationWindow возвращает идентификатор первого найденного окна с подходящим заголовком, а при неудаче - значение NULL. Вы можете использовать эту функцию для проверки, запущено ли указанное вами приложение, или нет.
Исходный текст DLL-библиотеки представлен в листинге 3.1.
Листинг 3.1. Файл dlldemo\dlldemo.c
// ==================================================
// DLL-библиотека DLLDemo.DLL
// Поиск окна по заданному заголовку
//
// (С) Фролов А.В., 1996
// Email: frolov@glas.apc.org
// ==================================================
#include <windows.h>
#include <windowsx.h>
#include "dlldemo.h"
// Глобальная переменная, в которую записывается
// идентификатор найденного окна или значение NULL,
// если окно с заданным заголовком не найдено
HWND hwndFound;
// -------------------------------------------------
// Функция DLLEntryPoint
// Точка входа DLL-библиотеки
// -------------------------------------------------
BOOL WINAPI DLLEntryPoint(
HMODULE hModule, // идентификатор модуля
DWORD fdwReason, // причина вызова функции DLLEntryPoint
LPVOID lpvReserved) // зарезервировано
{
switch(fdwReason)
{
// Подключение нового процесса
case DLL_PROCESS_ATTACH:
{
MessageBox(NULL, "Process attached", "DLL Demo", MB_OK);
break;
}
// Подключение новой задачи
case DLL_THREAD_ATTACH:
{
MessageBox(NULL, "Thread attached", "DLL Demo", MB_OK);
break;
}
// Отключение процесса
case DLL_PROCESS_DETACH:
{
MessageBox(NULL, "Process detached", "DLL Demo", MB_OK);
break;
}
// Отключение задачи
case DLL_THREAD_DETACH:
{
MessageBox(NULL, "Thread detached", "DLL Demo", MB_OK);
break;
}
}
return TRUE;
}
// -------------------------------------------------
// Функция FindApplicationWindow
// Поиск главного окна приложения по его заголовку
// -------------------------------------------------
HWND FindApplicationWindow(LPSTR lpszWindowTitle)
{
// Запускаем цикл поиска окна с заголовком,
// адрес которого передан функции через
// параметр lpszWindowTitle
EnumWindows(EnumWindowsProc, (LPARAM)lpszWindowTitle);
// Возвращаем значение глобальной переменной hwndFound,
// которое устанавливается функцией обратного вызова
// EnumWindowsProc в зависимости от результата поиска
return hwndFound;
}
// -------------------------------------------------
// Функция EnumWindowsProc
// -------------------------------------------------
BOOL CALLBACK EnumWindowsProc(
HWND hwnd, // идентификатор родительского окна
LPARAM lParam) // адрес строки заголовка окна
{
// Буфер для хранения заголовка окна
char szBuf[512];
// Получаем заголовок окна
GetWindowText(hwnd, szBuf, 512);
// Сравниваем заголовок со строкой, адрес которой
// передан в функцию EnumWindowsProc через параметр lParam
if(!strcmp((LPSTR)lParam, szBuf))
{
// Если заголовок совпал, сохраняем идентификатор
// текущего окна в глобальной переменной hwndFound
hwndFound = hwnd;
// Завершаем цикл просмотра окон
return FALSE;
}
// Если заголовок не совпал, продолжаем поиск
else
{
// Записываем в глобальную переменную hwndFound
// значение NULL. Это признак того, что окно
// с заданным заголовком не было найдено
hwndFound = NULL;
// Для продолжения поиска возвращаем значение TRUE
return TRUE;
}
}
В файле dlldemo.h (листинг 3.2) находятся прототипы функций, определенных в нашей DLL-библиотеке.
Листинг 3.2. Файл dlldemo\dlldemo.h
BOOL WINAPI DLLEntryPoint(HMODULE hModule,
DWORD fdwReason, LPVOID lpvReserved);
HWND FindApplicationWindow(LPSTR lpszWindowTitle);
BOOL CALLBACK EnumWindowsProc(
HWND hwnd, // идентификатор родительского окна
LPARAM lParam); // произвольное значение
Файл определения модуля dlldemo.def DLL-библиотеки представлен в листинге 3.3.
Листинг 3.3. Файл dlldemo\dlldemo.def
EXPORTS
FindApplicationWindow @1
Итак, займемся исходными текстами DLL-библиотеки.
В области глобальных переменных определена переменная hwndFound. В эту переменную будет записан идентификатор найденного окна или значение NULL, если поиск окончился неудачно.
Функция DLLEntryPoint предназначена для инициализации библиотеки. В нашем случае функция инициализации просто выводит на экран различные сообщения в зависимости от кода причины вызова.
Функция FindApplicationWindow ищет главное окно приложения по заголовку этого окна, адрес которого передается ей через параметр lpszWindowTitle.
Для поиска окна мы использовали функцию EnumWindows. В качестве первого параметра этой функции передается адрес функции обратного вызова, которая будет использована для сравнения заголовков всех окон с заданным, а в качестве второго - адрес искомого заголовка:
EnumWindows(EnumWindowsProc, (LPARAM)lpszWindowTitle);
Если функция EnumWindowsProc найдет окно, она запишет его идентификатор в глобальную переменную hwndFound. Если же приложение с таким заголовком не запущено, в эту переменную будет записано значение NULL.
Функции обратного вызова EnumWindowsProc ( имя функции может быть любым) передаются два параметра: идентификатор окна и 32-разрядное значение, которое передавалось функции EnumWindows в качестве второго параметра. В нашем случае это адрес заголовка искомого окна:
BOOL CALLBACK EnumWindowsProc(
HWND hwnd, // идентификатор родительского окна
LPARAM lParam) // адрес строки заголовка окна
{
char szBuf[512];
GetWindowText(hwnd, szBuf, 512);
if(!strcmp((LPSTR)lParam, szBuf))
{
hwndFound = hwnd;
return FALSE;
}
else
{
hwndFound = NULL;
return TRUE;
}
}
После вызова функции EnumWindows функция EnumWindowsProc будет вызываться в цикле для окна вернего уровня каждого запущенного приложения.
Зная идентификатор окна (который передается функции EnumWindowsProc через первый параметр), мы с помощью функции GetWindowText получаем заголовок окна и записываем его в буфер szBuf. Затем этот заголовок сравнивается с заданным при помощи функции strcmp. Адрес заданного заголовка мы получаем через параметр lParam.
Функция обратного вызова, адрес которой указан в первом параметре функции EnumWindows, может вернуть значение FALSE или TRUE.
В первом случае цикл просмотра окон заканчивается и функция EnumWindows возвращает управление вызвавшему ее приложению. Во втором случае просмотр окон будет продолжен до тех пор, пока функции обратного вызова не будут переданы идентификаторы главных окон всех запущенных приложений.
Если окно найдено, функция обратного вызова записывает его идентификатор в глобальную переменную hwndFound и возвращает значение FALSE, после чего поиск продолжается. Если же заголовки не совпадают, в эту переменную записывается значение NULL, после чего для продолжения поиска функция EnumWindowsProc возвращает значение TRUE.
Несколько слов о настройке проекта DLL-библиотеки DLLDemo.DLL.
При создании проекта DLL-библиотеки “с нуля” вы должны указать тип рабочего пространства проекта (Project Workspace) как Dynamic-Link Library (рис. 3.5).
Рис. 3.5. Создание нового проекта для DLL-библиотеки
Если в вашей DLL-библиотеке определена точка входа (функция инициализации), то в параметрах проекта вы должны указать ее имя. Для этого в системе Microsoft Visual C++ версии 4.0 выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, показанная на рис. 3.6.
Рис. 3.6. Диалоговая панель Project Settings
Пользуясь кнопками в правом верхнем углу этой диалоговой панели, откройте страницу Link. Затем выберите из списка Category строку Output. В поле Entry-point symbol введите имя вашей функции инициализации и нажмите кнопку OK.
Если вы выполняете редактирование загрузочного модуля DLL-библиотеки в пакетном режиме, при запуске редактора связей укажите параметр /entry:”ИмяФункцииИнициализации”.
Напомним, что для функции инициализации DLL-библиотеки вы можете выбрать любое имя. Нужно только указать его в проекте или в параметрах редактора связей.