TrayIcon GetForegroundWindow Problem |
Post Reply |
Author | |
jpbro
Senior Member Joined: 12 January 2007 Status: Offline Points: 1355 |
Post Options
Thanks(0)
Posted: 19 January 2008 at 6:12pm |
Hi,
I'll describe the behaviour I'm trying to achieve: 1) User clicks TrayIcon and my form is the foreground window -> MinimizeToTray(Me.hWnd) 2) User clicks TrayIcon and my form is *not* the foreground window -> SetForegroundWindow(Me.hWnd) 3) User clicks TrayIcon and my form is minimized to tray -> MaximizeFromTray(Me.hWnd) However, when using GetForegroundWindow in the TrayIcon Click event, the hWnd for my form is never returned (I believe the taskbar hwnd always gets returned). Any body have any idea on a good way to go about this? Thanks in advance. |
|
JKDev
Groupie Joined: 27 September 2007 Location: Ireland Status: Offline Points: 59 |
Post Options
Thanks(0)
|
This works:
|
|
jpbro
Senior Member Joined: 12 January 2007 Status: Offline Points: 1355 |
Post Options
Thanks(0)
|
Hi JKDev, thanks for the reply.
The code you provided does bring the window to the front, the problem I am having though is that I can't figure out a way to determine if my window (or its thread) is the foreground window in the TrayIcon Click event because the Shell_TrayWnd class becomes the foreground window *before* the Click event fires. It seems that no matter what window appears to be the foreground window (i.e. an instance of Notepad, or my project), then GetForegroundWindow always returns the hwnd for the Shell_TrayWnd class. This makes it impossible (as far as I have found so far) to perform the actions I require in the Click event (Minimize if foreground, Maximize if minimized to tray, Bring to front if not foreground and not minimized to tray). Any ideas? Thanks again. |
|
JKDev
Groupie Joined: 27 September 2007 Location: Ireland Status: Offline Points: 59 |
Post Options
Thanks(0)
|
have you tried getting the window in the mouse down or up event instead of the click event?...
|
|
jpbro
Senior Member Joined: 12 January 2007 Status: Offline Points: 1355 |
Post Options
Thanks(0)
|
I have, unfortunately the events are out of order so the Shell_TrayWnd class grabs the focus before any events fire. Most controls fire events in the MouseDown, MouseUp, Click order, but the TrayIcon control fires Click, MouseDown, MouseUp.
I have just about found a solution though - the second part of the code you sent has a task list example. What I am doing now is checking the task list on click to see if my app is the first in the list, and if so, assuming it is the foreground window. Seems to be working so far...I'll test it some more, clean up the code and then post it here if it is working. Thanks. |
|
jpbro
Senior Member Joined: 12 January 2007 Status: Offline Points: 1355 |
Post Options
Thanks(0)
|
The following method should work in *most* cases. The IsForegroundTaskWindow call may incorrectly return false in the case where a legitimate task window with no caption is actually in front of the passed window, but hopefully this is a rare occurence?
In a module: Option Explicit ' Required Win32 API Declarations Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long Private Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long Private Declare Function IsWindowVisible Lib "user32" (ByVal hWnd As Long) As Long Private Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long Private Declare Function GetForegroundWindow Lib "user32" () As Long Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long Private Declare Function IsIconic Lib "user32" (ByVal hWnd As Long) As Long Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long Private Declare Function AttachThreadInput Lib "user32" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long ' API Constants Private Const GWL_HWNDPARENT As Long = (-8) ' Constant used to determine window owner Private Const SW_SHOW = 5 ' Bring to front Private Const SW_RESTORE = 9 ' Restore from minimized Private mlngForegroundTaskHwnd As Long Public Function IsForegroundTaskWindow(hWnd As Long) As Long '--------------------------------------------------------------------------------------- ' Procedure : IsForegroundTaskWindow ' Created : January 21, 2008 ' Author : Jason Peter Brown (jbrown@statslog.com) ' Purpose : To determine whether our window is the foreground window during a ' TrayIcon Click event ' Notes : This is necessary because Shell_TrayWnd class steals the focus before ' we can test if our window is the foreground window ' Returns : True if passed hWnd is (most likely) the foreground window ' False if not ' Issues : May be incorrect if the actual foreground task window has no caption '--------------------------------------------------------------------------------------- Call EnumWindows(AddressOf EnumWindowsProc, hWnd) ' Determine the hwnd of the foreground task IsForegroundTaskWindow = (mlngForegroundTaskHwnd = hWnd) ' If the results from EnumWindowsProc matches the passed hwnd, we can assume we are the foreground task mlngForegroundTaskHwnd = 0 ' Reset the Foreground Task Hwnd for the next call End Function Private Function EnumWindowsProc(ByVal hWnd As Long, ByVal lParam As Long) As Long '--------------------------------------------------------------------------------------- ' Procedure : EnumWindowsProc ' Created : January 21, 2008 ' Author : Jason Peter Brown (jbrown@statslog.com), adapted from code by ' Karl E. Peterson (http://vb.mvps.org/articles/ap199902.pdf) ' Purpose : To discover the hWnd of the foreground task window (window with a caption) ' Returns : 1 to continue searching windows in the task list ' 0 to stop searching windows ' Issues : Will be incorrect if the actual foreground task window has no caption '--------------------------------------------------------------------------------------- Dim strWindowText As String ' Window Caption Dim r As Long ' API return value Dim lngContinue As Long: lngContinue = 1 ' 1 = search for next window, 0 = stop searching Dim lngThread1 As Long ' For comparing the hWnd thread to the lParam thread Dim lngThread2 As Long ' If they are the same, the we can assume our window ' is the foreground task window ' ' Make sure we meet visibility requirements. ' If IsWindowVisible(hWnd) Then ' ' It shouldn't have any parent window, either. ' If GetParent(hWnd) = 0 Then ' ' And, finally, it shouldn't have an owner. ' If GetWindowLong(hWnd, GWL_HWNDPARENT) = 0 Then ' ' Retrieve windowtext (caption) ' We do this, because we are looking for the first ' window with a caption. This *should* be the topmost task window strWindowText = Space$(256) r = GetWindowText(hWnd, strWindowText, Len(strWindowText)) If r Then ' There was a window caption, so this is (hopefully) a task window ' This will cause problems if the actual foreground task window ' does not have a caption...Ideas? ' Get the ThreadIDs of the hWnd and lParam, to see if they are the same lngThread1 = GetWindowThreadProcessId(hWnd, ByVal 0&) lngThread2 = GetWindowThreadProcessId(lParam, ByVal 0&) If lngThread1 = lngThread2 Then ' The threads are the same, so our window is the foreground window mlngForegroundTaskHwnd = lParam Else ' The threads are different, so the foreground window is NOT our window mlngForegroundTaskHwnd = hWnd End If lngContinue = 0 ' If we have gone this far, we should stop searching ' because we are either the foreground task window or not ' at this point End If End If End If End If EnumWindowsProc = lngContinue ' Return 1 to keep looking for a window, otherwise 0 if we are done End Function Public Function ForceForegroundWindow(ByVal hWnd As Long) As Boolean '--------------------------------------------------------------------------------------- ' Procedure : ForceForegroundWindow ' Created : January 21, 2008 ' Author : Karl E. Peterson (http://vb.mvps.org/articles/ap199902.pdf) with minor ' style modifications by Jason Peter Brown (jbrown@statslog.com) ' Purpose : To push a window to the foreground under all known circumstances ' Returns : True on success, False on failure '--------------------------------------------------------------------------------------- Dim lngThread1 As Long Dim lngThread2 As Long Dim r As Long Dim strClass As String ' ' Nothing to do if already in foreground. ' If hWnd = GetForegroundWindow() Then ForceForegroundWindow = True Else ' VB4/32, VB5, VB6 Push Your Way to the Front ' First need to get the thread responsible for ' the foreground window, then the thread running ' the passed window. ' lngThread1 = GetWindowThreadProcessId(GetForegroundWindow, ByVal 0&) lngThread2 = GetWindowThreadProcessId(hWnd, ByVal 0&) ' ' By sharing input state, threads share their ' concept of the active window. ' If lngThread1 <> lngThread2 Then Call AttachThreadInput(lngThread1, lngThread2, True) r = SetForegroundWindow(hWnd) Call AttachThreadInput(lngThread1, lngThread2, False) Else r = SetForegroundWindow(hWnd) End If ' ' Restore and repaint ' If IsIconic(hWnd) Then Call ShowWindow(hWnd, SW_RESTORE) Else Call ShowWindow(hWnd, SW_SHOW) End If ' ' SetForegroundWindow return accurately reflects ' success. ForceForegroundWindow = CBool(r) End If End Function Then, in your form: Private Sub TrayIcon1_Click() On Error Resume Next If mblnMinimized Then ' We are minimized to the tray Me.TrayIcon1.MaximizeFromTray Me.hWnd ' Restore Me.WindowState = vbMaximized ' Maximize mblnMinimized = False ' Clear the minimized flag Else ' We are *not* minimized to the tray If IsForegroundTaskWindow(Me.hWnd) Then ' Our window is most likely the foreground task window, Me.TrayIcon1.MinimizeToTray Me.hWnd ' so minimize it to the tray mblnMinimized = True ' Flag that we are indeed minimized to the tray Else ' Our window is most likely not the foreground task window ForceForegroundWindow Me.hWnd ' So bring our window to the front End If End If End Sub Private Sub Form_Resize() On Error Resume Next If Me.WindowState = vbMinimized Then Call TrayIcon1_Click End If End Sub For the record, I also set the ShowInTaskbar property to False for my window. This allows it to only appear in the system tray. Hope this helps someone (and thanks for your help JKDev)! |
|
Post Reply | |
Tweet
|
Forum Jump | Forum Permissions You cannot post new topics in this forum You cannot reply to topics in this forum You cannot delete your posts in this forum You cannot edit your posts in this forum You cannot create polls in this forum You cannot vote in polls in this forum |