ClientToScreen API functions returns not the expected point

0

I'm currently designing a function that searches a subpicture on a picture of a window. If the search was successful, it clicks on the center of this subpicture. Strangely, it always clicks a below the expected position.

The search algorithm works correct - I still checked it. The provided coordinates for the function are correct. But the function ClientToScreen works strange. For example:

My subpicture was found at x = 352, y = 70 - the coordinates are relative to the left upper corner of the window. The left upper corner of my window is at x = 91, y = 303 relative to the screen. So, I expected for the coordinates to click on, relative to the screen as follows:

X_Click = Window.Left + X_Click_rel2Wnd = 352 + 91= 443;
Y_Click = Window.Top + Y_Click_rel2Wnd = 70 + 303 = 373;

The function returns: 447 / 396

It looks like this picture shows: enter image description here

Anybody has an idea, whats wrong?

Below the code:

public static class ClickOnPointTool{
private const uint MOUSEEVENTF_LEFTDOWN = 0x02;
private const uint MOUSEEVENTF_LEFTUP = 0x04;
private const uint MOUSEEVENTF_RIGHTDOWN = 0x08;
private const uint MOUSEEVENTF_RIGHTUP = 0x10;
private const uint MOUSEEVENTF_MIDDLEDOWN = 0x00000020;
private const uint MOUSEEVENTF_MIDDLEUP = 0x00000040;

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct TPoint{
    public int X;
    public int Y;
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool ClientToScreen(System. IntPtr hWnd, ref TPoint lpPoint);

#pragma warning disable 649
internal struct INPUT{
    public System.UInt32 Type;
    public MOUSEKEYBDHARDWAREINPUT Data;
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
internal static extern uint SendInput(uint nInputs, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPArray), System.Runtime.InteropServices.In] INPUT[] pInputs, int cbSize);

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
internal struct MOUSEKEYBDHARDWAREINPUT{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public MOUSEINPUT Mouse;
}

internal struct MOUSEINPUT{
    public System.Int32 X;
    public System.Int32 Y;
    public System.UInt32 MouseData;
    public System.UInt32 Flags;
    public System.UInt32 Time;
    public System.IntPtr ExtraInfo;
}

#pragma warning restore 649
public static void ClickOnPoint(System.IntPtr wndHandle, int x, int y, bool KeepCursor = false, bool RightButton = false, bool DoubleClick = false, System.Data.DataTable WndBoundings = null){
    int nTimes = 0;
    TPoint clientPoint;
    clientPoint.X = x;
    clientPoint.Y = y;

    System.Drawing.Point oldPos = System.Windows.Forms.Cursor.Position;

    //System.Windows.Forms.MessageBox.Show("Click to " + clientPoint.X + " / " + clientPoint.Y + "");
    if(DoubleClick){nTimes = 2;}else{nTimes = 1;}

    /// get screen coordinates
    if(WndBoundings == null){
        ClientToScreen(wndHandle, ref clientPoint);
    }else{
        clientPoint.X += (int)WndBoundings.Rows[0]["Left"];
        clientPoint.Y += (int)WndBoundings.Rows[0]["Top"];
    }
    //System.Windows.Forms.MessageBox.Show("Click to " + clientPoint.X + " / " + clientPoint.Y + "");

    /// set cursor on coords, and press mouse
    System.Windows.Forms.Cursor.Position = new System.Drawing.Point(clientPoint.X, clientPoint.Y);

    INPUT inputMouseDown = new INPUT();
    inputMouseDown.Type = 0; /// input type mouse
    if(RightButton){
        inputMouseDown.Data.Mouse.Flags = MOUSEEVENTF_RIGHTDOWN; ///right button down
    }else{
        inputMouseDown.Data.Mouse.Flags = MOUSEEVENTF_LEFTDOWN; /// left button down
    }
    INPUT inputMouseUp = new INPUT();
    inputMouseUp.Type = 0; /// input type mouse
    if(RightButton){
        inputMouseUp.Data.Mouse.Flags = MOUSEEVENTF_RIGHTUP; /// right button up
    }else{
        inputMouseUp.Data.Mouse.Flags = MOUSEEVENTF_LEFTUP; /// left button up
    }

    var inputs = new INPUT[] { inputMouseDown, inputMouseUp };

    for(int i=0;i<nTimes;i++){
        SendInput((uint)inputs.Length, inputs, System.Runtime.InteropServices.Marshal.SizeOf(typeof(INPUT)));
    }

    /// return mouse 
    if(!KeepCursor){System.Windows.Forms.Cursor.Position = oldPos;}
}

}

c#
winapi
asked on Stack Overflow Dec 1, 2017 by Jan021981 • edited Dec 1, 2017 by jsanalytics

1 Answer

1

Thank you all. This runs well:

    private static System.Drawing.Point GetWindowCornerLU(System.IntPtr hWnd){
    if(hWnd == System.IntPtr.Zero){throw new System.Exception("ERROR: Window handle is not referenced!");}

    WindowHandle.User32.Rect rect = new WindowHandle.User32.Rect();
    WindowHandle.User32.GetWindowRect(hWnd, ref rect);
    return new System.Drawing.Point(rect.left, rect.top);
}

#pragma warning restore 649
public static void ClickOnPoint(System.IntPtr wndHandle, int x, int y, bool KeepCursor = false, bool RightButton = false, bool DoubleClick = false){
    int nTimes = 0;
    TPoint clientPoint;
    clientPoint.X = x;
    clientPoint.Y = y;

    System.Drawing.Point oldPos = System.Windows.Forms.Cursor.Position;

    //System.Windows.Forms.MessageBox.Show("Click to " + clientPoint.X + " / " + clientPoint.Y + "");
    if(DoubleClick){nTimes = 2;}else{nTimes = 1;}

    /// get screen coordinates
    //ClientToScreen(wndHandle, ref clientPoint);
    System.Drawing.Point prtLU = GetWindowCornerLU(wndHandle);
    clientPoint.X += prtLU.X;
    clientPoint.Y += prtLU.Y;
    //System.Windows.Forms.MessageBox.Show("Click to " + clientPoint.X + " / " + clientPoint.Y + "");

    /// set cursor on coords, and press mouse
    System.Windows.Forms.Cursor.Position = new System.Drawing.Point(clientPoint.X, clientPoint.Y);

    INPUT inputMouseDown = new INPUT();
    inputMouseDown.Type = 0; /// input type mouse
    if(RightButton){
        inputMouseDown.Data.Mouse.Flags = MOUSEEVENTF_RIGHTDOWN; ///right button down
    }else{
        inputMouseDown.Data.Mouse.Flags = MOUSEEVENTF_LEFTDOWN; /// left button down
    }
    INPUT inputMouseUp = new INPUT();
    inputMouseUp.Type = 0; /// input type mouse
    if(RightButton){
        inputMouseUp.Data.Mouse.Flags = MOUSEEVENTF_RIGHTUP; /// right button up
    }else{
        inputMouseUp.Data.Mouse.Flags = MOUSEEVENTF_LEFTUP; /// left button up
    }

    var inputs = new INPUT[] { inputMouseDown, inputMouseUp };

    for(int i=0;i<nTimes;i++){
        SendInput((uint)inputs.Length, inputs, System.Runtime.InteropServices.Marshal.SizeOf(typeof(INPUT)));
    }

    /// return mouse 
    if(!KeepCursor){System.Windows.Forms.Cursor.Position = oldPos;}
}

}

answered on Stack Overflow Dec 1, 2017 by Jan021981

User contributions licensed under CC BY-SA 3.0