Professor_Bernd Posted December 24, 2020 Share Posted December 24, 2020 (edited) Merry Christmas. 🎄 expandcollapse popup; 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()". Can anyone help to establish the normal behavior of the non-focusable GUI, even when the evil functions are used? Edited December 24, 2020 by Professor_Bernd Link to comment Share on other sites More sharing options...
Nine Posted December 24, 2020 Share Posted December 24, 2020 Remove $WS_EX_NOACTIVATE, and all works fine for me “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 24, 2020 Author Share Posted December 24, 2020 Hi Nine. If you remove $WS_EX_NOACTIVATE, the non-focusable GUI is focusable. Link to comment Share on other sites More sharing options...
mikell Posted December 25, 2020 Share Posted December 25, 2020 I don't really understand the purpose of the thing but this seems to work Func _WinGetCaretPos() If GUIGetCursorInfo($hMy_GUI) <> null Then Return ... Professor_Bernd 1 Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 25, 2020 Author Share Posted December 25, 2020 (edited) On 12/25/2020 at 10:01 AM, mikell said: Func _WinGetCaretPos() If GUIGetCursorInfo($hMy_GUI) <> null Then Return ... Impressive 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 December 29, 2020 by Professor_Bernd Link to comment Share on other sites More sharing options...
mikell Posted December 25, 2020 Share Posted December 25, 2020 54 minutes ago, Professor_Bernd said: I think it is a bug Your analysis makes sense. Comments transmitted to a Dev Glad I could help anyway Professor_Bernd 1 Link to comment Share on other sites More sharing options...
jpm Posted December 25, 2020 Share Posted December 25, 2020 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 expandcollapse popup; 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 Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 25, 2020 Author Share Posted December 25, 2020 (edited) 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? 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 December 25, 2020 by Professor_Bernd Typo removed (hopefully). Link to comment Share on other sites More sharing options...
mikell Posted December 25, 2020 Share Posted December 25, 2020 @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 expandcollapse popup#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 Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 25, 2020 Author Share Posted December 25, 2020 @mikell Ha, ha, I set a trap for myself! 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. Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 25, 2020 Author Share Posted December 25, 2020 @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? Link to comment Share on other sites More sharing options...
jpm Posted December 26, 2020 Share Posted December 26, 2020 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? Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 26, 2020 Author Share Posted December 26, 2020 (edited) 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. 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') expandcollapse popup; 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 December 26, 2020 by Professor_Bernd Added link to the source of the workaround. mikell 1 Link to comment Share on other sites More sharing options...
jpm Posted December 26, 2020 Share Posted December 26, 2020 it always good to see an happy people nice catch 😍 Professor_Bernd 1 Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 27, 2020 Author Share Posted December 27, 2020 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 expandcollapse popup; ------------------------------------------------------------------------------ ; "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 expandcollapse popup; ------------------------------------------------------------------------------ ; _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? Link to comment Share on other sites More sharing options...
GokAy Posted December 27, 2020 Share Posted December 27, 2020 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++. Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 27, 2020 Author Share Posted December 27, 2020 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? Link to comment Share on other sites More sharing options...
GokAy Posted December 28, 2020 Share Posted December 28, 2020 (edited) 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 December 28, 2020 by GokAy Link to comment Share on other sites More sharing options...
Professor_Bernd Posted December 28, 2020 Author Share Posted December 28, 2020 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? Link to comment Share on other sites More sharing options...
GokAy Posted December 28, 2020 Share Posted December 28, 2020 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 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now