Solved

"Shell Hooks" - care to comment ?

Posted on 1997-12-02
19
674 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
[Live Webinar] The Cloud Skills Gap

As Cloud technologies come of age, business leaders grapple with the impact it has on their team's skills and the gap associated with the use of a cloud platform.

Join experts from 451 Research and Concerto Cloud Services on July 27th where we will examine fact and fiction.

 
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
 
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

Ready to get started with anonymous questions?

It's easy! Check out this step-by-step guide for asking an anonymous question on Experts Exchange.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
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…
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.
Sometimes it takes a new vantage point, apart from our everyday security practices, to truly see our Active Directory (AD) vulnerabilities. We get used to implementing the same techniques and checking the same areas for a breach. This pattern can re…
Suggested Courses
Course of the Month6 days, 5 hours left to enroll

626 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