Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Как можно использовать DLL

Автор: Алексей Павлов
WEB-сайт: http://delphikingdom.com
Прислал: Bramus

Стимулом к написанию данной статьи стали многочисленные вопросы в форумах, а так же постоянные нападки одной молодой особы :)

Хочу сразу оговориться и сказать, что нижеследующий текст предназначен для ещё не совсем опытных , но уже достаточно "продвинутых" программистов. Он не претендует на полноту изложения по заданной теме и на "рэпирность" определений и высказываний, используемых автором.

На данный момент существует уже большое количество источников, в которых можно почитать много интересной информации о принципах написания и работе с библиотеками, но всё же, начиная разговор о использовании динамически компонуемых библиотеках, стоит ввести некоторые базовые определения и понятия, без которых дальнейшие выкладки будут не поняты людям, не имевшими дело с dll, а тем, кто уже имеет некоторый опыт в данной области будет не лишним освежить в памяти основы.

Итак, dll - это, содержащие код, ресурсы или какие-то иные данные, программные модули. Чем нас не устраивают обычные exe-файлы, спросите вы. Отвечу,- основной смысл dll состоит в том, что ваши приложения могут загружать и обрабатывать код из библиотеки в процессе работы, а не на этапе сборки. ). Аббревиатура DLL говорит сама за себя – Dynamic Linked Library, ДИНАМИЧЕСКИ компонуемая библиотека. По структуре своей dll схожа с exe файлами, но когда ваша программа использует некую dll, другая программа тоже может использовать эту же dll, при этом в памяти будет физически загружена только одна копия используемой библиотеки, а адресное пространство, выделенное вашей программе будет содержать образ/слепок/memory-mapped_file/экземпляр загруженной библиотеки.

Не стоит забывать, что процессу, загрузившему dll, доступны функции явно предоставляемые самой dll для «внешнего мира» - т.е. exports ф-ии.

Так... теперь пару слов о способах загрузки dll. Не секрет, что существует два способа загрузки библиотек: статический и динамический (в литературе можно встретить названия - явный и неявный, но я предпочитаю придерживаться борландовской терминологии). Статический используется, как правило тогда, когда в вашей библиотеке небольшое количество ф-ий и процедур, которые вы наверняка собираетесь использовать в вашей программе. Если же ваша библиотека содержит большое количество процедур/функций или процедуры, вызываемые вами, используются вашей программой не часто (к примеру, имеет место сложная обработка графического изображения), то в данном случае целесообразнее использовать динамическую загрузку, дабы не загромождать память. К минусам статической загрузки можно отнести тот факт, что если при попытке вашей программы загрузить dll эта библиотека не будет найдена - вы получите ошибку, а программа просто-напросто не запустится. Если вы используете динамическую загрузку, то программа запустится в любом случае, но в момент, когда вы попробуете использовать функцию из отсутствующей dll, возникнет исключение, которое можно программно обработать и продолжить выполнение программы.

Статическая загрузка

...
implementation

function ShowMyDialog(Msg: PChar): Boolean; stdcall; external 'project1.dll';

procedure SetValue(); cdecl; external 'some.dll';
...

Тут, кажется не должно быть никаких неясностей - указываем название ф-ии или процедуры, параметры, возвращаемый тип (для ф-ии), способ передачи аргументов: stdcall - используется при вызовах WinAPI ф-ий (передача параметров справа на лево), cdecl - при использовании dll, написанных на C++ ; есть ещё некоторые соглашения о вызовах о которых предлагаю вам почитать в справочной системе Delphi. Важно помнить, что если при разработке dll использовалось соглашение stdcall, то и при её вызове должно использоваться тоже stdcall ! Директива external указывает на местоположение библиотеки из которой вы хотите экспортировать ф-ию.

Динамическая загрузка:

...
uses
  ...
type
  { определяем процедурный тип, отражающий экспортируемую процедуру или ф-ию }
  TMyProcType = procedure(flag: Boolean); stdcall;
  TMyFuncType = function(Msg: PChar): Boolean; cdecl;
  { эту операцию можно сделать непосредственно в разделе var процедуры,
  в которой вы будите загружать dll }
  ...

  procedure TForm1.Button1Click(Sender: TObject);
var
  SetValue: TMyProcType; { объявляем переменные процедурного типа}
  ShowDialog: TMyFuncType;

  // или можно было сделать так (в таком случае type... не нужен):
  // SetValue: procedure (flag : Boolean); stdcall;
  // ShowDialog: function (Msg: PChar): Boolean; cdecl;

  Handle01, Handle02: HWND; { дескрипторы, загружаемых библиотек }

begin
  Handle01 := LoadLibrary(PChar('project1.dll')); { загрузка dll }
  try
    @ShowDialog := GetProcAddress(Handle01, 'ShowMyDialog');
      { получаем указатель на необходимую процедуру}
    ShowDialog(PChar('Dll function is working !!!')); { используем ф-ию }
  except
    ShowMessage('Ошибка при попытке использовать dll !');
  finally
    FreeLibrary(Handle01); { выгружаем dll }
  end; { try }
end;

end.

Аналогично и с процедурой SetValue();

Из всего вышесказанного необходимо понять, что при статической загрузке экспортируемая вами dll находится в памяти с момента запуска прогарммы вплоть до Application.Terminate и вам, как программисту, не надо следить за процессом освобождения памяти из под вашей dll; при динамической загрузке вы сами в любом месте программы загружаете dll, но и сами же должны следить за её выгрузкой. Если при динамическом способе загрузки вы самостоятельно, с помощью функции FreeLibrary, не выгрузите dll из памяти, то она будет находиться в адресном постранстве процесса, загруженного из вашей программы вплоть до его, процесса, уничтожения, но в момент завершения работы программы память из под библиотеки будет освобождена, т.к. сама dll может находиться только в адресном пространстве загрузившего её процесса, но никогда сама по себе !

Итак, считаем, что с ликбезом покончено. Для чего нужны и как устроены динамически компануемые библиотеки можно прочитать у Тайксейры - там вся теория дана в лучшем виде. Совсем неопытным в написании библиотек, а так же начинающим программистам - настоятельно рекомендую ознакомиться с общими принципами создания dll, описанными в статье Использование и создание DLL в Delphi. Статьи Королевства по теме. Для тех кто предпочитает C++ рекомендую прочитать статью Криса Касперски. После ознакомления - обязательно возвращайтесь ;)

Ну а теперь, пожалуй, приступим к самой теме данной статьи, а конкретнее к примерам применения и разъяснению этих примеров в меру авторской компетенции ;)

Содержание:

  • Регистрация dll в системе ( или как бы нам, воспользовавшись особенностями приложения Explorer.exe, заставить его загрузить нашу библиотеку в его адресное пространство).
  • Размещение модальных форм в dll.
  • Размещение "готовых" файловых образов библиотек в EXE-файле с последующим их извлечением и использованием.
  • Как в своих dll использовать процедуры и ф-ии, находящиеся в других библиотеках.
  • Удаление программы "во время исполнения".
  • Ловушки в dll.

Регистрация dll в системе

( или как бы нам, воспользовавшись особенностями приложения Explorer.exe, заставить его загрузить нашу библиотеку в его адресное пространство)

Часто возникают вопросы такого типа: "Как мне зарегистрировать свою dll в системе ? Как загрузить свою dll при загрузке компьютера ?"

Для регистрации вашей dll в системе, необходимо:

  • в HKEY_CLASSES_ROOT\CLSID задать новый идентификатор класса (GUID).
  • в HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad создать раздел InProcServer32 и в значение этого раздела записать путь к вашей dll, которая должна грузиться при загрузке Windows.
  • создать строковый параметр MyDllLoad со значением, равным определённому нами GUID.

Вот собственно и всё - теперь explorer, при каждом входе в систему, будет грузить вашу библиотеку ( ВНИМАНИЕ !!! при таком способе регистрации загруженная dll не будет выгружаться из памяти до тех пор, пока процесс Explorer’а будет существовать хотя бы в одном экземпляре !)

Реализация данного способа имеет следующий вид:

unit Unit1;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    registry;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  reg: Tregistry;

begin
  reg := Tregistry.create;
  reg.rootkey := HKEY_CLASSES_ROOT;

  {Теперь настала пора создать идентификатор для нашей библиотеки.
   Идентификатор класса - это  глобальный уникальный идентификатор,
   состоящий из шеснадцатиричных цифр. В него входят метка времени создания
   и адрес платы сетевого интерфейса, попросту говоря MAC, или фиктивный адрес
   платы при отсутствии оной в вашем компьютере 8) Получить этот идентификатор
   можно двумя путями:
   1). в Delphi, в любом месте редактора кода нажать Ctrl-Shift-G .
   2). или воспользоваться API функцией CoCreateGUID();}

  try
    reg.openkey('CLSID\{69502F20-E8CD-11D5-A784-0050BF44BD3B}\InProcServer32',
      true);
    reg.writestring('', 'C:\TEMP\MyDll.dll');
    reg.closekey;
    reg.rootkey := HKEY_LOCAL_MACHINE;
    reg.openkey('Software\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad', true);
    reg.writestring('MyDllLoade', '{69502F20-E8CD-11D5-A784-0050BF44BD3B}');
    reg.closekey;

  finally
    reg.free;
  end; {try}

end;

end.

Размещение модальных форм в dll

Этот вопрос не представляется сколь-нибудь интересным и поэтому я буду предельно краток. Делаете New-> DLL, затем New-> Form, накидываем туда всё что душе пожелает и поехали:

library ModelF;

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls,
  Unit1 in 'Unit1.pas' {Form1};

function ShowMyDialog(Msg: PChar): Boolean; stdcall;
begin
  {Создаем экземпляр Form1 формы TForm1}
  Form1 := TForm1.Create(Application);
  {В Label1 выводим сообщение Msg}
  Form1.Label1.Caption := StrPas(Msg);
  {Возвращаем True если нажата OK (ModalResult = mrOk)}
  Result := (Form1.ShowModal = mrOk);
  Form1.Free;
end;

exports ShowMyDialog;

begin
end.

И сам проект, содержащий вызывающий DLL код:
unit main2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

function ShowMyDialog(Msg: PChar): Boolean; stdcall; external 'project1.dll';
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if ShowMyDialog(PChar('Work !!!')) = TRUE then
    ShowMessage('TRUE !')
  else
    ShowMessage('FALSE !');
end;

end.

Размещение "готовых" файловых образов библиотек в EXE-файле с последующим их извлечением и использованием.

Представим такую ситуацию: ваша программа использует несколько dll-ок, не вами написанных, и у вас нет их исходного кода (т.е. непосредственно код процедур и функций, вами используемых в своей программе, вам не доступен), а вам ну просто "противопоказано" показывать эти библиотеке пользователю (мало ли - забудет он их переписать вместе с программой, у dll файлов как правило атрибуты невидимости стоят; или ещё какая-нибудь причина на то имеется - мало ли ;) В таком случае предлагается сделать следующее: поместить все используемые библиотеки непосредственно в код программы, как это делается с ресурсами, а затем, при запуске программы записывать их куда-либо на диск, использовать и, к примеру, если в этом есть необходимость - стирать при окончании работы программы. Вы можете сказать, что в таком случае сам exe файл увеличится в размерах и что можно использовать ф-ии библиотек, размещённых в exe файле без переноса их на диск. На это можно ответить только одно - всё это конечно так, но ведь цель данной статьи наметить подходы к решению проблем, которые могут возникнуть при решении тех или иных задач, а отнюдь не дать готовые рецепты для какого-то конкретного случая... Итак приступим:

1). открываем самый мощный текстовый редактор - Блокнот и начинаем ваять :

MYDLL RCDATA 
mydll.dll 

Записываем всё это как Lib.rc

2). Теперь для получения файла-ресурсов компилируем получившийся у нас Lib.rc :

brcc32.exe Lib.rc 

3). Получили Lib.res, который необходимо прикрепить к нашему проекту, для этого используем директиву {$R Lib.res}

Нижеследующий код иллюстрирует как можно прикрепить *.res файл к проекту и извлечь его при необходимости:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}{$R Lib.RES}

procedure TForm1.Button1Click(Sender: TObject);
var
  MyDll1: TResourceStream;
begin
  MyDll1 := TResourceStream.Create(hInstance, 'MYDLL', RT_RCDATA);
  try
    MyDll1.SaveToFile('duck.dll');
  finally
    MyDll1.Free;
  end; {try}

end;

end.

В результате сборки получим EXE-файл. При нажатии на кнопку формы будет создан файл duck.dll в той же директории, из которой была запущена программа (помните, что если dll-файлы в вашей системе имеют атрибут скрытых,- созданный duck.dll тоже будет невидимым).

Как в своих dll использовать процедуры и ф-ии,
находящиеся в других библиотеках

Не так давно один многообещающий ребёнок задал мне такой вопрос - "что можно предпринять, если моя программа статически грузит большое количество библиотек, и эти библиотеки тоже используют ф-ии, экспортируемые из других dll-ок, при этом мне не хотелось бы каждый раз прописывать их в главном модуле своей программы, загромождая код."

Для ответа на этот вопрос необходимо помнить, что связь между вызываемой ф-ией и её исполняемым кодом устанавливается во время выполнения приложения путём использования ссылки на конкретную ф-ию dll и при этом не важно, будут ли эти ссылки объявлены в самом приложении или в каком-то внешнем модуле. К примеру, посмотрите как выглядит файл windows.pas,- ближе к концу файла вы можете увидеть каким образом вызываются различные ф-ии из библиотеки advapi32.dll .

Итак, создаём новый unit и объявляем импортируемые ф-ии и процедуры и определяем все необходимые типы для ваших dll. К примеру, если у вас есть три библиотеки: 001.dll, 002.dll, 003.dll и в них находятся n-ое кол-во ф-ий и процедур, которые вы намереваетесь использовать в своей программе, то создаём новый unit и... :

unit gather_it;

interface

function MyFunc001(i: integer): PChar;
procedure MyProc002();
function MyFunc003(): Integer;
// ..........................
procedure Something();

implementation

function MyFunc001; external '001.dll';
procedure MyProc002; external '002.dll';
function MyFunc003; external '003.dll';
// ..........................
procedure Something; external 'some_other.dll';

end.

Как видно из этого примера - в этом unit-е нигде нет реализаций самих ф-ий, а лишь указания на то где эти ф-ии находятся. Для использования данного модуля просто прописываем его название (unit gather_it) в раздел uses того модуля в котором предполагается использовать описанные в unit gather_it ф-ии и процедуры.

Удаление программы "во время исполнения"

В название этого раздела вынесен вопрос, который ну если не каждую неделю задаётся в любом форуме, посвящённом программированию, то по крайней мере очень часто. Бывают моменты в жизни каждого программиста, когда надо сделать что-то быстро, не привлекая особого внимания, не оставляя явных следов и не травмируя нежную психику пользователя :) Я не буду говорить про ring0 и про реализацию всего этого на asm-е,- нет, мы пойдём другим путём... 8)

К примеру нам надо добиться того, что бы наша программа при запуске без всяких проволочек стирала себя физически с винта, при этом продолжая выполнять какой-то код. В лоб это сделать не получится, но что мешает исполнить необходимый нам код в контексте какого либо постороннего потока ? В таком случае запущенный нами перед закрытием нашего процесса (в нашем случае процесса, загруженного из нашего exe-файла) код сможет удалить нужный нам файл, потому что данный код будет выполняться в адресном пространстве постороннего потока.

Может объяснение не очень удачное, но посмотрев на реализацию всего этого всё станет предельно ясно.

Вот алгоритм того, о чём я так долго распылялся: (это один из способов, наверняка не самый элегантный, но я не отступаю от темы статьи и упорно продолжаю совать эти грешные dll куда ни поподя :)

Извлечь из нашего exe модуля заранее помещённую туда dll (это мы уже умеем) и записать эту dll в любое место диска.

Загрузить dll в адресное пространство НЕнашего потока и выполнить процедуру из этой библиотеки, которая сотрёт исходный файл.

Сделать это можно, используя rundll32.exe примерно следующим образом:
- для начала напишем небольшую библиотеку:

// ************************ DLL ************************
library test1;

uses
  Windows;

procedure test(); stdcall;
begin
  //Sleep(5); {можно задерживать выполнение кода при необходимости 8}
  DeleteFile(PChar('D:\Project1.exe'));
  {
  ВНИМАНИЕ, до тех пор, пока выполняющийся процесс, загруженный из Project1.exe,
  не выполнит команду FreeLibrary(собственный экземпляр) в Win9x/Me либо UnmapViewOfFile в
  NT/W2k, вы будете получать ошибку. Связанно это с тем, что эти вызовы ни в том ни в другом виде не
  вставляются автоматически компилятором Делфи в эпилог приложения.
  Вместо этого после выполнения эпилогом приложения вызова ExitProcess() ОС автоматически разорвет связь
  между процессом и файлом, вызвав ту или иную из вышеуказанных API-ф-ций.
  }
  FreeLibrary(GetModuleHandle(nil));
end;

exports test;

begin
  // MessageBox(Handle, 'Library loaded !', 'Yeh... !!!', 0);
end.
// ************************ DLL ************************

ну а та часть exe файла, которая отвечает за загрузку dll может выглядеть так:

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(0, nil, 'rundll32.exe', ' test1.dll,test', nil, SW_ShowNormal);
  Close;
end;
...

Заметьте, что в нашей программе мы нигде саму библиотеку не загружаем. rundll32.exe запускается следующим образом: rundll32.exe имя_dll, имя_процедуры, параметры. Таким образом в процедуру test мы можем передавать, к примеру, путь к файлу, который нужно стереть (не забывайте, что String не применим в случае если не используется юнит ShareMem, и надо использовать PChar). Из примера видно, что при запуске Project1.exe из корневого каталога диска D:\ и нажатии на Button1, rundll32 загрузит библиотеку test.dll (если она тоже лежит в корне диска D:\), выполнит процедуру test, которая удалит Project1.exe и освободит память. Кстати, загружаемую dll не видит ни TaskManager ни WinSight32 (это конечно не значит, что таким образом можно скрыть dll от NtQuerySystemInformation в NT или CreateToolhelp32Snapshot в Win9x !). Если общий подход ясен и у вас есть воображение, то можно добиться интересных результатов ;)

Теперь поговорим о так называемых ловушках (hooks). Тех, кто вообще не имеет представления о понятии hook-а в Windows-кой интерпретации, я опять же отсылаю к научно-популярной литературе, потому что подробно объяснять механизм работы hook-ов я не буду (т.к. эта тема выходит за рамки данной статьи), но из ниже приведённого кода и комментариев к нему, думаю, будет многое понятно для тех, кто знаком с термином hook, но кому не приходилось самому их программировать. От слов к делу. Сначала напишем основную часть кода - dll, в котором будем устанавливать и снимать hook-и, а так же обрабатывать полученные сообщения:

library hook_dll;

uses Windows, Messages;

var
  SysHook: HHook = 0;
  Wnd: Hwnd = 0;

  { данная ф-ия вызывается системой каждый раз, когда возникает какое-то событие в
  dialog box-е, message box-е, menu, или scroll bar-е}

function SysMsgProc(code: integer; wParam: word; lParam: longint): longint;
  stdcall;
begin
  { Передаём сообщение дальше по цепочке hook-ов. }
  CallNextHookEx(SysHook, Code, wParam, lParam);
  { флаг code определяет тип произошедшего события. }
  if code = HC_ACTION then
  begin
    { В wnd кладу дескриптор того окна, которое сгенерировало сообщение.}
    Wnd := TMsg(Pointer(lParam)^).hwnd;
    { Проверяю, нажата ли правая кнопка мыши}
    if TMsg(Pointer(lParam)^).message = WM_RBUTTONDOWN then
    begin
      { Раскрываю окно на всю клиентскую область.}
      ShowWindow(wnd, SW_MAXIMIZE);
      { Вывожу сообщение.}
      MessageBox(0, 'HOOK is working !', 'Message', 0);
    end;
  end;
end;

{ Процедура установки HOOK-а}

procedure hook(switch: Boolean) export; stdcall;
begin
  if switch = true then
  begin
    { Устанавливаю HOOK, если он не установлен (switch=true). }
    SysHook := SetWindowsHookEx(WH_GETMESSAGE, @SysMsgProc, HInstance, 0);
    { тут: WH_GETMESSAGE - тип hook-а ; @SysMsgProc - адрес процедуры обработки ;
      HInstance - указывает на DLL, содержащую процедуру обработки hook-а; последний
      параметр указывает на thread, с которым ассоциирована процедура обработки hook-а;
    }
    MessageBox(0, 'HOOK установлен !', 'Сообщение из DLL', 0);
  end
  else
  begin
    { Снимаю HOOK, если он установлен (switch=false). }
    UnhookWindowsHookEx(SysHook);
    MessageBox(0, 'HOOK снят !', 'Сообщение из DLL', 0);
    SysHook := 0;
  end;
end;

exports hook;

begin
  //MessageBox(0, 'MESSAGE FROM DLL  - loaded !', 'Message', 0);
end.

Так... с этим покончили; теперь код модуля, откуда будем вызывать нашу процедуру hook (для разнообразия буду использовать "динамическую" загрузку dll):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

{для динамической загрузки dll}
type
  MyProcType = procedure(flag: Boolean); stdcall;
  {*****************************}

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Hdll: HWND; { дескриптор загружаемой dll-ки (для динамической загрузки)}

implementation
{ раскоментируйте эту строку для статической загрузки }
//procedure hook(state: boolean); stdcall; external 'hook_dll.dll';

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  hook: MyProcType; {для динамической загрузки}

begin
  { раскоментируйте эту строку для статической загрузки }
  //hook(true);

  { ********* динамическая загрузка **************}
  Hdll := LoadLibrary(PChar('hook_dll.dll')); { загрузка dll }
  if Hdll > HINSTANCE_ERROR then { если всё без ошибок, то }
    @hook := GetProcAddress(Hdll, 'hook')
      { получаем указатель на необходимую процедуру}
  else
    ShowMessage('Ошибка при загрузке dll !');
  { **********************************************}

  hook(true);

  Button2.Enabled := True;
  Button1.Enabled := False;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  hook: MyProcType; {для динамической загрузки}

begin
  { раскоментируйте эту строку для статической загрузки }
  //hook(false);

  { ********* динамическая загрузка **************}
  hook := GetProcAddress(Hdll, 'hook');
    { получаем указатель на необходимую процедуру}
  { **********************************************}
  hook(false); { обращение к процедуре }

  Button1.Enabled := True;
  Button2.Enabled := False;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeLibrary(Hdll); { при закрытии формы - выгружаем dll }
end;

end.

Как видно из примера - после установки hook-а, при нажатии правой кнопки мыши на элементах dialog box-а, message box-а и menu, то окно (окно в Windows понимании ;), над которым был произведён клик - займёт всю клиентскую область.

Кстати говоря, почти все клавиатурные шпионы работают примерно по тому же принципу - устанавливают hook на события клавиатуры, и пишут себе в файлик на диске всё, что пользователь набирает. Но надо понимать, что hook может быть поставлен не только из dll, но и из обычного приложения.

На этом, пожалуй, и остановимся... Все предложения, пожелания, нарекания и т.д. присылайте мне, т.е. автору :)

Все примеры опробованы и работают под Win98 (на NT не пробовал).

Алексей Павлов
The adviser: - Digitman
Special thanks to: - Fellomena

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay