• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 440
  • Last Modified:

Hook a Shell

I'm using SetWindowsHookEx API function with parameter WH_SHELL in a DLL.

When my application call the DLL and active the hook, the taskbar not update when launch and close programs.

Urgent, please send code.
0
jmarin
Asked:
jmarin
  • 7
  • 7
1 Solution
 
LischkeCommented:
Hi jmarin,

sorry, but I can't understand what you need. Please reword your question then I can probably help...

Ciao, Mike
0
 
jmarinAuthor Commented:
Edited text of question.
0
 
LischkeCommented:
Mmmh, let's see if I got this. I guess you mean that when you activate your hook the shell does not work any longer as before, right?

If this is the case then I assume you don't call the next hook correctly. A common mistake in this regard is to store the handle of the hook in a global variable in the unit where the hook is implemented. But since the hook code is called in the various process contexts this variable is only valid for the process which set the hook. In order to make it available to all process contexts you'd need additional work. One approach has proven to be reliable: memory mapped files.

But before I continue I wanna know if this is what your problem is...

Ciao, Mike
0
[Webinar] Improve your customer journey

A positive customer journey is important in attracting and retaining business. To improve this experience, you can use Google Maps APIs to increase checkout conversions, boost user engagement, and optimize order fulfillment. Learn how in this webinar presented by Dito.

 
jmarinAuthor Commented:
Thanks Mike for your comment.

You have code of a Hook Shell?
0
 
LischkeCommented:
jmarin,

I have code for a mouse hook which is basically the same as a shell hook, but you haven't answered my question. Is it so that the shell does not work properly after you have set the hook, yes or no? I need to know this to take the right steps for your problem!

Ciao, Mike
0
 
jmarinAuthor Commented:
The Hook not work properly after of installed.  The taskbar not work properly and deactive the Win95 System Key.  I proof Mouse Hook and work very well, but not Shell Hook.
0
 
LischkeCommented:
Mmmh, if a mouse hook works fine but not the shell hook then the problem becomes really difficult. Please post your code of the call back functions here (stripped down if it is too much).

I'm here tomorrow again...

Ciao, Mike
0
 
jmarinAuthor Commented:
This Unit is used in the creation of DLL
In dll, export two function: one for set hook active and other for set hook inactive.



unit ShellHook;

interface

Uses Windows,SysUtils,Classes,Controls;

Type
  THookMsg = Packed record
               Code   : integer;
               WParam : WPARAM;
               LParam : LPARAM;
               Result : LResult
             end;

Type
  THook = Class;
  THookMethod = procedure (var HookMsg: THookMsg) of object;
  THookNotify = procedure (Hook : THook; var Hookmsg: THookMsg) of object;

  THook = Class
  Private
    FHook               : hHook;
    FHookProc           : Pointer;
    FOnPreExecute       : THookNotify;
    FOnPostExecute      : THookNotify;
    FActive             : Boolean;
    FLoadedActive       : Boolean;

    FThreadID           : Integer;

    Procedure SetActive(NewState : Boolean);
    Procedure SetThreadID(NewID : INteger);
    Procedure HookProc(Var HookMsg : THookMsg);
  Protected
    Procedure PreExecute(Var HookMsg : THookMsg; Var Handled : Boolean); Virtual;
    Procedure PostExecute(Var HookMsg : THookMsg); Virtual;
    Function AllocateHook : hHook; Virtual; Abstract;

  Public
    Constructor Create;
    Destructor Destroy; Override;
    Property ThreadID : Integer Read fThreadID Write SetThreadID Stored False;
    Property Active : Boolean Read fActive Write SetActive;
    Property OnPreExecute : THookNotify  Read fOnPreExecute Write fOnPreExecute;
    Property OnPostExecute : THookNotify  Read fOnPostExecute Write fOnPostExecute;
  Published
  End;


Type
  TShellHook = Class(THook)
  Private
  Protected
  Public
    Function AllocateHook : hHook; Override;
  Published
    Property Active;
    Property OnPreExecute;
    Property OnPostExecute;
  End;

function  MakeHookInstance (Method: THookMethod): pointer;
procedure FreeHookInstance (ObjectInstance: pointer);

implementation

const
  InstanceCount = 313;

type
  PObjectInstance = ^TObjectInstance;
  TObjectInstance = packed record
                      Code: Byte;
                      Offset: Integer;
                    case Integer of
                      0: (Next: PObjectInstance);
                      1: (Method: THookMethod);
                    end;
Type
  PInstanceBlock = ^TInstanceBlock;
  TInstanceBlock = packed record
                     Next: PInstanceBlock;
                     Code: array[1..2] of Byte;
                     WndProcPtr: Pointer;
                     Instances: array[0..InstanceCount] of TObjectInstance;
                   end;
var
  InstBlockList : PInstanceBlock  = nil;
  InstFreeList  : PObjectInstance = nil;

function StdHookProc (Code, WParam: WPARAM; LParam: LPARAM): LResult; stdcall; assembler;

asm
  XOR     EAX,EAX
  PUSH    EAX
  PUSH    LParam
  PUSH    WParam
  PUSH    Code
  MOV     EDX,ESP
  MOV     EAX,[ECX].Longint[4]
  CALL    [ECX].Pointer
  ADD     ESP,12
  POP     EAX
end;

function CalcJmpOffset(Src, Dest: Pointer): Longint;
begin
  Result := Longint(Dest) - (Longint(Src) + 5);
end;

function MakeHookInstance(Method: THookMethod): Pointer;

const
  BlockCode: array [1..2] of Byte = ($59, $E9);
  PageSize = 4096;

var
  Block: PInstanceBlock;
  Instance: PObjectInstance;

begin
  if InstFreeList = nil then
  begin
    Block := VirtualAlloc (nil, PageSize, MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    Block^.Next := InstBlockList;
    Move(BlockCode, Block^.Code, SizeOf(BlockCode));
    Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2],@StdHookProc));
    Instance := @Block^.Instances;
    repeat
      Instance^.Code := $E8;
      Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
      Instance^.Next := InstFreeList;
      InstFreeList := Instance;
      Inc(Longint(Instance), SizeOf(TObjectInstance));
    until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
    InstBlockList := Block
  end;
  Result := InstFreeList;
  Instance := InstFreeList;
  InstFreeList := Instance^.Next;
  Instance^.Method := Method
end;

{ Free a hook method instance }
procedure FreeHookInstance (ObjectInstance: Pointer);

Begin
  if ObjectInstance <> nil then
  Begin
    PObjectInstance(ObjectInstance)^.Next := InstFreeList;
    InstFreeList := ObjectInstance
  End
End;

Constructor THook.Create;
Begin
  fHookProc := MakeHookInstance(HookProc);
  fActive := false;
  fLoadedActive := False;
  fHook := 0;
  ThreadID := GetCurrentThreadID;
End;

Destructor THook.Destroy;
Begin
  Active := False;
  FreeHookInstance(fHookProc);
  Inherited;
End;

Procedure THook.SetActive(NewState : Boolean);

Begin
  If (fActive<>NewState) Then
  Begin
    fActive := NewState;
    Case fActive Of
      True : Begin
               fHook := AllocateHook;
               If (fHook=0) Then
               Begin
                 fActive := False;
                 Raise Exception.Create(Classname+' CREATION FAILED!');
               End;
             End;
      False : Begin
                If (FHook<>0) Then UnhookWindowsHookEx(fHook);
                fHook := 0;
              End;
    End;
  End;
End;

Procedure THook.SetThreadID(NewID : INteger);

Var
  IsActive              : Boolean;

Begin
  IsActive := fActive;
  Active := False;
  fThreadID := NewID;
  Active := IsActive;
End;

Procedure THook.HookProc(Var HookMsg : THookMsg);

Var
  Handled               : Boolean;

Begin
  Handled := False;
  PreExecute(HookMsg,Handled);
  If Not Handled Then
  Begin
    with HookMsg do Result := CallNextHookEx (fHook, Code, wParam, lParam);
    PostExecute(HookMsg);
  End;
End;

Procedure THook.PreExecute(Var HookMsg : THookMsg; Var Handled : Boolean);
Begin
  If Assigned(fOnPreExecute) then
  Begin
    fOnPreExecute(Self,HookMsg);
  End;
End;

Procedure THook.PostExecute(Var HookMsg : THookMsg);

Begin
  If Assigned(fOnPostExecute) then
  Begin
    fOnPostExecute(Self,HookMsg);
  End;
End;

Function TShellHook.AllocateHook: hHook;
Begin
  Result :=SetWindowsHookEx(WH_SHELL,fHookProc,hInstance,ThreadID);
End;

end.


0
 
LischkeCommented:
You wrote some very beautiful code, but unfortunately, I don't know what it even works. I assume you never tried it on Windows NT and you get sometimes strange shell behaviour (including a shell restart etc., but this is so common in Win9x nobody ever cares about it...).

You haven't fully understood how hooks work. Forget all your appealing classes and start over development. I don't say this just for fun. I mean it serious. I have done a four week development involving hooks for Win9x and WinNT just 8 weeks ago, so I know what I'm talking about!

The main problem is that a hook DLL is mapped into the address space of the process for which it actually receives a hook message. Imagine a mouse hook. While you move the mouse over the screen various mouse messages are generated and sent to the window under the mouse. While the message frequncy might be quite high it is very likely that the process where the window with the mouse over it belongs too. So what you need is not only a generic, but also a quick solution.

But what does it mean that the DLL is mapped into the address space? It means mainly that all your memory is NOT valid despite for the process which allocated it. This also means that your TShellHook class (which is created in your main application) is NOT valid in other processes as they all have separate address spaces. This fact is the reason why to my surprise your solution seems to work somehow (at least partially). I assume it has to do with Win9x' memory managment which is not as strict as in NT.

Now, all this negative stuff needs also a solution. My suggestion is to take out classes and such from your hook implementation (there's no way to use classes in hooks, try the straight way). Create a shared area for your DLL which can be access from all processes. Under C/C++ this is done with weird linker commands to create a shared data segment. Since this way is not possible in Delphi I'd suggest memory mapped files. You'd create (each time your hook is called!) a file mapping like this:

MappingHandle := CreateFileMapping(DWORD(-1), nil, PAGE_READWRITE, 0, SizeOf(TInstInfo), 'UniqueMappingName');

and map data with this code:

MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TInstInfo));

This allocates some memory in the system's swap file and is fast enough for hooks. MapViewOfFile returns a pointer which can be used in ALL process to access some memory. This memory should contain ALL variables needed to get the hook working. This includes also the hook handle, else you will not correctly call the next hook.

Ciao, Mike
0
 
LischkeCommented:
Oops little text corrections (my two years old son is playing around me ... :-):

"While the message frequncy might be quite high it is very likely that the process where the window with the mouse over it belongs too. So what you need is not only a generic, but also a quick solution. "

should read:

"While the message frequency might be quite high, it is very likely that the process where the window with the mouse over it belongs too changes over and over again. So what you need is not only a generic, but also a quick solution. "
0
 
jmarinAuthor Commented:
Lischke, thanks you.

Win the points
0
 
LischkeCommented:
Thanks jmarin, but a C grade for my little tutorial is a bit unfair, don't you think? Anyway, better than never getting a grade (as it happens so often here at E-E).

Ciao, Mike
0
 
jmarinAuthor Commented:
I'm sorry but select grade C and forbide select grade A.  Anyway, the answer is excellent.

Thanks you

Julian
0
 
jmarinAuthor Commented:
I'm sorry but select grade C and forbide select grade A.  Anyway, the answer is excellent.

Thanks you

Julian
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 7
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now