2003 (С) ВСЕ ПРАВА ЗАЩИЩЕНЫ


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;

Это функция имеет те же результаты что и две предыдущие.

!Важно. Необходимо указывать правльные имена ресурсов в библиотеках, иначе картинки не будут грузиться.
Теперь Вы имеете полное представление о том, каким образом загрузить какой-либо графический ресурс из динамической библиотеки.

Вот в принципе и все о методах использования динамически загружаемых библиотек.

Внимание! Запрещается перепечатка данной статьи или ее части без согласования с автором. Если вы хотите разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.

2003 (C) Все права защищены Корнушковым Николаем
Hosted by uCoz