It is currently Sat Dec 05, 2020 6:48 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 5 posts ] 
Author Message
 Post subject: Hooking the #WM_COMMAND message
PostPosted: Tue Sep 25, 2007 1:00 am 
Offline
Enthusiast
Enthusiast

Joined: Wed Jul 05, 2006 12:40 am
Posts: 155
I'm trying to create a utility to put apps in the system tray when minimized by hooking the #WM_SYSCOMMAND message with an #SC_MINIMIZE wParam. Every bit of literature I've found so far indicates that this message should be sent when the user either clicks Minimize in the system menu OR click the minimize button on the titlebar. For some reason, though, I'm not catching the minimize message unless it's selected from the system menu - it doesn't seem to fire when the titlebar button is pressed. Here's a minimal version of the code I'm using; maybe someone can put me on the right path, or at least prove to me that it's just my system that's screwing this up.
Code:
;Hook procedure
;compile this To a Shared library called "minimizehook.dll"

#WM_MYEVENT = #WM_USER + 1

ProcedureDLL MinimizeProc(code.l, wParam.l, *lParam.MSG)
  If code < 0
    ProcedureReturn CallNextHookEx_(@MinimizeProc(), code, wParam, lParam)
  EndIf
  If *lParam\message & $FFFF = #WM_SYSCOMMAND
    If (*lParam\wParam & $FFFF) = #SC_MINIMIZE
      hwnd = FindWindow_(0, "Minimize Hook Test")
      If hwnd
        SendMessage_(hwnd, #WM_MYEVENT, 0, 0)
      EndIf
    EndIf
  EndIf
  ProcedureReturn CallNextHookEx_(@MinimizeProc(), code, wParam, lParam)
EndProcedure

Code:
;Test app
;Either save this in the folder where "minimizehook.dll" was built,
;or change the path to the dll in the OpenLibrary() call, then execute.
;The hook is global, so any window that's minimized while this is
;running should case a message to be displayed in the hook test window.

#WM_MYEVENT = #WM_USER + 1

Enumeration
  #MainWindow
  #MessageText
  #MessageTimer
EndEnumeration

Global timer_start.l, hook_dll.l, hook.l

Procedure WndProc(hwnd, msg, wParam, lParam)
  result = #PB_ProcessPureBasicEvents
  Select msg
    Case #WM_MYEVENT
      SetGadgetText(#MessageText, "A window has been minimized")
      timer_start = ElapsedMilliseconds()
  EndSelect
  ProcedureReturn result
EndProcedure

hook_dll = OpenLibrary(0, "minimizehook.dll")
If Not hook_dll
  MessageRequester("Error", "Could not load minimizehook.dll")
  End
EndIf

If OpenWindow(#MainWindow, 0, 0, 320, 240, "Minimize Hook Test", #PB_Window_ScreenCentered|#PB_Window_SizeGadget|#PB_Window_MaximizeGadget|#PB_Window_MinimizeGadget)
  If CreateGadgetList(WindowID(#MainWindow))
    SetWindowCallback(@WndProc())
    SetWindowPos_(WindowID(#MainWindow), #HWND_TOPMOST, 0, 0, 0, 0, #SWP_NOMOVE|#SWP_NOSIZE)
    TextGadget(#MessageText, 10, 10, 300, 15, "")

    hook = SetWindowsHookEx_(#WH_GETMESSAGE, GetProcAddress_(hook_dll, "MinimizeProc"), hook_dll, 0)
    If Not hook
      MessageRequester("Error", "Unable to install message hook")
      CloseLibrary(0)
      End
    EndIf
     
    Repeat
      event = WindowEvent()
      If timer_start And (ElapsedMilliseconds() - timer_start) > 2000
        SetGadgetText(#MessageText, "")
        timer_start = 0
      EndIf
      Delay(1)
    Until event = #PB_Event_CloseWindow
  EndIf
EndIf

UnhookWindowsHookEx_(hook)
CloseLibrary(0)
End


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Sep 25, 2007 3:03 am 
Offline
PureBatMan Forever
PureBatMan Forever
User avatar

Joined: Tue Feb 10, 2004 3:07 am
Posts: 2307
Location: Ohio, USA
Instead of a dll/hook can you use something like this :?: If not I'll be happy to take a closer look at your code tomorrow when I have more time. ;)

Code:
#EVENT_SYSTEM_MINIMIZESTART = $16
#EVENT_SYSTEM_MINIMIZEEND = $17
#WINEVENT_SKIPOWNPROCESS = 2
#WINEVENT_OUTOFCONTEXT = 0

Procedure EventProc(hWinEventHook.l, event.l, hwnd.l, idObject.l, idChild.l, idEventThread.l, dwmsEventTime.l)
  Debug event
  Select event
    Case #EVENT_SYSTEM_MINIMIZESTART
      Debug "Minimize: " + Str(hwnd)
    Case #EVENT_SYSTEM_MINIMIZEEND
      Debug "Maximize: " + Str(hwnd)
  EndSelect
EndProcedure

If OpenWindow(0, 0, 0, 700, 500, "", #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SystemMenu) And CreateGadgetList(WindowID(0))
  hWinHook = SetWinEventHook_(#EVENT_SYSTEM_MINIMIZESTART, #EVENT_SYSTEM_MINIMIZEEND, 0, @EventProc(), 0, 0, #WINEVENT_SKIPOWNPROCESS | #WINEVENT_OUTOFCONTEXT)
  Debug hWinHook
  Repeat
    wEvent = WaitWindowEvent()
  Until wEvent = #PB_Event_CloseWindow
  If hWinHook
    UnhookWinEvent_(hWinHook)
  EndIf
EndIf
End

_________________
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Sep 25, 2007 7:56 am 
Offline
Enthusiast
Enthusiast

Joined: Wed Jul 05, 2006 12:40 am
Posts: 155
Wow, that's nice! I've been doing winapi a long time, and it never ceases to amaze me that there's always something more to learn about it. It also surprises me that I didn't land on that command in all my numerous google searches on the topic!


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Sep 25, 2007 1:03 pm 
Offline
PureBatMan Forever
PureBatMan Forever
User avatar

Joined: Tue Feb 10, 2004 3:07 am
Posts: 2307
Location: Ohio, USA
I stumbled upon that API function a while back and it has come in handy a few times for me. :)

I was able to get your DLL hook working by changing from a #WH_GETMESSAGE hook to a #WH_CBT hook.

msdn wrote:
WH_CBT Hook
The system calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the input focus; or before synchronizing with the system message queue. The value the hook procedure returns determines whether the system allows or prevents one of these operations. The WH_CBT hook is intended primarily for computer-based training (CBT) applications.

Here's the new DLL code...

Code:
;Hook procedure
;compile this To a Shared library called "minimizehook.dll"

#WM_MYEVENT = #WM_USER + 1

ProcedureDLL MinimizeProc(code.l, wParam.l, lParam)
  result = 0
  If code < 0
    ProcedureReturn CallNextHookEx_(@MinimizeProc(), code, wParam, lParam)
  EndIf
  If code = #HCBT_MINMAX
    If (lParam & $FFFF) = #SW_MINIMIZE
      hwnd = FindWindow_(0, "Minimize Hook Test")
      If hwnd
        SendMessage_(hwnd, #WM_MYEVENT, 0, 0)
        ;... Return 0 to allow operation to continue
        ;... Return 1 to prevent it from continuing
        result = 0
      EndIf
    EndIf
  EndIf
  ProcedureReturn result
EndProcedure


and then change the SetWindowsHookEx line in your test app to
Code:
hook = SetWindowsHookEx_(#WH_CBT , GetProcAddress_(hook_dll, "MinimizeProc"), hook_dll, 0) ]

_________________
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Sep 25, 2007 7:28 pm 
Offline
Enthusiast
Enthusiast

Joined: Wed Jul 05, 2006 12:40 am
Posts: 155
I appreciate your continued research on this, though your first solution has already done the trick. This is only my second attempt at api hooking, and my success the first time clearly must have been a lucky strike. This is a part of the winapi I would definitely like to get a little more comfortable with.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 14 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye