Window hooking?

Just starting out? Need help? Post your questions and find answers here.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Window hooking?

Post by PB »

Anyone know how to perform window hooking so I know when a third-party
window is opening? I'd like to make the Windows Calculator always open in
the center of the Desktop, but not move it after it's opened.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Heis Spiter
User
User
Posts: 41
Joined: Fri Aug 22, 2003 7:10 pm
Location: 76 (FRANCE)
Contact:

Post by Heis Spiter »

PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Window hooking?

Post by PB »

Thanks Heis, but I'm still confused over this. :(

If you look at http://tinyurl.com/5dzq7 there's an example of how to position
a MessageBox before it's displayed (click the "Listing 1" link in the article);
however I've not been successful in converting it to PureBasic. Anyone able
to offer any advice? Thanks.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Heis Spiter
User
User
Posts: 41
Joined: Fri Aug 22, 2003 7:10 pm
Location: 76 (FRANCE)
Contact:

Post by Heis Spiter »

I'll try to convert Listing 1. But, I'm not sure I'll managed to... :?
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

This code should get you started:

Hook dll:

Code: Select all

; This is the hook procedure. It must be compiled to a dll.
; Every process on which this hook is installed will load
; this dll and execute the procedure.
;
; Even though you also load this dll from the program that installes
; the hook, you have to keep in mind that this code is mostly excecuted
; inside a foreign process, not the one of your program.
;
; For this example, all the code does is catch a #WM_CREATE message
; and broadcast our own message, so the original program can read
; it. Usually, whatever task needs to be done can be done right here
; inside the procedure, so that extra message is not needed.
;
Global CreateMessage.l

ProcedureDLL AttachProcess(Instance)
  CreateMessage = RegisterWindowMessage_("MyHook_Window_Created")
EndProcedure

; This is the hook procedure
; The parameters depend on what type of hook you install.
; This is a hook which is called before the window procedure of the
; process is called.
;
ProcedureDLL MyHookProcedure(nCode.l, wParam.l, *lParam.CWPSTRUCT)

  ; Just catch this one message.
  If *lParam\message = #WM_CREATE
    PostMessage_(#HWND_BROADCAST, CreateMessage, *lParam\hwnd, 0)
  EndIf

  ; This is very important to allow multiple hooks to work side by side.
  ProcedureReturn CallNextHookEx_(@MyHookProcedure(), nCode, wParam, *lParam)
EndProcedure
Program:

Code: Select all

; This is the program that installes the hook.
; Note: it also needs to load the hook dll, even if it itself is not hooked.

; little gui stuff
Enumeration
  #GADGET_List
  #GADGET_Start
  #GADGET_Stop
EndEnumeration

#Hook_Path = "b:\test.dll" ; the hook dll
#LIBRARY_Hook = 0          ; PB id for our dll.

If OpenWindow(0, 0, 0, 400, 400, #PB_Window_SystemMenu|#PB_Window_Screencentered, "Hook example")
  If CreateGadgetList(WindowID())
    ListViewGadget(#GADGET_List, 5, 5, 390, 365)
    ButtonGadget(#GADGET_Start, 5, 375, 120, 20, "Install Hook")
    ButtonGadget(#GADGET_Stop, 130, 375, 120, 20, "Remove Hook")    
  EndIf
  
  DisableGadget(#GADGET_Stop, 1)
  CreateMessage = RegisterWindowMessage_("MyHook_Window_Created")
  
  Repeat
    Event = WaitWindowEvent()
    
    ; here we received our create message..
    ;
    If Event = CreateMessage
      hwnd = EventwParam()
      Name$ = Space(500)
      GetWindowText_(hwnd, @Name$, 500)
      AddGadgetItem(#GADGET_List, -1, "WM_CREATE: "+RSet(Hex(hwnd), 8, "0")+" -> "+Name$)
      SetGadgetState(#GADGET_List, CountGadgetItems(#GADGET_List)-1)
    
    ElseIf Event = #PB_EventCloseWindow
      Quit = 1
    
    ElseIf Event = #PB_EventGadget
      Select EventGadgetID()
      
        Case #GADGET_Start
        
          ; This code installes the hook.        
          If OpenLibrary(#LIBRARY_Hook, #Hook_Path)
            dllInstance = LibraryID(#LIBRARY_Hook)
            dllFunction = IsFunction(#LIBRARY_Hook, "MyHookProcedure")
            
            ; If you set the last parameter to a threadid, only this thread will get
            ; hooked. Like this, every thread is hooked.
            HookID = SetWindowsHookEx_(#WH_CALLWNDPROC, dllFunction, dllInstance, #NULL)
            If HookID
              DisableGadget(#GADGET_Start, 1)
              DisableGadget(#GADGET_Stop, 0)
              AddGadgetItem(#GADGET_List, -1, "=====> Hook installed")
              SetGadgetState(#GADGET_List, CountGadgetItems(#GADGET_List)-1)
            EndIf
          EndIf
        
        Case #GADGET_Stop
        
          ; This removes the hook.. quite simple.
          If HookID
            UnhookWindowsHookEx_(HookID)
            CloseLibrary(#LIBRARY_Hook)
            DisableGadget(#GADGET_Start, 0)
            DisableGadget(#GADGET_Stop, 1)
            AddGadgetItem(#GADGET_List, -1, "=====> Hook removed")
            SetGadgetState(#GADGET_List, CountGadgetItems(#GADGET_List)-1)
            HookID = 0
          EndIf
      
      EndSelect
    
    EndIf
  Until Quit
  
EndIf

; unhook if not allready done.
If HookID
  UnhookWindowsHookEx_(HookID)
  CloseLibrary(#LIBRARY_Hook)
EndIf
End
For your calculator problem, this should work well, because #WM_CREATE
is send after the window is created, but before it is displayed.
So if you move it from inside the hook procedure, it should work, and also
look good :)
quidquid Latine dictum sit altum videtur
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

Thanks Freak, I'll have a play later on... but I thought it would be a lot simpler
than using a DLL? I'd prefer a standalone exe solution, like that Visual Basic
example uses... if VB can do it, surely PureBasic can? Here's what I've been
testing, but it fails and even crashes my PC sometimes:

Code: Select all

; WARNING: Running this *might* crash your PC!

Procedure WindowHook(uMsg,wParam,lParam)
  If uMsg=#HCBT_ACTIVATE
    AddGadgetItem(0,-1,Str(wParam)) ; Show window handle that just opened?
    UnhookWindowsHookEx_(hHook)
  EndIf
  ProcedureReturn #FALSE
EndProcedure

If OpenWindow(1,300,250,400,200,#PB_Window_SystemMenu,"test")
  CreateGadgetList(WindowID())
  ListViewGadget(0,0,0,400,200)
  hHook=SetWindowsHookEx_(#WH_CBT,@WindowHook(),GetModuleHandle_(0),GetCurrentThreadId_())
  Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
Last edited by PB on Sun Jan 30, 2005 8:45 pm, edited 1 time in total.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Well, according to the documentation, you need to have the code located
in a dll if you want the hook to be system wide or on a thread which is not
your own.

And since you want it to work on the calculator, it won't help to hook your
own process.

btw, in your example, you should set the 3rd parameter to 0 instead of
GetModuleHandle_(0). This should only be a instance handle if the code IS
inside a dll, otheweise it should be 0.
quidquid Latine dictum sit altum videtur
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> according to the documentation, you need to have the code located
> in a dll if you want the hook to be system wide

Ah, I see. No wonder you used a DLL then! :) I'll play with your code then.
Many thanks for your help on this. Hopefully I can get it doing what I want.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

Hi Freak... Unfortunately I can't get it doing what I want. I've been watching
for the opening of calc.exe but by the time the DLL sees it, it's already drawn.
Do you have any suggestions?
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Hum, it seems to work fine here.
Did you put the code to move the window in the dll or the main app?

If you put it in the app, the window is of course allready drawn by the time
that the communication from dll to app is done.
But you don't need to do it from the app, this was only for my example,
you should do it from the dll.

Here's the code:

Code: Select all

ProcedureDLL MyHookProcedure(nCode.l, wParam.l, *lParam.CWPSTRUCT)

  ; Just catch this one message.
  If *lParam\message = #WM_CREATE
  
    ; only check toplevel windows
    If GetWindowLong_(*lParam\hwnd, #GWL_STYLE) & #WS_CHILD = 0
    
      Name$ = Space(1000)
      
      ; since this dll is executed by the hooked programm, this call will
      ; return the calculator executable name
      ;
      GetModuleFileName_(0, @Name$, 1000)
      If LCase(GetFilePart(Name$)) = "calc.exe"
       
        ; get the window size and move it. 
        GetWindowRect_(*lParam\hwnd, @Size.RECT)
        w = Size\right - Size\left
        h = Size\bottom - Size\top
        x = (GetSystemMetrics_(#SM_CXSCREEN)-w)/2
        y = (GetSystemMetrics_(#SM_CYSCREEN)-h)/2
        MoveWindow_(*lParam\hwnd, x, y, w, h, #False)
        
      EndIf
    EndIf    
  EndIf

  ; This is very important to allow multiple hooks to work side by side.
  ProcedureReturn CallNextHookEx_(@MyHookProcedure(), nCode, wParam, *lParam)
EndProcedure
Compile it to a dll and use the above app code to set/remove the hook.
Works fine here.
quidquid Latine dictum sit altum videtur
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> Did you put the code to move the window in the dll or the main app?

The main app. :oops: Thanks for your new code, I'll study it well. :)
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Lane
New User
New User
Posts: 5
Joined: Thu Sep 11, 2003 6:17 pm

Enumeration??

Post by Lane »

Enumeration does not appear to be a valid command.

It's not listed in the online command index either.

I'm running 3.10 - Ebay special
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Enumeration??

Post by PB »

> Enumeration does not appear to be a valid command

It was introduced in v3.80: http://www.purebasic.com/news36.php3

> It's not listed in the online command index either

See: http://www.purebasic.com/documentation/ ... tions.html

I'm running 3.10 - Ebay special

There's your problem. :) E-mail support@purebasic.com and talk to them.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
Lane
New User
New User
Posts: 5
Joined: Thu Sep 11, 2003 6:17 pm

correct sir!

Post by Lane »

Ok thanks.
Julien
User
User
Posts: 17
Joined: Fri Apr 25, 2003 6:08 pm
Location: France

Post by Julien »

How to change this code for Work with KEYBOARD ?

Thank
Post Reply