Control Manager: Coordinates of a window (aka, control).

Started by snowsnowsnow, June 20, 2019, 03:20:13 AM

Previous topic - Next topic

snowsnowsnow

Here's a problem I ran into recently.  Suppose you have a window handle (derived from code originally produced by RoboScripter) and you want to put the mouse on that window/control.  I figure the information about the location of the control must be stored somewhere (inside Windows), but I know of no way in Control Manager to get it.

To explain further.  I have:

ContrlHandle = cWnd...(...)

Now, I can, for example, set the focus to this window by cSetFocus(ControlHandle), but what I want to do is to click on it (simulate mouse click).

It seems like something like:

MousePlay("5 5",cWinIdConvert(ControlHanlde),"",@MPLAYLCLK,0)

ought to get me somewhere, but I could not get this to work.

So, alternatively, if I could get the coordinates (i.e., know where on the screen the control is located) of the control, then I could use that information in the MousePlay() command.

Is there a way?

td

There are a couple of issues with your strategy.  The first is that you can't place a child window ID in the parent window parameter of MousePlay.  The function uses different search methods for parent and child windows.  This is largely dictated by the Win32 APIs.  The second and more problematic issue is that your old version of the MousePlay is a bit buggy.  It was completely written about 6-8 years ago.

I presume there is a reason why you don't want to simply send a button click message to your button using Control Manager functions so perhaps consider using some combination of WinPosition, WinPositionChild, MouseMove, and MouseClick.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

snowsnowsnow

Quote from: td on June 20, 2019, 09:02:14 AM
There are a couple of issues with your strategy.  The first is that you can't place a child window ID in the parent window parameter of MousePlay.  The function uses different search methods for parent and child windows.  This is largely dictated by the Win32 APIs.  The second and more problematic issue is that your old version of the MousePlay is a bit buggy.  It was completely written about 6-8 years ago.

I presume there is a reason why you don't want to simply send a button click message to your button using Control Manager functions so perhaps consider using some combination of WinPosition, WinPositionChild, MouseMove, and MouseClick.

1) Yes, I realize that looks weird.  I tried lots of different combinations; none of them worked.  My general feeling (which may be wrong) is that the various "Get Window Position" functions don't really work with Window IDs (as returned by cWinIdConvert()).

2) (Re: new version of MosuePlay) Yep, that could be true.

3) It's not a button.  This is kind of a weird application - that does a lot of its own thing in terms of controls and stuff.  I need to click in a certain part of the screen and then send keys to that region.  Of course, my first thought was to use cSetEditText() rather than SendKeys, but that doesn't work.  It in fact sets the text, but the application does not recognize that you've changed it; it doesn't act on the new value.  It only works if you Click, Type, hit Enter.

4) I tried using WinPositionChild, like this:

topid = WinIdGet("My Top level window")
childhand = cWnd...(...)

x = WinPositionChild(topid,cWinIdConvert(childhand))

but it didn't like that (error message about child window not found).

5) If I could just get the screen position of the child window, I'd be good to go.  As it is, I worked around it by figuring out the position of the child window (by trial and error) and then hard-coding that value in the script (in MousePlay()), which works OK, but obviously ain't great.

It works, right up until the window gets moved or resized...

td

Quote from: snowsnowsnow on June 20, 2019, 01:42:19 PM

1) Yes, I realize that looks weird.  I tried lots of different combinations; none of them worked.  My general feeling (which may be wrong) is that the various "Get Window Position" functions don't really work with Window IDs (as returned by cWinIdConvert()).

WIL window ids as returned by CWinIdConvert do work with WIL window position function. However, that statement only applies to top-level windows. The function WinPositionChild only works with a window's text in the child window parameter.  Not sure why that is.  Unless there is some underlying reason for why child windows cannot be specified using a window id, adding that functionality could be an enhancement for a future release.

Quote
2) (Re: new version of MosuePlay) Yep, that could be true.

Know from first-hand experience that MousePlay had issues that were corrected in WinBatch 2013.

Quote
3) It's not a button.  This is kind of a weird application - that does a lot of its own thing in terms of controls and stuff.  I need to click in a certain part of the screen and then send keys to that region.  Of course, my first thought was to use cSetEditText() rather than SendKeys, but that doesn't work.  It in fact sets the text, but the application does not recognize that you've changed it; it doesn't act on the new value.  It only works if you Click, Type, hit Enter.

Don't know why I assumed button. My apologies.   CSetEditText changes the text by simply sending a WM_SETTEXT message to the window. Your window could be a user32/common control, loosely based on a user32/common control, or a roll-your-own.  Obviously, that means dealing with the window may entail some trial-and-error.  There can be two copies of the text associated with a window.  One set when the window is originally created and another set by the application after the window is originally created. Which text you get depends on how you request the text.  However, that usually only applies to parent or top-level windows and not child windows.     

Quote
4) I tried using WinPositionChild, like this:

topid = WinIdGet("My Top level window")
childhand = cWnd...(...)

x = WinPositionChild(topid,cWinIdConvert(childhand))

but it didn't like that (error message about child window not found).

Unfortunately, expected behavior per above.

Quote
5) If I could just get the screen position of the child window, I'd be good to go.  As it is, I worked around it by figuring out the position of the child window (by trial and error) and then hard-coding that value in the script (in MousePlay()), which works OK, but obviously ain't great.

It works, right up until the window gets moved or resized...

If you are really ambitious you could use a couple of DllCalls to get the pixel screen coordinates of your child window,  optionally convert them to client area coordinates of the parent window, and then to 1000X1000.  You could then pass the results of your calculation to one or more of the mouse functions.  This Tech Database example has a UDP for obtaining the pixel coordinance:

https://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WIL~Extenders/Control~Manager/EXAMPLES~FROM~USERS+AnalysisEx.txt

Have you tried using cSetFocus to place the input focus on your target and then appending a @CRLF, @LF, or @CR to the text you send to it?  If not, it probably will not work but might be worth a try.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

A very rough example that more or less illustrates one approach:

Code (winbatch) Select
AddExtender("wwctl44i.dll")

ShellExecute("WinBatch.exe", "", '', @normal,'')
WinWaitExist("~WinBatch Navigator (32-bit)",5)
WinActivate("~WinBatch Navigator (32-bit)")
hWb=DllHwnd('~WinBatch Navigator (32-bit)')
hHelpBut=cWndByID(hWb,125)

RECT = BinaryAlloc(16)
DllCall("User32.DLL", long:"GetWindowRect", long:hHelpBut, lpbinary:RECT)
nX = BinaryPeek4(RECT, 0)
nY = BinaryPeek4(RECT, 4)
BinaryFree(RECT)
nVx = DllCall("kernel32.dll",long:"MulDiv",long:nX,long:1000,long:WinMetrics(0)) + 2
nVy = DllCall("kernel32.dll",long:"MulDiv",long:nY,long:1000,long:WinMetrics(1)) + 2
                                                                                               
MousePlay(nVx:' ':nVy, '','', @MPLAYLCLK, 2) ; Delay to see it move.
exit
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

snowsnowsnow

We have a winner!

I UDF'ized your program and it works perfectly in my (funky) app.

This is why I love this board!  Thanks.

Here's the code:


Code (winbatch) Select


#DefineFunction udfHandToLoc(theHand)
RECT = BinaryAlloc(16)
DllCall("User32.DLL", long:"GetWindowRect", long:theHand, lpbinary:RECT)
nX = BinaryPeek4(RECT, 0)
nY = BinaryPeek4(RECT, 4)
BinaryFree(RECT)
nVx = DllCall("kernel32.dll",long:"MulDiv",long:nX,long:1000,long:WinMetrics(0)) + 2
nVy = DllCall("kernel32.dll",long:"MulDiv",long:nY,long:1000,long:WinMetrics(1)) + 2
Return StrCat(nVx," ",nVy)
#EndFunction

; MousePlay(udfHandToLoc(cWndBySeq(ControlHandle,Param1)),"","",@MPLAYLCLK,0)