Dynamic
Load Library (DLL). Как ими пользоваться?
Введение
Часто
возникает потребность в использовании динамических библиотек (aka
DLL). Примером могут служить всем известные плагины Winamp. По
своей сути они позволяют модернизировать Winamp по кускам.
Виды
DLL
Прежде
чем начать использование какой-либо процедуры или функции, находящейся
в динамической библиотеке, вам необходимо загрузить DLL в оперативную
память. Загрузка библиотеки может быть осуществлена одним из двух
способов: статическая загрузка и динамическая загрузка. Оба метода
имеют как преимущества, так и недостатки.
Статическая
загрузка
Статическая
загрузка означает, что динамическая библиотека загружается автоматически
при запуске на выполнение использующего ее приложения. Для того
чтобы использовать такой способ загрузки, вам необходимо воспользоваться
ключевым словом external при описании экспортируемой из динамической
библиотеки функции или процедуры. DLL автоматически загружается
при старте программы, и Вы сможете использовать любые экспортируемые
из нее подпрограммы точно так же, как если бы они были описаны
внутри модулей приложения. Например:
procedure
DoSomething; external 'MYLIB.DLL';
определена
процедура DoSomething из библиотеки MYLIB.DLL. При
таком определении процедур и функций библиотеки необходимо точно
указывать имя функции.
Также Вы можете вызвать процедуру по ее порядковому номеру в библиотеке,
используя директиву index. Например:
procedure
DoSomething; external 'MYLIB.DLL' index 15;
В этом
случае имя, которое вы даете процедуре при импорте не обязательно
должно совпадать с тем, которое было указано для нее в самой DLL.
Т.е. приведенная выше запись означает, что вы импортируете из
динамической библиотеки MYLIB.DLL процедуру, которая в
ней экспортировалась пятнадцатой, и при этом в рамках вашего приложения
этой процедуре дается имя DoSomeThing.
Если
вы по каким-либо причинам хотите переименовать функцию и не хотите
использовать предложенный выше вариант, то вам необходимо использовать
директиву name, которая используется вот так:
procedure
DoSomething; external 'MYLIB.DLL' name 'DoSomeThingCool';
Далее
Вы можете использовать эту процедуру так, как будто она описана
у Вас в программе, например:
begin
DoSomeThing;
end;
Вызывая
таким образом процедуры и функции вы можете указывать договоренность
вызова процедуры используя следующие директивы register,
pascal, cdecl, stdcall, и safecall.
Например,
function
MyFunction(X, Y: Real): Real; external 'MYLIB.DLL'; cdecl;
Эти
директивы определяют порядок, в котором указываются параметры
функции или процедуры. Они также влияют на удаление параметров
вызова из стека, на использование регистров процессора для передачи
параметров, и на ошибки и исключительные ситуации. По умолчанию
Delphi использует директиву register.
Следующая таблица предоставляет различия между директивами.
Директива
|
Порядок
параметров
|
Удаление
параметров из стека
|
Передавать
параметры в регистрах процессора?
|
Register
|
Слева
на право
|
Вызванная
процедура
|
Да
|
Pascal
|
Слева
на право
|
Вызванная
процедура
|
Нет
|
Cdecl
|
Справа
на лево
|
Вызывающая
процедура
|
Нет
|
Stdcall
|
Справа
на лево
|
Вызванная
процедура
|
Нет
|
Safecall
|
Справа
на лево
|
Вызванная
процедура
|
нет
|
Используемая
по умолчанию директива register наиболее эффективна, так
как она обычно предотвращает переполнение стека. Директива cdecl
полезна, когда Вы используете функции из библиотеки, написанной
на языке C или C++, в то время как stdcall
и safecall используются для вызова Windows API.
Директива pascal используется для совместимости со старыми
версиями Delphi.
Статический
способ - это наиболее легкий способ использования кода, помещенного
в DLL . Недостаток метода заключается в том, что если файл
библиотеки, на который имеется ссылка в приложении, отсутствует,
программа откажется загружаться.
Динамический
способ
Смысл
динамического метода заключается в том, что вы загружаете библиотеку
не при старте приложения, а в тот момент, когда вам это действительно
необходимо. Выгрузка библиотеки из памяти в данном случае также
осуществляется под вашим контролем. Преимущества такого способа
загрузки DLL - это уменьшение (по понятным причинам) времени
старта вашего приложения, можно определить отсутствие библиотеки
и тогда программа не будет орать благим матом о чем-то и поспешно
закрываться. А основной недостаток, как мне кажется, - это то,
что использование данного метода является более хлопотным, чем
рассмотренная выше статическая загрузка.
Сначала вам необходимо воспользоваться функцией Windows API
LoadLibrary . Функция описывается следующим образом:
function
LoadLibrary(DllFileName:PChar):HMODULE;
где
DllFileName - адрес необходимой библиотеки.
Данная
функция возвращает handle библиотеки. Если библиотека не загрузилась,
то функция возвращает NULL (что есть тоже самое что и 0).
Пример
использования функции:
Var
Handle: HMODULE;
Begin
Handle := LoadLibrary(Pchar('c:\test\my.dll')); //загружаем
If Handle = 0 then //
если библиотека не загрузилась, то зачем продолжать?
Exit;
…
end;
Для
использования функций и процедур из загруженной библиотеки, Вам
необходимо получить указатель на них, а также определить тип процедуры
или функции. Для этого должна использоваться функция GetProcAddress.
Функция описывается вот так:
function
GetProcAddress(Handle:HMODULE; NameOfFunction: string): pointer;
где
Handle это полученый благодаря функции LoadLibrary handle библиотеки,
NameOfFunction это имя функции в библиотеке.
Данная
функция возвращает указатель на функцию или процедуру в библиотеке.
Если такая процедура или функция не найдена, то функция возвращает
NULL.
Пример
использования функции из динамической библиотеки:
Type
TDllFunction = function(Num:integer;count:integer;Str:PChar):Pchar;
Var
DllFunction : TDllfunction;
Handle : HMODULE;
Res : array[1..20] of char;
…
Begin
…
DllFunction := TDllFunction(GetProcAddress(Handle,'GetChar'));
If DllFunction = null then
Exit;
Res := DllFunction(1,5,Pchar('Просто тест'));
…
end;
После
завершения использования библиотеки DLL должна быть выгружена
с применением FreeLibrary, которая имеет вид:
function
FreeLibrary(Handle:HMODULE):boolean;
где
Handle это handle библиотеки, найденный заранее функцией LoadLibrary.
При
успешном выполнении функция возвращает true, и , соответственно,
false при неудачном.
Пример
использования:
Begin
…
if not FreeLibrary(Handle) then
ShowMessage('Не могу выгрузить библиотеку');
end;
Важно!
При неправильном указании имен функций вы не получите ошибки в
течении компиляции, а только при использовании программы. Так
что следите за именами, которые указываете. Необходимо чтобы они
были идентичны именам, определенным в библиотеке.
Загрузка
картинок из библиотек.
Для
того чтобы загрузить картинку из библиотеки Вам необходимо знать
тип картинки (иконка, растровая картинка, курсор), а так же ее
уникальное имя ('BIG','APPCURSOR'). Рассмотрим функции загрузки
каждого типа картинок.
Начнем по порядку. Для того, чтобы загрузить иконку используется
функция LoadIcon, которая имеет вид:
function
LoadIcon(handle:HMODULE; IconName:PChar):HIcon;
Где
handle это handle библиотеки, полученный заранее функцией
LoadLibrary, IconName - уникальное имя иконки в
библиотеке.
Пример
использования:
var
Icon: TIcon;
begin
// создаем обьект
Icon := TIcon.Create;
try
// загружаем иконку из уже
открытой библиотеки в созданый обьект
Icon.Handle := LoadIcon(Instance,'BIGICON');
// добавляем иконку
в imagelist
if Icon.Handle <> null then
ImageList1.GetIcon(1,Icon);
finally
// уничтожаем
созданый обьект
Icon.Free;
end;
end;
Если
функция выполнилась удачно, то результат будет handle иконки,
если нет то Null.
Для
загрузки картинки используется функция LoadBitmap, которая
имеет вид:
function
LoadBitmap(handle:HMODULE;BitmapName:PChar):HBITMAP;
Где
handle это handle картинки полученный при помощи
функции LoadLibrary, BitmapName это уникальное имя
картинки в библиотеке.
Пример
использования:
var
Bitmap: TBitmap;
begin
// создаем обьект
Bitmap := TBitmap.Create;
try
// записываем
картнку из заранее открытой библиотеки
Bitmap.Handle := LoadBitmap(Instance,'ENTBITMAP');
// если запись удалась, то помещаем картинку
на форму
if Bitmap.Handle <> null
Image1.Picture.Assign(Bitmap);
finally
// освобождаем
обьект
Bitmap.Free;
end;
end;
Так
же как и LoadIcon, функция LoadBitmap имеет те же
результаты выполнения.
Для
загрузки курсоров из библиотек используется функция LoadCursor,
которая имеет вид:
function
LoadCursor(Handle:HMODULE; CursorName: PChar):HCURSOR;
Где
handle это handle картинки полученный при помощи
функции LoadLibrary, CursorName это уникальное имя
картинки в библиотеке.
Пример
использования:
const
crMyCursor
= 5;
procedure TForm1.FormCreate(Sender: TObject);
begin
// загружаем свой курсор в
палитру курсоров
Screen.Cursors[crMyCursor] := LoadCursor(HInstance,
'NewCursor');
// устанавливаем свой курсор
активным на этой форме и любуемся эффектом
Cursor := crMyCursor;
end;
Это
функция имеет те же результаты что и две предыдущие.
!Важно.
Необходимо указывать правльные имена ресурсов в библиотеках, иначе
картинки не будут грузиться.
Теперь Вы имеете полное представление о том, каким образом загрузить
какой-либо графический ресурс из динамической библиотеки.
Вот
в принципе и все о методах использования динамически загружаемых
библиотек.
Внимание!
Запрещается
перепечатка данной статьи или ее части без согласования с автором.
Если вы хотите разместить эту статью на своем сайте или издать
в печатном виде, свяжитесь с автором.
|