123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- unit TB2Hook;
- // Workaround for bug in C++Builder XE2 that makes threadvar's in this unit
- // overwrite each other when optimization is enabled
- {$O-}
- {
- Toolbar2000
- Copyright (C) 1998-2005 by Jordan Russell
- All rights reserved.
- The contents of this file are subject to the "Toolbar2000 License"; you may
- not use or distribute this file except in compliance with the
- "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in
- TB2k-LICENSE.txt or at:
- https://jrsoftware.org/files/tb2k/TB2k-LICENSE.txt
- Alternatively, the contents of this file may be used under the terms of the
- GNU General Public License (the "GPL"), in which case the provisions of the
- GPL are applicable instead of those in the "Toolbar2000 License". A copy of
- the GPL may be found in GPL-LICENSE.txt or at:
- https://jrsoftware.org/files/tb2k/GPL-LICENSE.txt
- If you wish to allow use of your version of this file only under the terms of
- the GPL and not to allow others to use your version of this file under the
- "Toolbar2000 License", indicate your decision by deleting the provisions
- above and replace them with the notice and other provisions required by the
- GPL. If you do not delete the provisions above, a recipient may use your
- version of this file under either the "Toolbar2000 License" or the GPL.
- $jrsoftware: tb2k/Source/TB2Hook.pas,v 1.15 2005/06/26 18:21:33 jr Exp $
- }
- interface
- uses
- Windows;
- type
- THookProcCode = (hpSendActivate, hpSendActivateApp, hpSendWindowPosChanged,
- hpPreDestroy, hpGetMessage);
- THookProcCodes = set of THookProcCode;
- THookProc = procedure(Code: THookProcCode; Wnd: HWND; WParam: WPARAM; LParam: LPARAM);
- procedure InstallHookProc(AUser: TObject; AProc: THookProc; ACodes: THookProcCodes);
- procedure UninstallHookProc(AUser: TObject; AProc: THookProc);
- implementation
- uses
- SysUtils, Classes, Messages;
- type
- THookType = (htCallWndProc, htCBT, htGetMessage);
- THookTypes = set of THookType;
- PHookUserData = ^THookUserData;
- THookUserData = record
- Prev: PHookUserData;
- User: TObject;
- InstalledHookTypes: THookTypes;
- end;
- PHookProcData = ^THookProcData;
- THookProcData = record
- Proc: THookProc;
- Codes: THookProcCodes;
- LastUserData: PHookUserData;
- end;
- threadvar
- HookHandles: array[THookType] of HHOOK;
- HookProcList: TList;
- HookCounts: array[THookType] of Longint;
- function CallWndProcHook(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT;
- stdcall;
- type
- THookProcCodeMsgs = hpSendActivate..hpSendWindowPosChanged;
- const
- MsgMap: array[THookProcCodeMsgs] of UINT =
- (WM_ACTIVATE, WM_ACTIVATEAPP, WM_WINDOWPOSCHANGED);
- var
- J: THookProcCodeMsgs;
- I: Integer;
- begin
- if Assigned(HookProcList) and (Code = HC_ACTION) then
- with PCWPStruct(LParam)^ do begin
- for J := Low(J) to High(J) do
- if Message = MsgMap[J] then begin
- for I := 0 to HookProcList.Count-1 do
- try
- with PHookProcData(HookProcList.List[I])^ do
- if J in Codes then
- Proc(J, hwnd, WParam, LParam);
- except
- end;
- Break;
- end;
- end;
- Result := CallNextHookEx(HookHandles[htCallWndProc], Code, WParam, LParam);
- end;
- function CBTHook(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT;
- stdcall;
- var
- I: Integer;
- begin
- if Assigned(HookProcList) and (Code = HCBT_DESTROYWND) then
- for I := 0 to HookProcList.Count-1 do
- try
- with PHookProcData(HookProcList.List[I])^ do
- if hpPreDestroy in Codes then
- Proc(hpPreDestroy, HWND(WParam), 0, 0);
- except
- end;
- Result := CallNextHookEx(HookHandles[htCBT], Code, WParam, LParam);
- end;
- function GetMessageHook(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT;
- stdcall;
- var
- I: Integer;
- begin
- if Assigned(HookProcList) and (Code = HC_ACTION) then
- for I := 0 to HookProcList.Count-1 do
- try
- with PHookProcData(HookProcList.List[I])^ do
- if hpGetMessage in Codes then
- Proc(hpGetMessage, 0, WParam, LParam);
- except
- end;
- Result := CallNextHookEx(HookHandles[htGetMessage], Code, WParam, LParam);
- end;
- function HookCodesToTypes(Codes: THookProcCodes): THookTypes;
- const
- HookCodeToType: array[THookProcCode] of THookType =
- (htCallWndProc, htCallWndProc, htCallWndProc, htCBT, htGetMessage);
- var
- J: THookProcCode;
- begin
- Result := [];
- for J := Low(J) to High(J) do
- if J in Codes then
- Include(Result, HookCodeToType[J]);
- end;
- const
- HookProcs: array[THookType] of TFNHookProc =
- (CallWndProcHook, CBTHook, GetMessageHook);
- HookIDs: array[THookType] of Integer =
- (WH_CALLWNDPROC, WH_CBT, WH_GETMESSAGE);
- procedure InstallHooks(ATypes: THookTypes; var InstalledTypes: THookTypes);
- var
- T: THookType;
- begin
- { Don't increment reference counts for hook types that were already
- installed previously }
- ATypes := ATypes - InstalledTypes;
- { Increment reference counts first. This should never raise an exception. }
- for T := Low(T) to High(T) do
- if T in ATypes then begin
- Inc(HookCounts[T]);
- Include(InstalledTypes, T);
- end;
- { Then install the hooks }
- for T := Low(T) to High(T) do
- if T in InstalledTypes then begin
- if HookHandles[T] = 0 then begin
- { On Windows NT platforms, SetWindowsHookExW is used to work around an
- apparent bug in Windows NT/2000/XP: if an 'ANSI' WH_GETMESSAGE hook
- is called *before* a 'wide' WH_GETMESSAGE hook, then WM_*CHAR
- messages passed to the 'wide' hook use ANSI character codes.
- This is needed for compatibility with the combination of Tnt Unicode
- Controls and Keyman. See "Widechar's and tb2k" thread on the
- newsgroup from 2003-09-23 for more information. }
- HookHandles[T] := SetWindowsHookExW(HookIDs[T], HookProcs[T],
- 0, GetCurrentThreadId)
- end;
- end;
- end;
- procedure UninstallHooks(const ATypes: THookTypes; const Force: Boolean);
- var
- T: THookType;
- begin
- { Decrement reference counts first. This should never raise an exception. }
- if not Force then
- for T := Low(T) to High(T) do
- if T in ATypes then
- Dec(HookCounts[T]);
- { Then uninstall the hooks }
- for T := Low(T) to High(T) do
- if T in ATypes then begin
- if (Force or (HookCounts[T] = 0)) and (HookHandles[T] <> 0) then begin
- UnhookWindowsHookEx(HookHandles[T]);
- HookHandles[T] := 0;
- end;
- end;
- end;
- procedure InstallHookProc(AUser: TObject; AProc: THookProc; ACodes: THookProcCodes);
- var
- Found: Boolean;
- I: Integer;
- UserData: PHookUserData;
- ProcData: PHookProcData;
- label 1;
- begin
- if HookProcList = nil then
- begin
- HookProcList := TList.Create;
- end;
- Found := False;
- UserData := nil; { avoid warning }
- for I := 0 to HookProcList.Count-1 do begin
- ProcData := PHookProcData(HookProcList[I]);
- if @ProcData.Proc = @AProc then begin
- UserData := ProcData.LastUserData;
- while Assigned(UserData) do begin
- if UserData.User = AUser then begin
- { InstallHookProc was already called for AUser/AProc. Go ahead and
- call InstallHooks again just in case the hooks weren't successfully
- installed last time. }
- goto 1;
- end;
- UserData := UserData.Prev;
- end;
- New(UserData);
- UserData.Prev := ProcData.LastUserData;
- UserData.User := AUser;
- UserData.InstalledHookTypes := [];
- ProcData.LastUserData := UserData;
- Found := True;
- Break;
- end;
- end;
- if not Found then begin
- New(UserData);
- try
- UserData.Prev := nil;
- UserData.User := AUser;
- UserData.InstalledHookTypes := [];
- HookProcList.Expand;
- New(ProcData);
- except
- Dispose(UserData);
- raise;
- end;
- ProcData.Proc := AProc;
- ProcData.Codes := ACodes;
- ProcData.LastUserData := UserData;
- HookProcList.Add(ProcData);
- end;
- 1:InstallHooks(HookCodesToTypes(ACodes), UserData.InstalledHookTypes);
- end;
- procedure UninstallHookProc(AUser: TObject; AProc: THookProc);
- var
- I: Integer;
- ProcData: PHookProcData;
- NextUserData, UserData: PHookUserData;
- T: THookTypes;
- begin
- if HookProcList = nil then Exit;
- for I := 0 to HookProcList.Count-1 do begin
- ProcData := PHookProcData(HookProcList[I]);
- if @ProcData.Proc = @AProc then begin
- { Locate the UserData record }
- NextUserData := nil;
- UserData := ProcData.LastUserData;
- while Assigned(UserData) and (UserData.User <> AUser) do begin
- NextUserData := UserData;
- UserData := UserData.Prev;
- end;
- if UserData = nil then
- Exit;
- { Remove record from linked list }
- if NextUserData = nil then begin
- { It's the last item in the list }
- if UserData.Prev = nil then begin
- { It's the only item in the list, so destroy the ProcData record }
- HookProcList.Delete(I);
- Dispose(ProcData);
- end
- else
- ProcData.LastUserData := UserData.Prev;
- end
- else
- NextUserData.Prev := UserData.Prev;
- T := UserData.InstalledHookTypes;
- Dispose(UserData);
- UninstallHooks(T, False);
- Break;
- end;
- end;
- if HookProcList.Count = 0 then
- FreeAndNil(HookProcList);
- end;
- initialization
- finalization
- UninstallHooks([Low(THookType)..High(THookType)], True);
- end.
|