| 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:    http://www.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:    http://www.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 $}interfaceuses  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);implementationuses  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;initializationfinalization  UninstallHooks([Low(THookType)..High(THookType)], True);end.
 |