Создание DLL, содержащей одни лишь ресурсы
Файл ресурсов надо скомпилировать - "rc MyResDll.rc" - и преобразовать линкером в DLL, обязательно указав флаг "/NOENTRY", т. к. эта динамическая библиотека содержит исключительно одни ресурсы и ни строки кода: "link MyRedDll.res /DLL /NOENTRY".
В Visual Studio это сделать еще проще - достаточно кликнуть по папке "Resourses" окна "File View" и добавить новый файл ресурса, который затем можно будет модифицировать визуальным редактором по своему усмотрению.
Для загрузки ресурса из DLL - в принципе, можно воспользоваться уже знакомой нам функцией LoadLibray, и передавать возращенный ею дескриптор LoadString или другой функции, работающей с ресурсами. Однако загрузку динамической библиотеки можно значительно ускорить, если "объяснить" системе, что эта DLL не содержит ничего, кроме ресурсов, и нам достаточно лишь спроецировать ее на адресное пространство процесса, а обо всем остальном мы сумеем позаботиться и самостоятельно.
Вот тут-то и пригодится функция LoadLibraryEx: ее первый аргумент, как и у коллеги LoadLibrary, задает имя динамической библиотеки для загрузки, второй - зарезервирован и должен быть равен нулю, а третий, будучи равным LOAD_LIBRARY_AS_DATAFILE, заставляет функцию делать именно то, что нам нужно - загружать DLL как базу данных (если динамическая библиотека содержит помимо ресурсов еще и код, то загрузка с этим ключом проходит все равно успешно, но функции загруженной DLL не будут доступны - только ресурсы):
// DllLoadRes.c
#include <stdio.h>
#include <windows.h>
main()
{
HINSTANCE h;
char buf[100];
h=LoadLibraryEx("MyResDll.dll",0,
LOAD_LIBRARY_AS_DATAFILE);
LoadString(h,1,&buf[0],99);
printf("%s\n",&buf[0]);
}
Демонстрация оптимизированной загрузки DLL, не содержащей ничего кроме ресурсов
Эта программа компилируются точно так же, как и предыдущие примеры явной компоновки - и после запуска победно выводит на экране "Hello, Word!", подтверждая, что ресурс "строка" из динамической библиотеки был успешно загружен! Аналогичным способом можно загружать ресурсы из исполняемых файлов; с этой точки зрения они ничем не отличаются от динамических библиотек.
ООП и DLL
Для создания dll-файла рекомендуется использовать мастер Visual Studio. Создаем новый тип проекта C++ типа Win32 Project:
Мастер нам сделал заготовку для нашей dll. Будем развивать ее. Для этого добавим 1 функцию и один класс с методом. Вот код:
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
//Наш код.
//Добавляем функцию Add.
__declspec(dllexport) int Add(int a, int b)
{
return a + b;
}
//Добавляем класс MyClass с методом MyMax.
class MyClass {
public:
__declspec(dllexport) int MyMax(int a, int b){
int res;
if(a>b) res = a; else res = b;
return res;
}
};
Мастер нам сделал заготовку для нашей dll. Будем развивать ее. Для этого добавим 1 функцию и один класс с методом. Вот код:
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
//Наш код.
//Добавляем функцию Add.
__declspec(dllexport) int Add(int a, int b)
{
return a + b;
}
//Добавляем класс MyClass с методом MyMax.
class MyClass {
public:
__declspec(dllexport) int MyMax(int a, int b){
int res;
if(a>b) res = a; else res = b;
return res;
}
};
Обратите внимание, что добавляемы функции и методы, которые мы хотим вызывать извне, вы пишем с модификатором __declspec(dllexport). Таким образом мы помечаем так называемые экспортируемые функции.
Компилируем программу. В папке debug проекта должен появится файл firstdll.dll.
Теперь пишем тестовую программу.
#include <iostream.h>
//Подключаем необходимый заголовочный файл.
#include "..\firstdll.cpp"
void main()
{
MyClass w;
cout<<w.MyMax(22, -40)<<"\n";
cout<<Add(10, 1)<<"\n";
}
Запускаем программу. Как и следовало ожидать, она выведет 22 и 11.
Функция DllMain
Большинство библиотек DLL - просто коллекции практически независимых друг от друга функций, экспортируемых в приложения и используемых в них. Кроме функций, предназначенных для экспортирования, в каждой библиотеке DLL есть функция DllMain. Эта функция предназначена для инициализации и очистки DLL. Она пришла на смену функциям LibMain и WEP, применявшимся в предыдущих версиях Windows. Структура простейшей функции DllMain может выглядеть, например, так:
BOOL WINAPI DllMain (HANDLE hInst,DWORD dwReason, LPVOID IpReserved) {
BOOL bAllWentWell=TRUE;
switch (dwReason) {
case DLL_PROCESS_ATTACH: // Инициализация процесса.
break;
case DLL_THREAD_ATTACH: // Инициализация потока.
break;
case DLL_THREAD_DETACH: // Очистка структур потока.
break;
case DLL_PROCESS_DETACH: // Очистка структур процесса.
break;
}
if(bAllWentWell) return TRUE;
else return FALSE;
}
Функция DllMain вызывается в нескольких случаях. Причина ее вызова определяется параметром dwReason, который может принимать одно из следующих значений.
При первой загрузке библиотеки DLL процессом вызывается функция DllMain с dwReason, равным DLL_PROCESS_ATTACH. Каждый раз при создании процессом нового потока DllMain вызывается с dwReason, равным DLL_THREAD_ATTACH (кроме первого потока, потому что в этом случае dwReason равен DLL_PROCESS_ATTACH).
По окончании работы процесса с DLL функция DllMain вызывается с параметром dwReason, равным DLL_PROCESS_DETACH. При уничтожении потока (кроме первого) dwReason будет равен DLL_THREAD_DETACH.
Все операции по инициализации и очистке для процессов и потоков, в которых нуждается DLL, необходимо выполнять на основании значения dwReason, как было показано в предыдущем примере. Инициализация процессов обычно ограничивается выделением ресурсов, совместно используемых потоками, в частности загрузкой разделяемых файлов и инициализацией библиотек. Инициализация потоков применяется для настройки режимов, свойственных только данному потоку, например для инициализации локальной памяти.
В состав DLL могут входить ресурсы, не принадлежащие вызывающему эту библиотеку приложению. Если функции DLL работают с ресурсами DLL, было бы, очевидно, полезно сохранить где-нибудь в укромном месте дескриптор hInst и использовать его при загрузке ресурсов из DLL. Указатель IpReserved зарезервирован для внутреннего использования Windows. Следовательно, приложение не должно претендовать на него. Можно лишь проверить его значение. Если библиотека DLL была загружена динамически, оно будет равно NULL. При статической загрузке этот указатель будет ненулевым.
В случае успешного завершения функция DllMain должна возвращать TRUE. В случае возникновения ошибки возвращается FALSE, и дальнейшие действия прекращаются.
Замечание. Если не написать собственной функции DllMain(), компилятор подключит стандартную версию, которая просто возвращает TRUE.
Пример проблемы:
{
HANDLE hThread; DWORD dwThreadId;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// DLL проецируется на адресное пространство процесса
// создаем поток для выполнения какой-то работы
hThread = CreateThread(NULL, 0, SomeFunction, NULL, 0, &dwThreadId);
// задерживаем наш поток до завершения нового потока
WaitForSingleObject(hThread, INFINITE);
// доступ к новому потоку больше не нужен
CloseHandle(hThread);
break;
case DLL_THREAD_ATTACH:
// создается еще один поток
break;
case DLL_THREAD_DETACH:
// поток завершается корректно
break;
case DLL_PROCESS_DETACH:
// DLL выгружается из адресного пространства процесса
break;
}
return(TRUE);
}
Функции точки входа должно выполнять только простые задачи инициализации. Они не должны вызвать функцию LoadLibrary или LoadLibraryEx (или функцию, которая вызывает эти функции), потому что это может создать циклы взаимозависимости в порядке загрузки DLL. Это может стать результатом того, что DLL начнет использоваться прежде, чем система выполнит код ее инициализации. Точно так же функция точки входа не должна вызвать функцию FreeLibrary (или функцию, которая ее вызывает), потому что это может привести к тому, что DLL начнет использоваться после того, как система выполнила код завершения ее работы.
Пример от Microsoft. Пошаговое руководство. Создание и использование библиотеки DLL (C++) Visual Studio 2013
В этом пошаговом руководстве описывается создание библиотеки динамической компоновки (DLL) для использования с C++ приложением. Использование библиотеки — хороший способ повторного использования кода. Вместо повторной реализации одних и тех же процедур в каждой программе, вы создаете их один раз и затем ссылаетесь на них из других приложений. Поместив код в библиотеке DLL, вы сэкономите место в каждом приложении, которое ссылается на этод код, а так же сможете обновлять DLL без перекомпиляции всех приложений. Дополнительные сведения о библиотеках DLL см. в разделе DLL в Visual C++.
Дата добавления: 2017-01-26; просмотров: 2491;