Jump to content
Professor_Bernd

Non-focusable GUI does not give focus after calling "evil" functions

Recommended Posts

Merry Christmas. ūüéĄ

; Non-focusable GUI does not give focus after calling "evil" functions
;
; Demo 1, 2020-12-24, Professor Bernd.

#include <GuiConstants.au3>

; ToolWindow, frameless, topmost, which never gets focus.
$hMy_GUI = GUICreate("TEST", 500, 100, -1, -1, _
  BitOR($WS_POPUP, $WS_BORDER), BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_NOACTIVATE))

$btnClose = GUICtrlCreateButton("X", 10, 10, 24, 24)

GUISetState(@SW_SHOWNOACTIVATE, $hMy_GUI)

Example_1()

Func Example_1()
  Local $aCaretPos

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        Exit
      Case $btnClose
        Exit
    EndSwitch

    ; First start the script with commented out line "$aCaretPos = _WinGetCaretPos()".
    ; Click on the non-focusable GUI and press character or arrow keys on the keyboard.
    ; All input goes to the editor (or to other windows if you click on them).
    ;
    ; Exit the script, uncomment the line "$aCaretPos = _WinGetCaretPos()" and start
    ; the script again. Click on the non-focusable GUI and press character or arrow
    ; keys on the keyboard. No other window will get the input.

    ; The function is not evaluated, it is only for demo.
;     $aCaretPos = _WinGetCaretPos()
  WEnd

  ; Affected (= "evil") functions are: "WinGetCaretPos()", "ControlGetFocus()" and "ControlGetPos()".
EndFunc

; ------------------------------------------------------------------------------
; This is the example function from the AutoIt help for "WinGetCaretPos".
; ------------------------------------------------------------------------------
; A more reliable method to retrieve the caret coordinates in MDI text editors.
Func _WinGetCaretPos()
    Local $iXAdjust = 5
    Local $iYAdjust = 40

    Local $iOpt = Opt("CaretCoordMode", 0) ; Set "CaretCoordMode" to relative mode and store the previous option.
    Local $aGetCaretPos = WinGetCaretPos() ; Retrieve the relative caret coordinates.
    Local $aGetPos = WinGetPos("[ACTIVE]") ; Retrieve the position as well as height and width of the active window.
    Local $sControl = ControlGetFocus("[ACTIVE]") ; Retrieve the control name that has keyboard focus.
    Local $aControlPos = ControlGetPos("[ACTIVE]", "", $sControl) ; Retrieve the position as well as the size of the control.
    $iOpt = Opt("CaretCoordMode", $iOpt) ; Reset "CaretCoordMode" to the previous option.

    Local $aReturn[2] = [0, 0] ; Create an array to store the x, y position.
    If IsArray($aGetCaretPos) And IsArray($aGetPos) And IsArray($aControlPos) Then
        $aReturn[0] = $aGetCaretPos[0] + $aGetPos[0] + $aControlPos[0] + $iXAdjust
        $aReturn[1] = $aGetCaretPos[1] + $aGetPos[1] + $aControlPos[1] + $iYAdjust
        Return $aReturn ; Return the array.
    Else
        Return SetError(1, 0, $aReturn) ; Return the array and set @error to 1.
    EndIf
EndFunc   ;==>_WinGetCaretPos

 

This demo script actually creates a GUI that never gets the focus. You can click on it as often as you want, the focus is always on another window, e.g. on the editor. That's why you can, for example, navigate through the other window with the arrow keys, or write characters in an editor, even if you click on the non-focusable GUI.

This is the behavior as long as the line calling "$aCaretPos = _WinGetCaretPos()" is commented out. If you leave this line uncommented, after starting the script everything is as above. But as soon as you click 1x on the non-focusable GUI, the effect is reversed and the non-focusable GUI ALWAYS has the focus! NO other window gets the focus, e.g. Editor, Browser, Windows Explorer, etc.

I was able to narrow down the culprit. The affected (= "evil") functions are: "WinGetCaretPos()", "ControlGetFocus()" and "ControlGetPos()". :evil:

Can anyone help to establish the normal behavior of the non-focusable GUI, even when the evil functions are used?

Edited by Professor_Bernd

Share this post


Link to post
Share on other sites
On 12/25/2020 at 10:01 AM, mikell said:

 

Func _WinGetCaretPos()
  If GUIGetCursorInfo($hMy_GUI) <> null Then Return
  ...

Impressive idea! :idea:

I have now tested for so long and tried so much to prevent focus on the non-focusable GUI or to set focus on the editor, but I didn't come up with this idea.

For example, I have also tested the following without success:

Func _WinGetCaretPos()
    If WinGetHandle("[ACTIVE]") = $hMy_GUI Then Return
    ...

 

On 12/25/2020 at 10:01 AM, mikell said:

I don't really understand the purpose of the thing but this seems to work

The trick you found there simply causes the "evil" code not to be executed when the mouse cursor is over the non-focusable GUI. If you click on the non-focusable GUI while the evil code is not executed, the non-focusable GUI can function normally and thus never gets focus.

This is a good workaround! I hope it works when I put it in the real script (CallTipViewer).

  Edit: For users reading this later: At that time I didn't know that the workaround doesn't work. See my post 5 posts down.

I also found the bug in "WinGetCaretPos". In the AutoIt3 source code , it checks the foreground window instead of checking the active window. (search in the AutoIt3 source code page for "WinGetCaretPos")

Spoiler

///////////////////////////////////////////////////////////////////////////////
// WinGetCaretPos()
//
// pos = WinGetCaretPos()
// Gets the caret position of the foreground window
// Trying to get the caret position of a chosen window seems unreliable
// Coordinates are relative to the window its in (This gets funky in MDI's but the correct
// coordinates can be determined by some scripting to fix the offfset)
///////////////////////////////////////////////////////////////////////////////

AUT_RESULT AutoIt_Script::F_WinGetCaretPos(VectorVariant &vParams, Variant &vResult)
{
	POINT point, ptOrigin;
	Variant *pvTemp;
	HWND hWnd = GetForegroundWindow(); // <== I think GetActiveWindow() would be better.

	// Doesn't work without attaching
	Util_AttachThreadInput(hWnd, true);

	if (GetCaretPos(&point) == FALSE)
		SetFuncErrorCode(1);
	else
	{
		// point contains the caret pos in CLIENT area coordinates, convert to screen (absolute coords)
		// and then let the current mode decide how they will be returned
		ClientToScreen(hWnd, &point);
		ConvertCoords(m_nCoordCaretMode, ptOrigin);
		point.x -= ptOrigin.x;
		point.y -= ptOrigin.y;

		Util_VariantArrayDim(&vResult, 2);

		pvTemp = Util_VariantArrayGetRef(&vResult, 0);		// First element
		*pvTemp = (int)point.x;					// X

		pvTemp = Util_VariantArrayGetRef(&vResult, 1);
		*pvTemp = (int)point.y;					// Y
	}

	Util_AttachThreadInput(hWnd, false);

	return AUT_OK;

} // WinGetCaretPos()

 

I think it is a bug, because the foreground window does not necessarily have to be the active window. Any topmost window can be in the Z-order above the active window that contains the caret.

I would be happy if someone would inform the developers of AutoIt3. (Even though it probably won't benefit my code if it ever gets fixed). I myself unfortunately have too little knowledge to present this in English.

Thank you very much for this workaround! I will test it in my CallTipViewer script. Keep your fingers crossed that it works then.¬†ūüĎć

Edited by Professor_Bernd

Share this post


Link to post
Share on other sites

I am not sure it is a bug as doc for WinGetCaretPos() say it is working on the foreground windows

so what did the @mikell line is just returning in all cases as the GUI is defined

Not very clear to check an array existing with "Null"

In Fact the evil function can really be executed without pb if my proof is correct

; Non-focusable GUI does not give focus after calling "evil" functions
;
; Demo 1, 2020-12-24, Professor Bernd.

#include <GuiConstants.au3>

; ToolWindow, frameless, topmost, which never gets focus.
Local $hMy_GUI = GUICreate("TEST", 500, 100, -1, -1, _
  BitOR($WS_POPUP, $WS_BORDER), BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_NOACTIVATE))

Local $btnClose = GUICtrlCreateButton("X", 10, 10, 24, 24)
Local $btnEvil = GUICtrlCreateButton("Evil", 100, 10, 24, 24)

GUISetState(@SW_SHOWNOACTIVATE, $hMy_GUI)

Example_1()

Func Example_1()
  Local $aCaretPos

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        Exit
      Case $btnClose
        Exit
      Case $btnEvil
        $aCaretPos = _WinGetCaretPos()
    EndSwitch

    ; First start the script with commented out line "$aCaretPos = _WinGetCaretPos()".
    ; Click on the non-focusable GUI  and press character or arrow keys on the keyboard.
    ; All input goes to the editor (or to other windows if you click on them).
    ;
    ; Exit the script, uncomment the line "$aCaretPos = _WinGetCaretPos()" and start
    ; the script again. Click on the non-focusable GUI and press character or arrow
    ; keys on the keyboard. No other window will get the input.

    ; The function is not evaluated, it is only for demo.
;~      $aCaretPos = _WinGetCaretPos()
  WEnd

  ; Affected (= "evil") functions are: "WinGetCaretPos()", "ControlGetFocus()" and "ControlGetPos()".
  Return $aCaretPos
EndFunc

; ------------------------------------------------------------------------------
; This is the example function from the AutoIt help for "WinGetCaretPos".
; ------------------------------------------------------------------------------
; A more reliable method to retrieve the caret coordinates in MDI text editors.
Func _WinGetCaretPos()
    Local $iXAdjust = 5
    Local $iYAdjust = 40

    Local $iOpt = Opt("CaretCoordMode", 0) ; Set "CaretCoordMode" to relative mode and store the previous option.
    Local $aGetCaretPos = WinGetCaretPos() ; Retrieve the relative caret coordinates.
    Local $aGetPos = WinGetPos("[ACTIVE]") ; Retrieve the position as well as height and width of the active window.
    Local $sControl = ControlGetFocus("[ACTIVE]") ; Retrieve the control name that has keyboard focus.
    Local $aControlPos = ControlGetPos("[ACTIVE]", "", $sControl) ; Retrieve the position as well as the size of the control.
    $iOpt = Opt("CaretCoordMode", $iOpt) ; Reset "CaretCoordMode" to the previous option.

    Local $aReturn[2] = [0, 0] ; Create an array to store the x, y position.
    If IsArray($aGetCaretPos) And IsArray($aGetPos) And IsArray($aControlPos) Then
        $aReturn[0] = $aGetCaretPos[0] + $aGetPos[0] + $aControlPos[0] + $iXAdjust
        $aReturn[1] = $aGetCaretPos[1] + $aGetPos[1] + $aControlPos[1] + $iYAdjust
        Return $aReturn ; Return the array.
    Else
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $aReturn = ' & $aReturn & @CRLF & '>Error code: ' & @error & '    Extended code: ' & @extended & ' (0x' & Hex(@extended) & ')' & @CRLF) ;### Debug Console
       Return SetError(1, 0, $aReturn) ; Return the array and set @error to 1.
    EndIf
EndFunc   ;==>_WinGetCaretPos

Cheers

Share this post


Link to post
Share on other sites

Hi jpm.

I don't even know where to start. :) Your example has nothing to do with the problems I described.

In my code the wicked functions are executed all the time in a loop.
In your code the wicked functions are only executed when a button is clicked.

In my code the GUI is created as non-focusable.
In your code, the GUI is focusable after a single click.

In my code the caret coordinates are constantly queried. The caret coordinates are used to place a CallTip window before it becomes visible.
In your code, the caret coordinates are only requested when the button is clicked. It is of no use to place the CallTip after it is visible. Then it is already placed somewhere else.

6 hours ago, jpm said:

I am not sure it is a bug as doc for WinGetCaretPos() say it is working on the foreground windows

If you want to get the caret's position, you probably want the caret from the window that gets the keyboard input, right? What sense does it make to search for the caret in an inactive window? In my specific case, a CallTip window is displayed above the editor topmost. The CallTipWin is a non-focusable GUI. Keyboard input goes to the editor below the CallTipWin. The CallTipWin does not contain any caret at all. Why look for the caret position there, just because it is the foreground window, while the editor window is active and has the input focus? :huh2:

In addition, it is clearly visible that the GUI that is actually not focusable is activated by WinGetCaretPos() and receives the input focus that it should not receive.

6 hours ago, jpm said:

In Fact the evil function can really be executed without pb if my proof is correct

What does pb mean? Problems? (Sorry, my English is not that good).

I see big problems there, described in the first post and in the demo code! If WinGetCaretPos() is called in a loop and not only 1x by button, then no other window gets the focus after clicking 1x on the actually non-focusable GUI. Even if I only click 1x on the button in your example, the GUI becomes focusable, and the exStyle $WS_EX_NOACTIVATE is destroyed.

The CallTip is placed when called, the parameters are highlighted and moved with each change of a parameter. When the caret leaves a function, the CallTip is closed. If the caret changes to another function within a function, the CallTip is moved. - So WinGetCaretPos() cannot be queried only 1x by button, but must determine the values in a loop. This leads to the problems mentioned above.

Another example is a virtual keyboard. If you have a non-focusable GUI with keyboard keys, then you query the caret position where the characters should be inserted into an edit in the underlying window. But when clicking a button, WinGetCaretPos() takes the focus away from the edit just because the virtual keyboard is the foreground window. This would not work.

If it is a bug to use GetForegroundWindow(), I can't say for sure. I would be happy if you know another solution for the mentioned problems.

Regardless of a solution, thank you for posting here. :)

 

Edit 2: "virtual keyboard" example: I think to insert characters with a virtual keyboard at the caret, you don't need the caret position, but maybe ControlGetFocus().

Edited by Professor_Bernd
Typo removed (hopefully).

Share this post


Link to post
Share on other sites

@Professor_Bernd

Sorry my previous suggestion was bad... I didn't test !! :>
I don't know if this is what you want but the best way I can think of is something like this

#include <GuiConstants.au3>

$hMy_GUI = GUICreate("TEST", 500, 100, -1, 10, _
  BitOR($WS_POPUP, $WS_BORDER), BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_NOACTIVATE))
$tip = GuiCtrlCreateLabel("", 400, 10, 100, 17)
$btnClose = GUICtrlCreateButton("X", 10, 10, 24, 24)

GUISetState(@SW_SHOWNOACTIVATE, $hMy_GUI)


Local $aCaretPos[2] = [0, 0], $pos = $aCaretPos

While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        Exit
      Case $btnClose
        Exit
    EndSwitch

     $aCaretPos = _WinGetCaretPos()
  If IsArray($aCaretPos) and ($pos[0] <> $aCaretPos[0] or $pos[1] <> $aCaretPos[1]) Then 
     $pos = $aCaretPos
     GuiCtrlSetData($tip, $pos[0] & " x " & $pos[1])
  EndIf
  Sleep(10)
WEnd

;=====================================

Func _WinGetCaretPos()
    Local $iXAdjust = 5
    Local $iYAdjust = 40

    Local $iOpt = Opt("CaretCoordMode", 0) ; Set "CaretCoordMode" to relative mode and store the previous option.
    Local $aGetCaretPos = WinGetCaretPos() ; Retrieve the relative caret coordinates.
    Local $aGetPos = WinGetPos("[ACTIVE]") ; Retrieve the position as well as height and width of the active window.
    Local $sControl = ControlGetFocus("[ACTIVE]") ; Retrieve the control name that has keyboard focus.
    Local $aControlPos = ControlGetPos("[ACTIVE]", "", $sControl) ; Retrieve the position as well as the size of the control.
    $iOpt = Opt("CaretCoordMode", $iOpt) ; Reset "CaretCoordMode" to the previous option.

    Local $aReturn[2] = [0, 0] ; Create an array to store the x, y position.
    If IsArray($aGetCaretPos) And IsArray($aGetPos) And IsArray($aControlPos) Then
        $aReturn[0] = $aGetCaretPos[0] + $aGetPos[0] + $aControlPos[0] + $iXAdjust
        $aReturn[1] = $aGetCaretPos[1] + $aGetPos[1] + $aControlPos[1] + $iYAdjust
        Return $aReturn ; Return the array.
    Else
        Return SetError(1, 0, $aReturn) ; Return the array and set @error to 1.
    EndIf
EndFunc   ;==>_WinGetCaretPos

 

Share this post


Link to post
Share on other sites

@mikell

Ha, ha, I set a trap for myself! :P By not evaluating _WinGetCaretPos() in the demo, I didn't realize that your GUIGetCursorInfo() idea doesn't work. Actually, the evil functions are never executed with it. That is, caret coordinates don't arrive either.

 

7 hours ago, jpm said:

Not very clear to check an array existing with "Null"

OK, now I understand.

 

So let's start from scratch. ;) I think mikell's idea could still be the right approach for a workaround. The concept could be to not execute the nasty functions while the mouse cursor is over the non-focusable GUI. In the case of my CallTip, that might work. I'll do some tests with _WinAPI_PtInRect and the like.

Share this post


Link to post
Share on other sites

@jpm

The MS documentation for GetCaretPos function (winuser.h) talks about "the" caret. It occurred to me that there is only one caret in the system and the caret is in the active window. Is that correct? If yes, this underlines my assumption to check the active window in WinGetCaretPos() instead of the foreground window. What do you think? :think:

Share this post


Link to post
Share on other sites

I don't understand what the last example from @mikell prove.

When I click on thews_ex_notactive window the caret does not show up on any other windows. whatever you click in

so what do you want to be the behavior?

 

Share this post


Link to post
Share on other sites
1 hour ago, jpm said:

I don't understand what the last example from @mikell prove.

When I click on thews_ex_notactive window the caret does not show up on any other windows. whatever you click in

Yes, the last example from mikell does not bring any improvement. But mikell's ideas have probably put me on the right track and I have now found a possible workaround. :idea:

1 hour ago, jpm said:

so what do you want to be the behavior?

Here is my demo 6, which shows once the unwanted behavior of the nasty functions and once with the workaround the desired behavior.  Un/comment simply the line:

GUIRegisterMsg($WM_MOUSEACTIVATE, 'WM_EVENTS')

; Non-focusable GUI does not give focus after calling "evil" functions
;
; Demo 6, 2020-12-26, Professor Bernd.

#include <ButtonConstants.au3>
#include <GuiConstants.au3>
#include <WinAPIConv.au3>
#include <WinAPISysWin.au3>

Opt("MustDeclareVars", 1)

; First start the script with commented out line
; "GUIRegisterMsg($WM_MOUSEACTIVATE, 'WM_EVENTS')".
; Click on the non-focusable GUI and press character or arrow keys on the keyboard.
; No other window will get the input.
;
; Workaround: Exit the script, uncomment the line
; "GUIRegisterMsg($WM_MOUSEACTIVATE, 'WM_EVENTS')" and start the script again.
; Click on the non-focusable GUI and press character or arrow keys on the keyboard.
; All input goes to the editor (or to other windows if you click on them).

GUIRegisterMsg($WM_MOUSEACTIVATE, 'WM_EVENTS')

Const $MA_NOACTIVATEANDEAT = 4

; ToolWindow, frameless, topmost, which never gets focus.
Global $hMy_GUI = GUICreate("TEST", 200, 120, -1, -1, _
    BitOR($WS_POPUP, $WS_BORDER), BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_NOACTIVATE))

Global $btnClose = GUICtrlCreateButton("X", 10, 10, 24, 24)

Global $lblLabel_1 = GUICtrlCreateLabel("Label 1", 10, 60, 300)
Global $lblLabel_2 = GUICtrlCreateLabel("Label 1", 10, 80)
Global $lblCoords = GUICtrlCreateLabel("Coords", 80, 10, 80)

GUISetState(@SW_SHOWNOACTIVATE, $hMy_GUI)

Example_1()

Func Example_1()
  Local $aCaretPos, $iCnt = 0

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        Exit
      Case $btnClose
        Exit
      Case $lblLabel_1
        $iCnt += 1
        GUICtrlSetData($lblLabel_2, $iCnt)
      Case $lblLabel_2
        $iCnt += 1
        GUICtrlSetData($lblLabel_1, $iCnt)
    EndSwitch

    $aCaretPos = _WinGetCaretPos()
    If IsArray($aCaretPos) Then
      GUICtrlSetData($lblCoords, "X: " & $aCaretPos[0] & "  Y: " & $aCaretPos[1])
      WinMove($hMy_GUI, "", $aCaretPos[0], $aCaretPos[1])
    EndIf
  WEnd

  ; Affected (= "evil") functions are: "WinGetCaretPos()", "ControlGetFocus()" and "ControlGetPos()", ...
EndFunc   ;==>Example_1

; ------------------------------------------------------------------------------
; This is the example function from the AutoIt help for "WinGetCaretPos".
; ------------------------------------------------------------------------------
; A more reliable method to retrieve the caret coordinates in MDI text editors.
Func _WinGetCaretPos()
  Local $iXAdjust = 0
  Local $iYAdjust = 20

  Local $iOpt = Opt("CaretCoordMode", 0)   ; Set "CaretCoordMode" to relative mode and store the previous option.
  Local $aGetCaretPos = WinGetCaretPos()   ; Retrieve the relative caret coordinates.
  Local $aGetPos = WinGetPos("[ACTIVE]")   ; Retrieve the position as well as height and width of the active window.
  Local $sControl = ControlGetFocus("[ACTIVE]")   ; Retrieve the control name that has keyboard focus.
  Local $aControlPos = ControlGetPos("[ACTIVE]", "", $sControl)   ; Retrieve the position as well as the size of the control.
  $iOpt = Opt("CaretCoordMode", $iOpt)   ; Reset "CaretCoordMode" to the previous option.

  Local $aReturn[2] = [0, 0]   ; Create an array to store the x, y position.
  If IsArray($aGetCaretPos) And IsArray($aGetPos) And IsArray($aControlPos) Then
    $aReturn[0] = $aGetCaretPos[0] + $aGetPos[0] + $aControlPos[0] + $iXAdjust
    $aReturn[1] = $aGetCaretPos[1] + $aGetPos[1] + $aControlPos[1] + $iYAdjust
    Return $aReturn     ; Return the array.
  Else
    Return SetError(1, 0, $aReturn)     ; Return the array and set @error to 1.
  EndIf
EndFunc   ;==>_WinGetCaretPos

Func WM_EVENTS($hWndGUI, $MsgID, $WParam, $LParam)
  Switch $hWndGUI
    Case $hMy_GUI
      Switch $MsgID
        Case $WM_MOUSEACTIVATE
          ; Check mouse position
          Local $aMouse_Pos = GUIGetCursorInfo($hMy_GUI)
          If $aMouse_Pos[4] <> 0 Then
            Local $word = _WinAPI_MakeLong($aMouse_Pos[4], $BN_CLICKED)
            _SendMessage($hMy_GUI, $WM_COMMAND, $word, GUICtrlGetHandle($aMouse_Pos[4]))
          EndIf
          Return $MA_NOACTIVATEANDEAT
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_EVENTS

This workaround works great in Scite, but in PSPad double clicks in the editor get eaten, probably by the nasty functions. Does anyone know a solution for this?

Edited by Professor_Bernd
Added link to the source of the workaround.

Share this post


Link to post
Share on other sites
On 12/26/2020 at 2:51 PM, jpm said:

nice catch

Thank you. :)

On 12/26/2020 at 2:28 PM, Professor_Bernd said:

This workaround works great in Scite, but in PSPad double clicks in the editor get eaten, probably by the nasty functions. Does anyone know a solution for this?

The problem persists and the "nasty" functions, called in a loop, eat the double clicks not only in PSPad but also in other windows, e.g. in Windows desktop, Windows Notepad, AutoIt Help window, ...

Here is a demo with which you can test it:

Spoiler
; ------------------------------------------------------------------------------
; "Evil" functions, called in a loop, eat the double-clicks in some windows.
;
; Demo 8b, 2020-12-27, Professor Bernd.
;
; Affected (= "evil") functions are: "WinGetCaretPos()", "ControlGetFocus()" and "ControlGetPos()", ...
;
; Affected windows are: PSPad, Windows desktop, Win Notepad, AutoIt Help window, ...
;
; Not affected windows are: SciTE, Windows Explorer, Waterfox/Firefox, Libre Office Writer, ...
;
; Steps to reproduce:
; Start the script and click on the non-focusable window. Then double-click on
; a desktop icon, or another affected window.
; ------------------------------------------------------------------------------

#include <ButtonConstants.au3>
#include <GuiConstants.au3>
#include <Misc.au3>
#include <WinAPIConv.au3>
#include <WinAPISysWin.au3>

Opt("MustDeclareVars", 1)

; The function "WM_EVENTS" does not affect the double-click eating. You can test
; it by commenting out the next line.
GUIRegisterMsg($WM_MOUSEACTIVATE, 'WM_EVENTS')

; Global Const $MA_NOACTIVATE = 3
Global Const $MA_NOACTIVATEANDEAT = 4

; ToolWindow, frameless, topmost, which never gets focus.
Global $hMy_GUI = GUICreate("TEST", 200, 120, -1, -1, _
  BitOR($WS_POPUP, $WS_BORDER), BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST, $WS_EX_NOACTIVATE))
Global $btnClose = GUICtrlCreateButton("X", 10, 10, 24, 24)
Global $lblCoords = GUICtrlCreateLabel("Coords", 80, 10, 80)

GUISetState(@SW_SHOWNOACTIVATE, $hMy_GUI)

Example_1()

Func Example_1()
  Local $aCaretPos, $iCnt = 0
  Local $iCaretX_Old, $iCaretY_Old

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        Exit
      Case $btnClose
        Exit
    EndSwitch

    $aCaretPos = _WinGetCaretPos()

    If IsArray($aCaretPos) Then
      If ($iCaretX_Old <> $aCaretPos[0]) Or ($iCaretY_Old <> $aCaretPos[1]) Then
;         GUICtrlSetData($lblCoords, "X: " & $aCaretPos[0] & "  Y: " & $aCaretPos[1])
        ConsoleWrite("X: " & $aCaretPos[0] & "  Y: " & $aCaretPos[1] & @CRLF)
      EndIf
      $iCaretX_Old = $aCaretPos[0]
      $iCaretY_Old = $aCaretPos[1]
    EndIf
  WEnd
EndFunc   ;==>Example_1

; ------------------------------------------------------------------------------
; This is the example function from the AutoIt help for "WinGetCaretPos".
; ------------------------------------------------------------------------------
; A more reliable method to retrieve the caret coordinates in MDI text editors.
Func _WinGetCaretPos()
  Local $iXAdjust = 0
  Local $iYAdjust = 20

  Local $iOpt = Opt("CaretCoordMode", 0)   ; Set "CaretCoordMode" to relative mode and store the previous option.
  Local $aGetCaretPos = WinGetCaretPos()   ; Retrieve the relative caret coordinates.
  Local $aGetPos = WinGetPos("[ACTIVE]")   ; Retrieve the position as well as height and width of the active window.
  Local $sControl = ControlGetFocus("[ACTIVE]")   ; Retrieve the control name that has keyboard focus.
  Local $aControlPos = ControlGetPos("[ACTIVE]", "", $sControl)   ; Retrieve the position as well as the size of the control.
  $iOpt = Opt("CaretCoordMode", $iOpt)   ; Reset "CaretCoordMode" to the previous option.

  Local $aReturn[2] = [0, 0]   ; Create an array to store the x, y position.
  If IsArray($aGetCaretPos) And IsArray($aGetPos) And IsArray($aControlPos) Then
    $aReturn[0] = $aGetCaretPos[0] + $aGetPos[0] + $aControlPos[0] + $iXAdjust
    $aReturn[1] = $aGetCaretPos[1] + $aGetPos[1] + $aControlPos[1] + $iYAdjust
    Return $aReturn     ; Return the array.
  Else
    Return SetError(1, 0, $aReturn)     ; Return the array and set @error to 1.
  EndIf
EndFunc   ;==>_WinGetCaretPos

Func WM_EVENTS($hWndGUI, $MsgID, $WParam, $LParam)
  Switch $hWndGUI
    Case $hMy_GUI
      Switch $MsgID
        Case $WM_MOUSEACTIVATE
          ; Check mouse position
          Local $aMouse_Pos = GUIGetCursorInfo($hMy_GUI)
          If $aMouse_Pos[4] <> 0 Then
            Local $word = _WinAPI_MakeLong($aMouse_Pos[4], $BN_CLICKED)
            _SendMessage($hMy_GUI, $WM_COMMAND, $word, GUICtrlGetHandle($aMouse_Pos[4]))
          EndIf
          Return $MA_NOACTIVATEANDEAT
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_EVENTS

 

 

As an alternative to "WinGetCaretPos()" I tested "_WinAPI_GetCaretPos()". Unfortunately "_WinAPI_GetCaretPos()" has a bug which is not fixed even with the patch from @jpm.

  • Without the patch the return is always 0.
  • With the patch the return is an array and each item is always 0.
Spoiler
; ------------------------------------------------------------------------------
; _WinAPI_GetCaretPos() Bug: Always returns 0, respectively 0, 0
;
; Demo 8c, 2020-12-27, Professor Bernd.
; ------------------------------------------------------------------------------

#include <GuiConstants.au3>
#include <WinAPIRes.au3>

Opt("MustDeclareVars", 1)

; ToolWindow, frameless, topmost, which never gets focus.
Global $hMy_GUI = GUICreate("TEST", 200, 120)

GUISetState(@SW_SHOW, $hMy_GUI)

Example_1()

Func Example_1()
  Local $aCaretPos
  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        Exit
    EndSwitch

;     $aCaretPos = _WinAPI_GetCaretPos()
    $aCaretPos = _WinAPI_GetCaretPos_Patch()

    If Not IsArray($aCaretPos) Then
      ConsoleWrite($aCaretPos & @CRLF)
    Else
      ConsoleWrite("X: " & $aCaretPos[0] & "  Y: " & $aCaretPos[1] & @CRLF)
    EndIf
  WEnd
EndFunc   ;==>Example_1

Func _WinAPI_GetCaretPos_Patch()
    Local $tPOINT = DllStructCreate($tagPOINT)
    Local $aRet = DllCall('user32.dll', 'bool', 'GetCaretPos', 'struct*', $tPOINT)
    If @error Or Not $aRet[0] Then Return SetError(@error + 10, @extended, 0)

    Local $aResult[2]
    For $i = 0 To 1
        $aResult[$i] = DllStructGetData($tPOINT, $i + 1)
    Next
    Return $aResult
EndFunc   ;==>_WinAPI_GetCaretPos

 

Does anyone have an idea how to solve this? For example, how to make the API DllCall for 'GetCaretPos' so that it returns a correct result?

Share this post


Link to post
Share on other sites

Hey,

I just ran it in SciTE, and seems to be semi-functioning. Doesn't work in Chrome while typing this (no caret info), and dbl.clicks don't work in Excel or Notepad, but does work in Notepad++.

 

Share this post


Link to post
Share on other sites
35 minutes ago, GokAy said:

dbl.clicks don't work in Excel or Notepad, but does work in Notepad++.

Thanks for the confirmation! I tested on Win 10, what Windows did you test on? Have you tested double clicks on the windows desktop?

Share this post


Link to post
Share on other sites

Win10. No dbl.clicks on desktop either. Notepad++ was the only one working. But that's about all I have tested with.

Maybe you can get something working better out of these:

https://docs.microsoft.com/en-us/windows/win32/api/ (User Interface and Desktop - Windows and Messages)

https://docs.microsoft.com/en-us/windows/win32/inputdev/mouse-input-notifications

Not sure if you would need this one, but also attaching a text doc with Win32 API declarations for VBA.

Edit: According to this https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorinfo

(Same goes for GetCursorPos)

Return value

Type: BOOL

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Win32API_PtrSafe.txt

Edited by GokAy

Share this post


Link to post
Share on other sites
9 hours ago, GokAy said:

Win10. No dbl.clicks on desktop either. Notepad++ was the only one working.

Thanks for confirming. :)

9 hours ago, GokAy said:

Maybe you can get something working better out of these:

Please excuse, maybe my skills are too low, but I don't know how these things can help me. ūüėü

What I need is a replacement for the "evil" functions, starting with "WinGetCaretPos()".  For that I wanted to use "_WinAPI_GetCaretPos()" as a replacement, but that probably has a bug as well.

Does anyone know how to make the API DLLCall for "_WinAPI_GetCaretPos()" to return a correct result? :think:

Share this post


Link to post
Share on other sites
27 minutes ago, Professor_Bernd said:

Please excuse, maybe my skills are too low, but I don't know how these things can help me.

You or me? :) Most of these are new to me as well, and I only used very simple ones and only in VBA (Excel). 

Declare PtrSafe Function GetCaretPos Lib "user32" Alias "GetCaretPos" (lpPoint As POINTAPI) As Long

Type POINTAPI
        x As Long
        y As Long
End Type

Copies the caret's position to the specified POINT structure.

 

-- May not be accurate or even correct! --

So, my understanding is you need to:

1. Create a DLL Struct for POINTAPI (i.e., $tStruct)

https://www.autoitscript.com/autoit3/docs/functions/DllStructCreate.htm

2. Then make the call: DllCall("user32.dll","BOOLEAN","GetCaretPos","STRUCT",$tStruct)    ** Maybe need to use "STRUCT*" which means by reference  

https://www.autoitscript.com/autoit3/docs/functions/DllCall.htm

Using "Struct" as Return Type

STRUCT structure created with DllStructCreate()

3. And read x and y with:

https://www.autoitscript.com/autoit3/docs/functions/DllStructGetData.htm

Share this post


Link to post
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

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...