Trochę mi się nudziło i napisałem prosty przykład jak za pomocą hooka obsługiwać menu dodane do innej aplikacji (Autorowi pytania wysyłem na e-mail a tu piszę dla potomnych, którzy wiedzą do czego służy Szukaj)
To jest kod pprogramu który ma otrzymać komnikat o kliknięciu na dodane do programu menu.
Aby przykład się ładnie skompilował trzeba w Object Inspektorze zmienić nazwę formularza z domyslnej Form1 na frmMain, natępnie dodać button i Label, mieniając ich nazwy na btnClose i lblInfo. Następnie zapisać projeekt pod nazwą HookDemo.dpr a moduł UMain.pas. Teraz pozostaje wszystko w pliku UMain.pas zastąpić tym kodem.
Plik UMain.pas
unit UMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const
WM_MYMENUCLICK = WM_USER + 111; //wParam =0, lParam Menu ID
ID_MENU = 1000;
TEXT_MENU = 'Nowe Menu';
HookLib = 'HookLib.dll';
InstallHook = 'InstallHook';
DeinstallHook = 'DeinstallHook';
type
TInstallHook = function (hMyAppWnd, hHookWnd:THandle; MenuID: DWord): THandle;
TDeinstallHook = function (hHook: THandle): Boolean;
TfrmMain = class(TForm)
lblInfo: TLabel;
btnClose: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure btnCloseClick(Sender: TObject);
private
{ Private declarations }
hHookLib: Dword;
lpInstallHook: TInstallHook;
lpDeinstallHook: TDeinstallHook;
hHook: Dword;
public
{ Public declarations }
procedure WMMyMenuClick(var Message: TMessage); message WM_MYMENUCLICK;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.WMMyMenuClick(var Message: TMessage);
begin
MessageBox(Handle, TEXT_MENU, 'Hook Demo', MB_ICONINFORMATION or MB_SYSTEMMODAL);
end;
procedure TfrmMain.FormCreate(Sender: TObject);
var
WinPath: array [0..MAX_PATH] of Char;
NotepadPath: string;
fd: _WIN32_FIND_DATA;
hFind: Cardinal;
si: STARTUPINFO;
pi: _PROCESS_INFORMATION;
MenuItem: MENUITEMINFO;
hNotepadWnd: THandle;
hNotepadMenu: THandle;
hHelpMenu: THandle;
i: integer;
MenuCaption: array [0..10] of Char;
begin
lblInfo.Caption:= 'W menu Pomoc programu Notatnik, który powinien się'#13#10'' +
'otworzyć po uruchomieniu tego programu znjduje się'#13#10'' +
'nowa pozycja "Nowe Menu".'#13#10'' +
'Po kliknięciu na nią pojawi się komunikat.';
hHookLib:= 0;
hHook:= 0;
//Znajdź scieżkę do programu Notatnik
GetWindowsDirectory(winpath, MAX_PATH);
NotepadPath:= WinPath + '\Notepad.exe';
hFind:= FindFirstFile(PChar(NotepadPath), fd);
if hFind = INVALID_HANDLE_VALUE then
begin
NotepadPath:= WinPath + '\System32\Notepad.exe';
hFind:= FindFirstFile(PChar(NotepadPath), fd);
if hFind = INVALID_HANDLE_VALUE then
begin
MessageBox(Handle, 'Nie znaleziono programu Notanik!',
'Hook Demo', MB_ICONERROR);
exit;
end;
end;
Windows.FindClose(hFind);
//Uruchom Program Notatnik
ZeroMemory(@si, sizeof(STARTUPINFO));
si.cb:= SizeOf(STARTUPINFO);
if CreateProcess(nil, PChar(NotepadPath), nil, nil, False, NORMAL_PRIORITY_CLASS,
nil, nil, si, pi) then
begin
WaitForInputIdle(pi.hProcess, INFINITE); //Poczekaj na zaladowanie notatnika
hNotepadWnd:= FindWindow('Notepad', nil); //Znajdz okno
hNotepadMenu:= GetMenu(hNotepadWnd);
i:=0;
hHelpMenu:= GetSubMenu(hNotepadMenu, i);
//Znajdź menu Pomoc
while (hHelpMenu <> 0) do
begin
GetMenuString(hNotepadMenu, i, MenuCaption, 10, MF_BYPOSITION);
if lstrcmpi(MenuCaption, 'Pomo&c') = 0 then
break;
Inc(i);
hHelpMenu:= GetSubMenu(hNotepadMenu, i);
end;
if (hNotepadWnd = 0) or (hHelpMenu = 0) then
begin
MessageBox(Handle, 'Nie znaleziono okna lub menu programu Notanik!',
'Hook Demo', MB_ICONERROR);
exit;
end;
//Wstaw nowe menu (jako pod menu Pomoc)
MenuItem.cbSize:= SizeOf(MENUITEMINFO);
MenuItem.fMask:= MIIM_TYPE or MIIM_STATE or MIIM_ID;
MenuItem.fType:= MFT_STRING;
MenuItem.fState:= MFS_ENABLED;
MenuItem.wID:= ID_MENU;
MenuItem.hSubMenu:= 0;
MenuItem.hbmpChecked:= 0;
MenuItem.hbmpUnchecked:= 0;
MenuItem.dwItemData:= 0;
MenuItem.dwTypeData:= TEXT_MENU;
MenuItem.cch:= lstrlen(TEXT_MENU);
MenuItem.hbmpItem:= 0;
if InsertMenuItem(hHelpMenu, 0, True, MenuItem) = False then
begin
MessageBox(Handle, 'Nie udało się dodać menu!',
'Hook Demo', MB_ICONERROR);
exit;
end;
//Ładuj biblioteke DLL i pobierz adresy funkcji
hHookLib:= LoadLibrary(HookLib);
if (hHookLib <> 0) then
begin
lpInstallHook:= GetProcAddress(hHookLib, InstallHook);
lpDeinstallHook:= GetProcAddress(hHookLib, DeinstallHook);
hHook:= lpInstallHook(Handle, hNotepadWnd, ID_MENU);
if (hHook = 0) then
MessageBox(Handle, 'Wystąpł błąd podczas próby założenia hooka!',
'Hook Demo', MB_ICONERROR)
end
else
begin
MessageBox(Handle, 'Wystąpł błąd podczas próby załadowania biblioteki DLL!',
'Hook Demo', MB_ICONERROR);
end;
end
else
MessageBox(Handle, 'Nie udało się uruchomić programu Notanik!',
'Hook Demo', MB_ICONERROR);
end;
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if (hHook <> 0) then
lpDeinstallHook(hHook);
if (hHookLib <> 0) then
FreeLibrary(hHookLib);
end;
procedure TfrmMain.btnCloseClick(Sender: TObject);
begin
Close;
end;
end.
Oczywiście potrzebna jest jeszcze biblioteka DLL , więc trzeba steorzyć projekt nowej biblioteki DLL zapisać go pod nazwą HookLib.dpr a następnie zmienić kod pliku HookLib.dpr na ten:
Plik HookLib.dpr:
library HookLib;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
Windows, Messages;
const
WM_MYMENUCLICK = WM_USER + 111; //wParam =0, lParam Menu ID
type
TSharedData = record
hMyAppWnd: THandle;
hHookedWnd: THandle;
MenuID: Dword;
hHook: THandle;
end;
PSharedData = ^TSharedData;
var
hSharedData: THandle;
SharedDataPtr: PSharedData;
function GetMessageProc(nCode:DWORD;wParam:WPARAM;lParam:LPARAM): Longint; export; stdcall;
var
hFile: THandle;
DataPtr: PSharedData;
hHook: THandle;
hMyAppWnd: THandle;
MenuID: Dword;
Msg: tagMSG;
begin
hFile:= OpenFileMapping(FILE_MAP_WRITE, True, 'SharedData');
DataPtr:= MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
hHook:= DataPtr^.hHook;
hMyAppWnd:= DataPtr^.hMyAppWnd;
MenuID:= DataPtr^.MenuID;
if (nCode = PM_NOREMOVE) then
begin
CopyMemory(@Msg, Pointer(lParam), SizeOf(tagMSG));
if (Msg.message = WM_COMMAND) then
begin
if LOWORD(Msg.wParam) = MenuID then //Czy to dodane menu?
SendMessage(hMyAppWnd, WM_MYMENUCLICK, 0, LOWORD(Msg.wParam));
end;
end;
CallNextHookEx(hHook, nCode, wParam, lParam);
UnmapViewOfFile(DataPtr);
CloseHandle(hFile);
result:= 0;
end;
//hMyAppWnd - uchwy okna programu
//hHookWnd - uchwyt okna na ktore zakladamy hook (0 - hook globalny)
//MenuID - ID menu na które zakladamy hook
function InstallHook(hMyAppWnd, hHookWnd:THandle; MenuID: DWord): THandle;
var
hThread: THandle;
begin
//Instaluje Hook
result:=0;
//Pobieramy uchwyt watku, okna ktore chcemy przechwycic
hThread:= GetWindowThreadProcessId(hHookWnd, nil);
if hThread = 0 then
exit;
//Tworzymy sobie tymczasowy plik na dane
hSharedData:= CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
sizeof(TSharedData), 'SharedData');
if (hSharedData = 0) then
exit;
SharedDataPtr:=MapViewOfFile(hSharedData, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (SharedDataPtr = nil) then
begin
CloseHandle(hSharedData);
exit;
end;
SharedDataPtr^.MenuID:= MenuID;
SharedDataPtr^.hHookedWnd:= hHookWnd;
SharedDataPtr^.hMyAppWnd:= hMyAppWnd;
SharedDataPtr^.hHook:= SetWindowsHookEx(WH_GETMESSAGE, @GetMessageProc,
hInstance, hThread);
result:= SharedDataPtr^.hHook;
end;
function DeinstallHook(hHook: THandle): Boolean;
begin
//Deinstaluje Hook
UnmapViewOfFile(SharedDataPtr);
CloseHandle(hSharedData);
result:= UnhookWindowsHookEx(hHook);
end;
exports
InstallHook, DeinstallHook;
begin
end.
Aby przykład działał bllioteka DLL musi znajdować się w tymsamym katalogu co program HookDemo. Podczas uruchomiania programu powinien się także uruchomić Notatnik z Windows w którego menu Pomoc powinno się znajdować dodane menu "Nowe Menu". Po kliknięciu na nie zostanie wysłany komunikat do okna przykładowego programu (HookDemo), który go obsłuży i wyświetli MessageBox.
Przykład kompilowany na Delphi 6, sprawdzany na Windows 98 i XP
// wrzuć to do gotowców - Ł