Windows
Взаимодействие процессов
Архитектура 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. |
Страница 5 из 42