Kyle Foster
asked on
Another SetWindowsHookEx problem. Setting ThreadID to 0 in Delphi 7 but getting an Application Hook instead
I have a Windows Hook DLL where I am trying to capture LButtonDown throughout the system to close one or more of a PopupMenu that I have created myself. It is descended from TComponent so it has nothing to do with the Windows common popupmenu control, but it must close itself if the user clicks anywhere else on the screen.
I am only getting the WM_LBUTTONDOWN message when I click on my app so it appears that I am not getting a system wide hook. It is behaving like an Application Hook.
Can someone look at the syntax because I've went over it 100 times and I am probably missing something stupid.
-------------------------- ---
here are the HookMouse,UnHookMouse and HookProc functions from the DLL,
you can ignore the parts about the FormDebug (that is working) and the ComponentHandles (there may be more than once instance of the component so I keep a list. That is also working)
---------------
function HookProc(nCode: Integer; MsgID: WParam; Data: LParam): LResult; stdcall;
var
ndx : Integer;
aHandle : Integer;
begin
Result := CallNextHookEx(Hook,nCode, MsgID,Data );
try
if MsgID=WM_LBUTTONDOWN then begin
if FormLog<>nil then
formLog.mmoLOG.Lines.Add(' Left Mouse Down Received');
if ComponentHandles<>nil then
for ndx := 0 to ComponentHandles.Count-1 do begin
aHandle := StrToIntDef(ComponentHandl es[ndx],0) ;
if (aHandle<>0) then
PostMessage(aHandle, WM_CloseIt, 0, 0);
end;
end;
except
end;
end;
procedure HookMouse(TheComponentHand le:HWnd;De bug:Boolea n); stdcall;
begin
if ComponentHandles=nil then
ComponentHandles := TStringlist.Create;
if (FormLog=nil) and Debug then begin
FormLog := TFormLog.Create(nil);
FormLog.Show;
end;
ComponentHandles.Add(IntTo Str(TheCom ponentHand le));
if Hook = 0 then
Hook:=SetWindowsHookEx(WH_ MOUSE,Hook Proc,HInst ance,0);
end;
procedure UnHookMouse(TheComponentHa ndle:HWnd) ; stdcall;
var
ndx : Integer;
begin
ndx := ComponentHandles.IndexOf(I ntToStr(Th eComponent Handle));
if ndx>=0 then
ComponentHandles.Delete(nd x);
if ComponentHandles.count=0 then begin
ComponentHandles.Free;
ComponentHandles := nil;
UnhookWindowsHookEx(Hook);
Hook:=0;
FormLog.Close;
FormLog.Free;
FormLog := nil;
end;
end;
I am only getting the WM_LBUTTONDOWN message when I click on my app so it appears that I am not getting a system wide hook. It is behaving like an Application Hook.
Can someone look at the syntax because I've went over it 100 times and I am probably missing something stupid.
--------------------------
here are the HookMouse,UnHookMouse and HookProc functions from the DLL,
you can ignore the parts about the FormDebug (that is working) and the ComponentHandles (there may be more than once instance of the component so I keep a list. That is also working)
---------------
function HookProc(nCode: Integer; MsgID: WParam; Data: LParam): LResult; stdcall;
var
ndx : Integer;
aHandle : Integer;
begin
Result := CallNextHookEx(Hook,nCode,
try
if MsgID=WM_LBUTTONDOWN then begin
if FormLog<>nil then
formLog.mmoLOG.Lines.Add('
if ComponentHandles<>nil then
for ndx := 0 to ComponentHandles.Count-1 do begin
aHandle := StrToIntDef(ComponentHandl
if (aHandle<>0) then
PostMessage(aHandle, WM_CloseIt, 0, 0);
end;
end;
except
end;
end;
procedure HookMouse(TheComponentHand
begin
if ComponentHandles=nil then
ComponentHandles := TStringlist.Create;
if (FormLog=nil) and Debug then begin
FormLog := TFormLog.Create(nil);
FormLog.Show;
end;
ComponentHandles.Add(IntTo
if Hook = 0 then
Hook:=SetWindowsHookEx(WH_
end;
procedure UnHookMouse(TheComponentHa
var
ndx : Integer;
begin
ndx := ComponentHandles.IndexOf(I
if ndx>=0 then
ComponentHandles.Delete(nd
if ComponentHandles.count=0 then begin
ComponentHandles.Free;
ComponentHandles := nil;
UnhookWindowsHookEx(Hook);
Hook:=0;
FormLog.Close;
FormLog.Free;
FormLog := nil;
end;
end;
here is some code for a Mouse Hook Library, it has two exported functions, StartHook and StopHook,. . .
By the way - - Your "ComponentHandles" StringList may Not work to store window handles be cause it is NOT a shared memory, if you need to have an array of Handles, I would think you will need to do it in a Memory Mapped file, so all the DLLs can read it, , I wonder why you use stdcall in your HookMouse procedure ?
library MouseHk;
uses
Messages, Windows, SysUtils;
{$R *.RES}
const
memFileName = 'eJ7Lc+2Yk';
var
Hooked: Boolean = False;
hMouseHook, hMemFile: THandle;
pFormHnd: PDWORD = nil;
function HkFunc(Code, wParam: Integer; var MouseStrut: TMOUSEHOOKSTRUCT): Integer; stdcall;
begin
Result:=CallNextHookEx(0, Code, wParam, Integer(@MouseStrut));
if pFormHnd = nil then Exit;
if (Code = HC_ACTION)then
if (wParam = WM_LBUTTONDOWN) or (WM_NCLBUTTONDOWN = wParam) then
PostMessage(pFormHnd^, WM_CLOSE, 0, 0);
end;
function StartHook(hForm: THandle): Integer;
begin
Result := 0;
if Hooked then
begin
Result := 1;
Exit;
end;
if not IsWindow(hForm) then
begin
Result := 2;
Exit;
end;
hMouseHook := SetWindowsHookEx(WH_MOUSE, @HkFunc, hInstance, 0);
if hMouseHook = 0 then
Result := 3 else
begin
hMemFile := CreateFileMapping(MAXDWORD , nil, PAGE_READWRITE,0,
SizeOf(THandle), memFileName);
pFormHnd := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
if pFormHnd = nil then
begin
Result := 4;
UnhookWindowsHookEx(hMouse Hook);
Exit;
end else
pFormHnd^ := hForm;
Hooked := True;
end;
end;
function StopHook: Boolean;
begin
if Hooked and (hMouseHook <> 0) then
Result := UnhookWindowsHookEx(hMouse Hook) else
Result := True;
Hooked := False;
if pFormHnd <> nil then
begin
UnmapViewOfFile(pFormHnd);
pFormHnd := nil;
end;
CloseHandle(hMemFile);
end;
procedure EntryProc(dwReason : DWORD);
begin
if (dwReason = Dll_Process_Detach) then
begin
if hMouseHook <> 0 then
UnhookWindowsHookEx(hMouse Hook);
if pFormHnd <> nil then
UnmapViewOfFile(pFormHnd);
CloseHandle(hMemFile);
end;
end;
exports
StartHook,
StopHook;
begin
DLLProc := @EntryProc;
hMemFile := OpenFileMapping(FILE_MAP_W RITE, False, memFileName);
if hMemFile <> 0 then
pFormHnd := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
end.
By the way - - Your "ComponentHandles" StringList may Not work to store window handles be cause it is NOT a shared memory, if you need to have an array of Handles, I would think you will need to do it in a Memory Mapped file, so all the DLLs can read it, , I wonder why you use stdcall in your HookMouse procedure ?
library MouseHk;
uses
Messages, Windows, SysUtils;
{$R *.RES}
const
memFileName = 'eJ7Lc+2Yk';
var
Hooked: Boolean = False;
hMouseHook, hMemFile: THandle;
pFormHnd: PDWORD = nil;
function HkFunc(Code, wParam: Integer; var MouseStrut: TMOUSEHOOKSTRUCT): Integer; stdcall;
begin
Result:=CallNextHookEx(0, Code, wParam, Integer(@MouseStrut));
if pFormHnd = nil then Exit;
if (Code = HC_ACTION)then
if (wParam = WM_LBUTTONDOWN) or (WM_NCLBUTTONDOWN = wParam) then
PostMessage(pFormHnd^, WM_CLOSE, 0, 0);
end;
function StartHook(hForm: THandle): Integer;
begin
Result := 0;
if Hooked then
begin
Result := 1;
Exit;
end;
if not IsWindow(hForm) then
begin
Result := 2;
Exit;
end;
hMouseHook := SetWindowsHookEx(WH_MOUSE,
if hMouseHook = 0 then
Result := 3 else
begin
hMemFile := CreateFileMapping(MAXDWORD
SizeOf(THandle), memFileName);
pFormHnd := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
if pFormHnd = nil then
begin
Result := 4;
UnhookWindowsHookEx(hMouse
Exit;
end else
pFormHnd^ := hForm;
Hooked := True;
end;
end;
function StopHook: Boolean;
begin
if Hooked and (hMouseHook <> 0) then
Result := UnhookWindowsHookEx(hMouse
Result := True;
Hooked := False;
if pFormHnd <> nil then
begin
UnmapViewOfFile(pFormHnd);
pFormHnd := nil;
end;
CloseHandle(hMemFile);
end;
procedure EntryProc(dwReason : DWORD);
begin
if (dwReason = Dll_Process_Detach) then
begin
if hMouseHook <> 0 then
UnhookWindowsHookEx(hMouse
if pFormHnd <> nil then
UnmapViewOfFile(pFormHnd);
CloseHandle(hMemFile);
end;
end;
exports
StartHook,
StopHook;
begin
DLLProc := @EntryProc;
hMemFile := OpenFileMapping(FILE_MAP_W
if hMemFile <> 0 then
pFormHnd := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
end.
code in Form to use DLL -
procedure TForm1.but_MouseHkClick(Se nder: TObject);
var
hLibb: THandle;
StartHook: function(hForm: THandle): Integer;
begin
hLibb := LoadLibrary('MouseHk.dll') ;
if hLibb = 0 then
begin
Showmessage('ERROR - Library did NOT Load');
Exit;
end;
@StartHook := GetProcAddress(hLibb, 'StartHook');
if @StartHook = nil then
begin
ShowMessage('StartHook was not located');
FreeLibrary(hLibb);
Exit;
end;
if StartHook(Handle) <> 0 then
begin
Showmessage('ERROR - StartHook Failed');
end;
end;
procedure TForm1.but_MouseHkClick(Se
var
hLibb: THandle;
StartHook: function(hForm: THandle): Integer;
begin
hLibb := LoadLibrary('MouseHk.dll')
if hLibb = 0 then
begin
Showmessage('ERROR - Library did NOT Load');
Exit;
end;
@StartHook := GetProcAddress(hLibb, 'StartHook');
if @StartHook = nil then
begin
ShowMessage('StartHook was not located');
FreeLibrary(hLibb);
Exit;
end;
if StartHook(Handle) <> 0 then
begin
Showmessage('ERROR - StartHook Failed');
end;
end;
ASKER
Here are my thoughts. My Application will have multiple TMyPopups. All of these instances will talk to the same dll. If there is another application that is using the dll, you are correct in saying that there will be another copy of the dll mapped into the processes memory segment. But it will have it's own hook specific to that application so the dll should work fine.
Even if there may be a better way to do the popup menu scenario. I still should be able to make the hook dll work. There's nothing too it and I've done it before I just couldn't find the code. My Hook Dll and loadlibrary code look the same as yours.
Also, the only reason I use STDCALL for my StartHook,StopHook procs are in case I wish to use the dll from another language besides Delphi. Kinda Old School.
The problem once again is that I am getting an Application hook. There is only one app calling the dll to set the hook. My SetWindowsHookEx is the same as everyone elses as far as I can tell.
To keep it simple. I set the windows hook to capture system wide Left mouse down events and when a WM_LMOUSEDOWN is captured I write to a log forms memo control.
If I click in my app I get the log message, if I click anywhere else on the form I get nothing.
Should I be using the JOURNAL option instead of the WH_MOUSE?
Even if there may be a better way to do the popup menu scenario. I still should be able to make the hook dll work. There's nothing too it and I've done it before I just couldn't find the code. My Hook Dll and loadlibrary code look the same as yours.
Also, the only reason I use STDCALL for my StartHook,StopHook procs are in case I wish to use the dll from another language besides Delphi. Kinda Old School.
The problem once again is that I am getting an Application hook. There is only one app calling the dll to set the hook. My SetWindowsHookEx is the same as everyone elses as far as I can tell.
To keep it simple. I set the windows hook to capture system wide Left mouse down events and when a WM_LMOUSEDOWN is captured I write to a log forms memo control.
If I click in my app I get the log message, if I click anywhere else on the form I get nothing.
Should I be using the JOURNAL option instead of the WH_MOUSE?
ASKER
Well, I just looked at the link you posted above, and it sparked some memories of a custom hint box that a couples of us implemented in a product for win95 that did the same thing.
It appears that I have degressed with time. I will play with that, because quite frankly, I don't like the thought of distributing a mousehook dll to multiple versions of windows and having to field to calls.
Thanks, I'll let you now how it goes.
It appears that I have degressed with time. I will play with that, because quite frankly, I don't like the thought of distributing a mousehook dll to multiple versions of windows and having to field to calls.
Thanks, I'll let you now how it goes.
ASKER
Im still upset that I can't get the windowshook to give me system wide messages.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
EVERY Process. I did not know this, but it makes everything completely understandable to me know. Thanks.
But, due to your comment that stoked my memory. I build a function into the popup form to determine is the mouse is over the form.
I added a timer to the TComponent Descendent to make sure that the mouse is over at least 1 of the popups. If it leaves and does not return for 500 milliseconds. then I post my user defined message to the foms to close themselves.
Thanks
But, due to your comment that stoked my memory. I build a function into the popup form to determine is the mouse is over the form.
I added a timer to the TComponent Descendent to make sure that the mouse is over at least 1 of the popups. If it leaves and does not return for 500 milliseconds. then I post my user defined message to the foms to close themselves.
Thanks
https://www.experts-exchange.com/questions/21718165/PopupPanel-component.html
I have plenty of examples for doing Key Hooks, the reason your code does not work is because this DLL is loaded into most of the other processes and in the other processes the HookMouse procedure is NOT called, so in those DLL loads the TheComponentHandle is never loaded into the variable, , you will need Map File memory container to have this TheComponentHandle availible to all instances of the DLL