Solved

"Shell Hooks" - care to comment ?

Posted on 1997-12-02
19
647 Views
Last Modified: 2012-08-14
Anybody willing to comment on using ShellProcs ?

i (think) i have followed the specs to the letter, but the computer doesn't like it.

i'm installing a system wide hook (yes, in a DLL) and setting the hook by

InstDLL := GetModuleHandle ('MyHook.DLL');
hProc := SetWindowsHookEx (WH_SHELL, MyHook, InstDll, 0);

i have also tried to set it within the DLL by
hProc := SetWindowsHookEx (WH_SHELL, MyHook, 0, 0);

and i am not doing much in "MyHook" proc either: i am just sending a message to the main window (i have the main handle passed to the DLL before it). it goes like this:

function MyHook(nCode: integer; wp: WPARAM; lp: LPARAM): LRESULT;stdcall;

begin
if(nCode >= 0) then
  SendMessage(Caller, wm_User+1, wp, nCode);
Result := CallNextHookEx(hook, nCode, wp, lp);
end;
[variables Caller & Hook are defined]

but the Hook function only for the process and not systemwide and often it causes a GPF in Kernel32 !

Any comments ....?
0
Comment
Question by:ahalya
  • 12
  • 7
19 Comments
 
LVL 7

Author Comment

by:ahalya
ID: 1352747
Edited text of question
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352748
Edited text of question
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352749
looks like i've to increase points (even) to get comments !

0
 
LVL 4

Expert Comment

by:d003303
ID: 1352750
Hi,
try to call CallNextHookEx AFTER you have processed your hook procedure.

Hope this helps,

Slash/d003303
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352751
Hi Slash/d003303 !

Thx for your comment.
actually i am calling the NextHookProc after sending my msg. i made a mistake in shortening the code.

Here is my hook proc.
=========================================================
begin
if(nCode <0) then
   begin
   Result := CallNextHookEx(hookshell, nCode, wp, lp);
   end
else
  begin;
  SendMessage(Caller, wm_User+1, wp, nCode);
  Result :=  CallNextHookEx(hookshell, nCode, wp, lp);
  end;
end;

0
 
LVL 7

Author Comment

by:ahalya
ID: 1352752
Edited text of question
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352753
now that i'm giving away so may points i think i'm entitled to a perfect example !
0
 
LVL 4

Expert Comment

by:d003303
ID: 1352754
Hi ahalya,

NEVER use SendMesage in a HookProc ! Use PostMessage instead, SendMessage waits until the message is processed by the receiving window. If something in your your message handler causes the shell to send out a WH_SHELL message, you are in danger to cause a deadlock or a GPF.
0
 
LVL 4

Expert Comment

by:d003303
ID: 1352755
Sorry, the above should be a comment...
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 7

Author Comment

by:ahalya
ID: 1352756
Thx for the comment. my "test" message handler just increments a count. Thx for your comment (the second one as well). i'll open this up to the other experts now.


0
 
LVL 4

Accepted Solution

by:
d003303 earned 450 total points
ID: 1352757
Yo,

maybe the error is somewhere else in your code. So I decieded to quickly (hopefuly not too dirty ;-)) write such a program, here is a working code :

1. DLL-Code

// to be hooktest.dpr
library hooktest;

uses
  Windows,
  Messages;

var HCaller,
    HHookProc : THandle;
    HookProc  : function(nCode: integer; wp: WPARAM; lp: LPARAM): LRESULT; stdcall;

function TheHookProc(nCode: integer; wp: WPARAM; lp: LPARAM): LRESULT; stdcall;
begin
  // this makes you feel safe when no message
  // seems to come through to your main app
  messagebeep(0);

  if(nCode < 0) then
   begin
     Result := CallNextHookEx(HHookProc, nCode, wp, lp);
   end
  else
   begin
     PostMessage(HCaller, wm_User+1, wp, nCode);
     Result := CallNextHookEx(HHookProc, nCode, wp, lp);
   end;
end;

procedure Init(Wnd, LibHandle, ModHandle : THandle); stdcall;
begin
  HCaller := Wnd;
  @HookProc := GetProcAddress(LibHandle, 'TheHookProc');
  if @HookProc <> nil
   then HHookProc := SetWindowsHookEx(WH_SHELL, HookProc, ModHandle, 0);
end;

procedure Exit; stdcall;
begin
  UnhookWindowsHookEx(HHookProc);
end;

exports
  Init,
  Exit,
  TheHookProc;

begin
end.

-----------------------------------------------

2. Test-Application-Code

// to be hookexe.pas
unit hookexe;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls;

type
  TForm1 = class(TForm)
    StatusBar1: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    MsgCount  : Integer;
    LibHandle,
    ModHandle : THandle;
    InitProc  : procedure(Wnd, LibHandle, ModHandle : THandle); stdcall;
    ExitProc  : procedure; stdcall;
    procedure WMUSER_1(var Message : TMessage); message WM_USER + 1;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMUSER_1(var Message : TMessage);
begin
  Inc(MsgCount);
  StatusBar1.SimpleText := Format('Got Message %d, Code %d', [MsgCount, Message.lParam]);
end;

procedure TForm1.FormCreate(Sender: TObject);
var DllName : string;
begin
  MsgCount := 0;
  // assuming the DLL is called hooktest.dll and is located in the app's directory
  DllName := ExtractFilePath(Application.ExeName) + 'hooktest.dll';
  LibHandle := LoadLibrary(PChar(DllName));
  ModHandle := GetModuleHandle('hooktest.dll');
  if LibHandle <> 0 then
   begin
     StatusBar1.SimpleText := 'Succeeded to load DLL';
     @ExitProc := GetProcAddress(LibHandle, 'Exit');
     if @ExitProc <> nil then
      begin
        StatusBar1.SimpleText := 'Succeeded to get exitproc address';
        @InitProc := GetProcAddress(LibHandle, 'Init');
        if @InitProc <> nil then
         begin
           StatusBar1.SimpleText := 'Succeeded to get initproc address';
           InitProc(Self.Handle, LibHandle, ModHandle);
         end;
      end;
   end
  else StatusBar1.SimpleText := 'Failed to load DLL';
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if LibHandle <> 0 then
   begin
     if @ExitProc <> nil
      then ExitProc;
     FreeLibrary(LibHandle);
   end;
end;

end.

-----------------------------------------------

3. Form-Code, to be hookexe.dfm

object Form1: TForm1
  Left = 200
  Top = 108
  Width = 262
  Height = 195
  Caption = 'Form1'
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object StatusBar1: TStatusBar
    Left = 0
    Top = 149
    Width = 254
    Height = 19
    Panels = <>
    SimplePanel = True
  end
end

-----------------------------------------------

4. EXE-Project-file

program hooktst;

uses
  Forms,
  hookexe in 'hookexe.pas' {Form1};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


Have fun,

Slash/d003303
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352758
Hi there,

Thx for your interest. But your code does NOT correctly work. (atleast not in my m/c). To illustrate my point drop two labels on your form and do the following in the message handler.
[actually one label is enough; i/m trying to seperate create/destroy and activate]

Labels get updated only when "HookEXE" is activated and not when activations is switched between other programs. try opeining notepad and paint and switch between them while running Hookexe.
Nothing is happens for me. [yeah, i hear those beeps too. but i don't receive any messages ! - i even tried sending messages even when ncode is < 0; didn't work]


case Message.LParam of
HSHELL_WINDOWCREATED:Label1.Caption := 'Window Created at '+ TimeToStr(Now);
HSHELL_WINDOWDESTROYED:Label1.Caption := 'Window Destroyed at ' + TimeToStr(Now);
HSHELL_WINDOWACTIVATED:Label1.Caption := 'Window Activated at ' + TimeToStr(Now);
HSHELL_TASKMAN:Label1.Caption := 'Taskman Selected';
HSHELL_ACTIVATESHELLWINDOW:Label1.Caption := 'Shell Main window Activated';
end;

case Message.LParam of
HSHELL_WINDOWCREATED   : Label2.Caption := 'Last Create Message received at ' + TimeToStr(Now) + ' with handle  : ' + IntToStr(Message.WParam);
HSHELL_WINDOWDESTROYED : Label2.Caption := 'Last Destroy message received at ' + TimeToStr(Now) + ' with handle  : ' + IntToStr(Message.WParam);
end;

Form1.update;

0
 
LVL 4

Expert Comment

by:d003303
ID: 1352759
Yo,

got the problem. Every hook function is executed in the context of the thread the hook event belongs to. For that, the DLL has to be instanciated from the system for each thread the hook is valid for. The effect is, that the global variables of the DLL are only valid for YOUR application's thread. If another thread causes the hook event, the hook procedure uses uninitialized variables.
Solution : The Caller variable is obsolete. The corresponding code line in hooktest.dpr has to be
  PostMessage(FindWindow('TMyShellHookMainWindow', nil), wm_User+1, wp, nCode);

In hookexe.pas :
Change the name of the form in object inspector to
TMyShellHookMainWindow

Now the handle is valid in any context of the hook procedure. You can change the name of the form in any name you like, just remember that the name has to be adjusted in the DLL-DPR, too.
Also, thus the form name is global to the system, use a 'unique' name.

Have fun,
Slash/d003303
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352760
Thx. this sounds very probable. i'll try it out.

but in that case, how the heck do we maintain global variables within the DLL ?  (in Delphi ?)

i heard that some linker switches do the trick in c++ stuff.
is there an equivalent in Delphi ?


0
 
LVL 4

Expert Comment

by:d003303
ID: 1352761
Global variables in DLLs are global to each instance. Sharing the global variables between instances is not that easy. It cannot be a linker switch, the OS is instanciating a module with an increment on its reference count, allocationg a new heap and giving the module the heap address. The code-segment is only loaded once. So there is no mechanism for the linker to interact.
To share variables between instances you need memory mapped files. I am currently working on a component to simplify their use.

Slash/d003303
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352762
yes, it works (almost).
i'm still having some trouble getting all the messages and the relevant info. (wnd handle & stuff).

i'll try it some more before awarding the points for you.
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352763
couple of questions:

(1). What should be the third parameter
     in a SetWindowsHookEx.

in your code var "ModHandle". (Help file calls it an instance handle, hMod ?)

{SetWindowsHookEx(WH_SHELL, HookProc, ModHandle, 0);}


(2).

in your code you are getting variables LibHandle and a ModHandle.
{form Code:
LibHandle := LoadLibrary(PChar(DllName));
ModHandle := GetModuleHandle('MyHook.DLL');}

BUT, Wouldn't LoadLibrary and GetModuleHandle will return the same value ? (i.e. LibHandle and ModHandle are always equal !)

Could you please clarify ?



0
 
LVL 4

Expert Comment

by:d003303
ID: 1352764
Yo,

you are right, LoadLibrary and GetModuleHandle return the same value. So you can omit the GetModuleHandle call and simplify the Init procedure with just 1 parameter:

procedure Init(LibHandle : THandle); stdcall;

About the  CallNextHookEx function :
If you refer to the documentation it must not be possible that CallNextHookEx is working properly, because the HHookProc parameter is zero in a process space different from the process space that set the hook. I went through some Microsoft C-samples for hooks, and in some comments it says that CallNextHook MUST be called with zero as the first parameter to be working SAFE ! Funny, isn't it ?
I think that it refers to legacy garbage. CallNextHookEx was available in Win3.1 where there was ONE large process space. It would not be nice to call a function CallNextHookExExExEx...
By the way, in their hook samples they also use FindWindow to post the messages to the main application :-)

Have fun,
Slash/d003303
0
 
LVL 7

Author Comment

by:ahalya
ID: 1352765
sorry i didn't get back with my comments all these days.

i'm really confused after reading your last comments.
CallNextHookEx with zero as the first param. Hmm...
what on earth is going on in the OS ?  

anyway, i now have almost concluded that this thing is not goinh to work well. When i have my hook on, the taskbar does weird things. Whenever i close an app task bar doesn't update itself (and all  i'm doing in my hook is a "FindWindow" and postmessage, for God's sake).

Thanks a lot for your comments and time. i really enjoyed it. btw, i do these things for "fun" and this time i haven't had much of it :-(

If you have any more comments, i'm eager to hear them. if you figure out a better way ever please remember to add a comment here !


0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now