1

I am trying to simulate a mouse click on the CView window in a legacy code which I must say I don't fully understand. The idea is to search for a particular item in the CView, get its co-ordinates and then simulate a right mouse click on it using SendInput. I want to understand if the basic steps I am following are correct before I proceed digging further into the legacy code which has a bunch of transformations happening across co-ordinate systems :( Here are the steps I follow:

  1. Get the position co-ordinates of the item displayed in CView. at this point the co-ordinates is in the internal co-ordinate system (lets call it CDPoint).

    CDPoint gPosn = viewObj->m_point_a ;

  2. Covert the co-ordinates to the client co-ordinate system i.e CDPoint to CPoint using the existing transformations in the code.

    CPoint newPosn = GetTransform().Scale(gPosn);

//Note: The basis of arriving that this is the correct transformation to use is the below code with the exact reverse transform happening in the mouse click handler code to convert CPoint to CDPoint:

 `CDesignView::OnLButtonDown(UINT nFlags, CPoint p) {
      CDPoint np = GetTransform().DeScale(p);
 }`

Is this thinking right that CPoint received in the OnLButtonDown() handler will always be in the client co-ordinates and hence the reverse transform should convert CDPoint (internal co-ordinates) to client coordinates (CPoint) ?

  1. Convert client co-ordinates to screen co-ordinates:

    ClientToScreen(&newPosn);

  2. Pass these values to SendInput function after converting to pixel co-ordinates:

    INPUT buffer[1]; MouseSetup(buffer); MouseMoveAbsolute(buffer, newPos.x, newPos.y); MouseClick(buffer);

  3. The Mousexxx() functions are defined as below similar to the sample code in this post: How to simulate a mouse movement

.

#define SCREEN_WIDTH (::GetSystemMetrics( SM_CXSCREEN )-1) 
#define SCREEN_HEIGHT (::GetSystemMetrics( SM_CYSCREEN )-1) 

static void inline makeAbsXY(double &x, double &y) { 
    x = (x * 0xFFFF) / SCREEN_WIDTH ; 
    y = (y * 0xFFFF) / SCREEN_HEIGHT ; 
}

static void inline MouseSetup(INPUT *buffer)
{
    buffer->type = INPUT_MOUSE;
    buffer->mi.dx = (0 * (0xFFFF / SCREEN_WIDTH));
    buffer->mi.dy = (0 * (0xFFFF / SCREEN_HEIGHT));
    buffer->mi.mouseData = 0;
    buffer->mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
    buffer->mi.time = 0;
    buffer->mi.dwExtraInfo = 0;
}

static void inline MouseMoveAbsolute(INPUT *buffer, double x, double y)
{
    makeAbsXY(x,y) ; 
    buffer->mi.dx = x ;
    buffer->mi.dy = y ;
    buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE);
    SendInput(1, buffer, sizeof(INPUT));
}

static void inline MouseClick(INPUT *buffer)
{
    buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN);
    SendInput(1, buffer, sizeof(INPUT));
    Sleep(10);
    buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP);
    SendInput(1, buffer, sizeof(INPUT));
}

Could anyone pls provide pointers on what might be going wrong in these steps since the simulated mosue click always seem to be shifted left by some factor which keeps increasing as x becoems larger. I have verified that is gPosn is pointing to (0,0) it always simulates a mouse click on the top right corner of the client screen.

Thanks for your time.

Community
  • 1
  • 1
veekay
  • 65
  • 2
  • 7
  • If you have access to `viewObj` then you shouldn't need to fake input. Other than that, use `GetWindowRect(viewObj->m_hWnd, &rect)` to find the coordinates of the control. Use `(rc.left + 1)` and `(rc.top + 1)` for X/Y coordinates. `SendInput` should not be broken by `Sleep`. Also use `SendInput` with an array, instead of sending one message at a time. – Barmak Shemirani Sep 24 '15 at 05:27
  • Thanks Barmak. The aim is to create a test application by simulating mouse clicks on various objects in the main application. Hence the need to use SendInput and fake a mouse click. – veekay Sep 24 '15 at 09:04
  • @Barnmak: Regarding using viewObt->m_hwnd, my viewObj is not derived from a CWnd class. Probably the variable name was misleading but this object really is a handle to what is depicted on the CView window - something you would create using OnDraw or OnPaint in the CView. All it captures is the position co-ordinates but in this case that will be in internal units. Converting this internal unit to pixel coordinates is where I am stuck at. So the method you suggest will not work for me :( – veekay Sep 24 '15 at 09:04
  • 1
    I deleted my answer because I realized this is "check my code..." which is off topic. You should at least put this in form of yes or no question. Yes, your conversion for SendInput x/y value is correct. – Barmak Shemirani Sep 25 '15 at 11:22
  • @BarmakShemirani So what was the answer? Have the exact same issue (x slightly shifted). – helmesjo Dec 04 '16 at 15:43
  • @helmesjo, see below – Barmak Shemirani Dec 04 '16 at 17:37

1 Answers1

0

If you have x and y in client coordinates, you have to convert them to screen coordinates:

POINT point;
point.x = x;
point.y = y;
::ClientToScreen(m_hWnd, point);

Where m_hWnd is the window which owns the objects. x and y are relative to top-left of the client area of this window.

Assuming point.x and point.y are in screen coordinates, the rest of the conversion for SendInput is correct. You can also create INPUT array for SendInput, this will send the mouse messages without interruption.

INPUT input[3];
for (int i = 0; i < 3; i++)
{
    memset(&input[i], 0, sizeof(INPUT));
    input[i].type = INPUT_MOUSE;
}

input[0].mi.dx = (point.x * 0xFFFF) / (GetSystemMetrics(SM_CXSCREEN) - 1);
input[0].mi.dy = (point.y * 0xFFFF) / (GetSystemMetrics(SM_CYSCREEN) - 1);
input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
input[1].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
input[2].mi.dwFlags = MOUSEEVENTF_RIGHTUP;

SendInput(3, input, sizeof(INPUT));
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • Thanks Barmak for confirming the steps are correct. My concern was really in the conversion from internal co-ordinates to client co-ordinates. I realized the bug was in the code that gets the itnernal co-ordinate. My code works fine after fixing that. – veekay Sep 25 '15 at 06:40