Jump to content

How to detect gain and loss of focus?


 Share

Recommended Posts

I have a GUI that must use the keyboard Enter key as a hotkey. However, whenever the GUI loses focus to another application window, I need to release the Enter key. After studying the problem for a while, the SET and KILL Windows Message Codes appeared to be the solution so I implemented these steps to register them for use:

GUIRegisterMsg($WM_SETFOCUS, "On_Get_Focus")
GUIRegisterMsg($WM_KILLFOCUS, "On_Lost_Focus")
:
:
Func On_Get_Focus($wParam, $lParam)
        MsgBox(32, "Got Focus", "", 1)
    HotKeySet("{ENTER}", "NumKeyEnter")
    Return 0
EndFunc

Func On_Lost_Focus($wParam, $lParam)
        MsgBox(32, "Lost Focus", "", 1) 
    HotKeySet("{ENTER}")
    Return 0
EndFunc

The problem is that the functions are never executed -- although the GUI does lose focus when I click on any other application window. The same GUI receives keystrokes using a registered $WM_COMMAND and that works just fine. These two functions are registered in the same manner and I don't see a difference. The MSDN2 documentation for these two notifications is very simple and to the point -- but is there an overall better way to accomplish this?

I will welcome any advice.

Thanks in advance.

Edited by qwert
Link to comment
Share on other sites

Work fine for me:

#include <GUIConstants.au3>

$hGUI = GUICreate("Test GUI", 300, 200)

GUIRegisterMsg($WM_SETFOCUS, "On_Get_Focus")
GUIRegisterMsg($WM_KILLFOCUS, "On_Lost_Focus")

GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func On_Get_Focus($wParam, $lParam)
    ConsoleWrite("Got Focus" & @LF)
    HotKeySet("{ENTER}", "NumKeyEnter")
EndFunc

Func On_Lost_Focus($wParam, $lParam)
    ConsoleWrite("Lost Focus" & @LF)
    HotKeySet("{ENTER}")
EndFunc

Func NumKeyEnter()
    ConsoleWrite("Enter" & @LF)
EndFunc
:D
Link to comment
Share on other sites

Thanks for your response.

The code you suggested went into an immediate lockup state on my PC, so I modified it as shown below to use message boxes. It then displayed one each of the "got focus" and "lost focus" -- and then locked up and wouldn't respond to the gui_event_close.

#include <GUIConstants.au3>

$hGUI = GUICreate("Test GUI", 300, 200, 100, 100)

GUIRegisterMsg($WM_SETFOCUS, "On_Get_Focus")
GUIRegisterMsg($WM_KILLFOCUS, "On_Lost_Focus")

GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func On_Get_Focus($wParam, $lParam)
;   ConsoleWrite("Got Focus" & @LF)
    MsgBox(32, "Got Focus", "", 1)
    HotKeySet("{ENTER}", "NumKeyEnter")
EndFunc

Func On_Lost_Focus($wParam, $lParam)
;   ConsoleWrite("Lost Focus" & @LF)
    MsgBox(32, "Lost Focus", "", 1)
    HotKeySet("{ENTER}")
EndFunc

Func NumKeyEnter()
;   ConsoleWrite("Enter" & @LF)
    MsgBox(32, "Key", "Num Key Enter", 1)
EndFunc

Any idea of why it behaves this way?

Link to comment
Share on other sites

Every time you use a MsgBox you are shifting focus, thus creating an endless loop.

Thanks. That was what I was missing -- and obviously why rasim changed it to a ConsoleWrite. I'll change it back and test -- but how should I monitor the console messages? I've never used them before, but I know the command console isn't involved.
Link to comment
Share on other sites

If you run the Script from SCITE, you can see the ConsoleOutput in the small edit at the bottom

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

If you run the Script from SCITE, you can see the ConsoleOutput in the small edit at the bottom

Thanks for your help. I've now been able to get rasim's example working. But how can I view the console output from a compiled runtime? That's how I need to test my end result. I'm aware that the documentation advises:

Writes data to a stream that text editors can sometimes read.

But how can the output be vectored to an open NotePad window, for example, to get the same effect SciTE provides?
Link to comment
Share on other sites

But how can the output be vectored to an open NotePad window, for example, to get the same effect SciTE provides?

ControlSend("Untitled - Notepad", "", "Edit1", "Line one." & @CRLF & "Line two.")

:D

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Or log it:

#include <GUIConstants.au3>
#include <Date.au3>

Global $hLogfile
$hGUI = GUICreate("Test GUI", 300, 200, 100, 100)

GUIRegisterMsg($WM_SETFOCUS, "On_Get_Focus")
GUIRegisterMsg($WM_KILLFOCUS, "On_Lost_Focus")

GUISetState()
LogOpen()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE
LogClose()

Func On_Get_Focus($wParam, $lParam)
    LogWrite("Got Focus")
    HotKeySet("{ENTER}", "NumKeyEnter")
EndFunc

Func On_Lost_Focus($wParam, $lParam)
    LogWrite("Lost Focus")
    HotKeySet("{ENTER}")
EndFunc

Func NumKeyEnter()
    LogWrite("Enter")
EndFunc

Func LogClose()
    Local $tCur
    If $hLogfile <> 0 Then
        $tCur = _Date_Time_GetSystemTime()
        FileWriteLine($hLogfile, @CRLF & "=========================================================")
        FileWriteLine($hLogfile, "Log file ended: " & _Date_Time_SystemTimeToDateTimeStr($tCur))
        FileClose($hLogfile)
    EndIf
EndFunc

Func LogOpen()
    Local $tCur
    $hLogfile = FileOpen(@ScriptDir & "\" & @MON & @MDAY & @YEAR & ".log", 10)
    If @error = -1 Then
        $hLogfile = 0
        Return
    Else
        $tCur = _Date_Time_GetSystemTime()
        FileWriteLine($hLogfile, "Log file started: " & _Date_Time_SystemTimeToDateTimeStr($tCur))
        FileWriteLine($hLogfile, "=========================================================" & @CRLF & @CRLF)
    EndIf
EndFunc

Func LogWrite($sMsg)
    Local $tCur
    If $hLogfile <> 0 Then
        $tCur = _Date_Time_GetSystemTime()
        FileWriteLine($hLogfile, _Date_Time_SystemTimeToTimeStr($tCur) & ": " & $sMsg)
    EndIf
EndFunc
Link to comment
Share on other sites

OK, after another round of testing ... I'm basically back to where I started: I'm not getting focus-related messages.

Here's an updated test script:

#include <GUIConstants.au3>

$hGUI = GUICreate("Test GUI", 300, 200, 100, 100)
$Button = GUICtrlCreateButton( "a", 20, 40, 40, 34)
$Entry = GUICtrlCreateInput( "", 100, 40, 100, 20)

GUIRegisterMsg($WM_COMMAND, "On_Command")
GUIRegisterMsg($WM_SETFOCUS, "On_Get_Focus")
GUIRegisterMsg($WM_KILLFOCUS, "On_Lost_Focus")
HotKeySet("{ENTER}", "NumKeyEnter")

WinSetOnTop($hGUI, "", 1)
GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func On_Get_Focus($wParam, $lParam)
    ControlSend( "Untitled - Notepad", "", "Edit1", "Got Focus" & @LF,1)
    HotKeySet("{ENTER}", "NumKeyEnter")
Return 0
EndFunc

Func On_Lost_Focus($wParam, $lParam)
    ControlSend("Untitled - Notepad", "", "Edit1", "Lost Focus" & @LF,1)
    HotKeySet("{ENTER}")
Return 0
EndFunc

Func On_Command($hWnd, $Msg, $wParam, $lParam)
    ControlSend("Untitled - Notepad", "", "Edit1", "On Command" & @LF,1)
    Local $iNotifyCode = BitShift($wParam, 16), $iID = BitAnd($wParam, 0x0000FFFF)
Return 0
EndFunc

Func NumKeyEnter()
    ControlSend("Untitled - Notepad", "", "Edit1", "Enter Key" & @LF,1)
EndFunc

When I run it, I never receive any Got Focus and Lost Focus messages in NotePad -- but I always receive the On Command and Enter Key messages. When I had tried the simpler script (in my second post), the focus message were received in NotePad.

Are there different rules when multiple notification messages are registered? I hope someone can shed some light on this. At this point, I'm baffled.

Link to comment
Share on other sites

OK, after another round of testing ... I'm basically back to where I started: I'm not getting focus-related messages.

Anyone willing to give the above (revised) script a try? The Lost Focus notification apparently never executes ... so I feel like I'm back to square one on this whole issue.

Thanks for any help.

Link to comment
Share on other sites

I don't know, how it usually is done, but this works with a workaround:

I use the $NM_OWNERDRAW-Message and check there, if Window is active and if it was Active before.

#include <GUIConstants.au3>
Global $WinNotActive = 1
$hGUI = GUICreate("Test GUI", 300, 200, 100, 100)
$Button = GUICtrlCreateButton( "a", 20, 40, 40, 34)
$Entry = GUICtrlCreateInput( "", 100, 40, 100, 20)

GUIRegisterMsg($WM_COMMAND, "On_Command")
GUIRegisterMsg($WM_SETFOCUS, "On_Get_Focus")
GUIRegisterMsg($WM_KILLFOCUS, "On_Lost_Focus")
GUIRegisterMsg($WM_NOTIFY,"On_Notify")
HotKeySet("{ENTER}", "NumKeyEnter")

WinSetOnTop($hGUI, "", 1)
GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func On_Get_Focus($wParam, $lParam)
    ControlSend( "Unbenannt - Editor", "", "[CLASSNN:Edit1]", "Got Focus" & @LF,1)
    HotKeySet("{ENTER}", "NumKeyEnter")
Return 0
EndFunc

Func On_Lost_Focus($wParam, $lParam)
    ControlSend("Unbenannt - Editor", "", "[CLASSNN:Edit1]", "Lost Focus" & @LF,1)
    HotKeySet("{ENTER}")
Return 0
EndFunc

Func On_Command($hWnd, $Msg, $wParam, $lParam)
    ControlSend("Unbenannt - Editor", "", "[CLASSNN:Edit1]", "On Command" & @LF,1)
    Local $iNotifyCode = BitShift($wParam, 16), $iID = BitAnd($wParam, 0x0000FFFF)
Return 0
EndFunc

Func NumKeyEnter()
    ControlSend("Unbenannt - Editor", "", "[CLASSNN:Edit1]", "Enter Key" & @LF,1)
EndFunc


Func On_Notify($hWnd, $Msg, $wParam, $lParam)
    ControlSend("Unbenannt - Editor", "", "[CLASSNN:Edit1]", "Notify" & @LF,1)
    Local $param = DllStructCreate("long;int;int",$lParam)

    $nNotifyCode    = DllStructGetData($param,3)
    $nID            = DllStructGetData($param,2)
    $hCtrl          = DllStructGetData($param,1)
    if $nNotifyCode = $NM_CUSTOMDRAW Then
        Select
        Case Not WinActive($hWnd) And Not $WinNotActive
            On_Lost_Focus(-1,-1)
            $WinNotActive = 1
        Case WinActive($hWnd) And $WinNotActive
            On_Get_Focus(-1,-1)
        $WinNotActive = 0
        EndSelect
    EndIf

    ; Proceed the default Autoit3 internal message commands.
    ; You also can complete let the line out.
    ; !!! But only 'Return' (without any value) will not proceed
    ; the default Autoit3-message in the future !!!
    Return $GUI_RUNDEFMSG
EndFunc
Edited by ProgAndy

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

Thanks very much for the response. After a quick first look, I can't say I exactly understand your suggestion -- but I will work with it in a few hours. I've got to come up with something that's reliable since I have to be able to release the hotkey, or my application certainly won't be "user friendly".

Link to comment
Share on other sites

What you need is WM_ACTIVATE.

That's more like it! I now get a notification on each and every change of focus. The one remaining problem is that the contents of the parameters being passed is always the same, so I don't know which occurrence is Lost Focus:

WM_Activate 0052076A 00000006

WM_Activate 0052076A 00000006

WM_Activate 0052076A 00000006

Here's what I'm using to display the notification's parameters:

ControlSend( "Untitled - Notepad", "", "Edit1", "WM_Activate " & Hex($wParam) & " " & Hex($lParam) & @LF,1)

Any ideas?

Link to comment
Share on other sites

The one remaining problem is that the contents of the parameters being passed is always the same, so I don't know which occurrence is Lost Focus:

WM_Activate 0052076A 00000006

Neither of those tells you of lost focus,

if your message functions look like this

Func On_Get_Focus($wParam, $lParam) ;<--- RTFM

I'm pretty sure we've been here before, with you or someone else, and I'm not about explain it over again.

Just have this

Func On_WM_ACTIVATE($hWnd, $Msg, $wParam, $lParam)
    Local $iState = BitAnd($wParam, 0x0000FFFF), $iMinimize = BitShift($wParam, 16)
    If $iState Then
        HotKeySet("{ENTER}", "NumKeyEnter")
    Else
        HotKeySet("{ENTER}")
    EndIf
EndFunc

"be smart, drink your wine"

Link to comment
Share on other sites

Func On_Get_Focus($wParam, $lParam) ;<--- RTFM

I'm pretty sure we've been here before, with you or someone else, and I'm not about explain it over again.

Actually, I did RTFM and the MSDN2 site told me this:

WM_ACTIVATE Notification

The WM_ACTIVATE message is sent to both the window being activated and the window being deactivated. If the windows use the same input queue, the message is sent synchronously, first to the window procedure of the top-level window being deactivated, then to the window procedure of the top-level window being activated. If the windows use different input queues, the message is sent asynchronously, so the window is activated immediately.

Syntax of WM_ACTIVATE

WPARAM wParam

LPARAM lParam;

wParam

The low-order word specifies whether the window is being activated or deactivated. This parameter can be one of the following values. The high-order word specifies the minimized state of the window being activated or deactivated. A nonzero value indicates the window is minimized.

WA_ACTIVE -- Activated by some method other than a mouse click (for example, by a call to the SetActiveWindow function or by use of the keyboard interface to select the window).

WA_CLICKACTIVE -- Activated by a mouse click.

WA_INACTIVE -- Deactivated.

lParam

Handle to the window being activated or deactivated, depending on the value of the wParam parameter. If the low-order word of wParam is WA_INACTIVE, lParam is the handle to the window being activated. If the low-order word of wParam is WA_ACTIVE or WA_CLICKACTIVE, lParam is the handle to the window being deactivated. This handle can be NULL.

I certainly don't use notification messages enough to recognize when that when they say "2 parameters" they mean "4 parameters". Examples of the correct results I'm getting back now are:

WM_Activate 002207C0 00000006 00000001 00000000

WM_Activate 002207C0 00000006 00000000 00000000

WM_Activate 002207C0 00000006 00000002 00000000

WM_Activate 002207C0 00000006 00000000 00000000

WM_Activate 002207C0 00000006 00200001 00000000

WM_Activate 002207C0 00000006 00200000 00000000

I certainly appreciate the code you provided (but not so much the scolding).

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...