finding and clicking on a color

ive been trying to code a program that will look for a color, which is hard coded into the program at the moment, move the mouse to that point when its found and then click. this code compiles....but doesnt function. please help!

p.s. the resolution of my computer is also hard coded


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <conio.h>
#include <iostream>
#include <windows.h>
#include <stdlib.h>

using namespace std;
 int main(){
     int x,y;
         x=1;
         y=1;
         start:
         cout<<x+","+y;
         x += 3;
         if(x==1366){ y += 3;
         }
         if(x==1366){ x = 1;
         }
         if(y==768){ y = 1;
         }
         Sleep(5);
             HDC hdc = GetDC(NULL);
             DWORD color = GetPixel(hdc, x, y);
             unsigned int r = GetRValue(color);
             unsigned int g = GetGValue(color);
             unsigned int b = GetBValue(color);
             if( r == 95 ){
                 if(g == 103){
                     if(b == 65){
                         COORD p = { x, y };
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), p );
    mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
    Sleep(100000);
    goto start;
                     }
                     else{
                     goto start;}
                  }
                  else{
                  goto start;}
              }
              else{
              goto start;}
cin.get();
cin.ignore();
return 0;
 }
Last edited on
The find code works for me (I looked for black, RGB(0, 0, 0), rather than RGB(95, 103, 65).)

(Are you sure you have RGB(95, 103, 65) on your screen? You could screen cap to Paint, or some other bitmap editor, and check the RGB value is what you expect.)

But the coordinates used by the GDI and the Console APIs are not the same: GDI's GetPixel is working with (well) pixels; the Console SetConsoleCursorPosition call uses character rows and columns. So you need to convert the GDI coordinates into the correct row and column for the console.

I've never had the never to do this, nor have I come across a neat way to do it. If you don't actually have to move the console cursor, you could move the mouse instead -- mouse_event with MOUSEEVENTF_MOVE (this might be necessary whether or not you move the console cursor.)

But while I'm here:

#0 mouse_event has been superseded by SendInput. You should use the newer function instead.

#1 I had to tweak the cout line to

cout<<x+","+y;

(C++ does not know how to add integers and strings, or even how to add two C-style strings. The << verions says output x, then output ",", etc.)

#2 goto is bad -- you should rework your code to use for-loops. For information about them, see:

Control Structures -- The for loop
http://www.cplusplus.com/doc/tutorial/control/#for

You will end up with less code if you do this.

#3 The coords of the screen run from 0 to N-1, where N is the width or height. So for a 1280 x 1024 mode, the pixels run from (0, 0) to (1279, 1023)

#4 This

1
2
3
4
5
6
7
if( r == 95 ){
if(g == 103){
if(b == 65){
// do stuff
}
}
}


can be better written as

1
2
3
if( r == 95 && g == 103 && b == 65 ){
    // do stuff
}


or even (if you like to make the grouping more obvious)

1
2
3
if( (r == 95) && (g == 103) && (b == 65) ){
    // do stuff
}


(C++ uses short circuit evaluation, from left to right, so it will not bother to evaluate g == 103 or b == 65 if r is not 95.)

But in this case, rather than breaking up the value returned by GetPixel and testing it color by color, you could create an RGB value to compare with the value returned.

1
2
3
4
5
6
7
8
9
10
11
// COLORREF is a typedef of DWORD provided for this kind of use

COLORREF colorWanted = RGB(95, 103, 65);

// etc

COLORREF color = GetPixel(hdc, x, y);

if(color == colorWanted)
{
    // etc 


Andy

PS As it looks like you're new here...

Please use code tags --> the <> button below the new post box or to the right of the post edit box. As well as looking nice, it provides line numbers which make it easier to refer to posted code.

1
2
3
4
5
6
7
8
#include <iostream>
using namespace std;

int main() {
    cout << "Hello nicely formatted world!\n";

    return 0;
}


And a couple of articles:

How to use code tags
http://www.cplusplus.com/articles/jEywvCM9/

How to use tags
http://www.cplusplus.com/articles/z13hAqkS/
(for code and other types of tag)

You can even go back and add tags to your earlier posts...
Last edited on
Thank you for your reply! I found this very helpful.
I'm under the impression that my biggest issue is the coordinate system.
I'm still pretty new to c++ but i do know how to properly use for while and other
loop statements, this is just a prime example of lazy coding.
Also as you guessed i am new to this forum.

I'll clean it up and post it in the comments to see what you think.

p.s. If i use the mouse event to move the cursor does it bypass the need for
a conversion between the GDI and the console API

Thanks,
Tigerlad
Last edited on
If i use the mouse event to move the cursor does it bypass the need for
a conversion between the GDI and the console API

Yes. But you prob have to scale it.

I'm not sure how to do it with mouse_event, I've always used SendInput (which replaced the older call when Windows 2000 came out, so it's a while ago.)

With SendInput you scale the coords like this:

1
2
3
4
5
6
7
8
9
10
11
	double x = ptClick.x * (65535.0/static_cast<double>(m_sizeDesktop.cx));
	double y = ptClick.y * (65535.0/static_cast<double>(m_sizeDesktop.cy));

	INPUT Input={0};

	Input.type  = INPUT_MOUSE;
	Input.mi.dx = static_cast<long>(x);
	Input.mi.dy = static_cast<long>(y);

	Input.mi.dwFlags = MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE;
	::SendInput(1,&Input,sizeof(INPUT));


This is from a bit test code I have lying about. SIZE m_sizeDesktop is the screen width and height, as you could no doubt guess. POINT ptClick is the point I want to click, in GDI coords.

Andy

PS I've now checked MSDN; the entry for mouse_event suggests it's the same for the newer function.

PPS The code above is from a helper class I use in some of my GUI tests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once

// MouseControl.h

class CMouseControl
{
private:
	CSize m_sizeDesktop;

public:
	CMouseControl(const CSize& sizeDesktop)
	: m_sizeDesktop(sizeDesktop)
	{
	}

	void MouseMove(const CPoint& ptClick);
	void MouseLeftClick(const CPoint& ptClick, DWORD dwPause);
	void MouseRightClick(const CPoint& ptClick, DWORD dwPause);
};


where CSize and CPointer class wrapped of SIZE and POINT.
Last edited on
im still not get getting a completion of the if statement, i know this because the
mouse isnt moving and the sleep statement isnt being reached.

here is the code i have thus far

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <conio.h>
#include <iostream>
#include <windows.h>
#include <stdlib.h>

using namespace std;
 int main(){
     int x,y,r,g,b;
     x=1;
     y=1;
     int hrz = 0;
     int vert = 0;
            RECT desktop;
            const HWND hDesktop = GetDesktopWindow();
            GetWindowRect(hDesktop, &desktop);
            hrz = desktop.right;
            vert = desktop.bottom;
            cout<<"R value=";
            cin>>r;
            cout<<endl<<"G value=";
            cin>>g;
            cout<<endl<<"B value=";
            cin>>b;

    while(1==1){
         cout<<x<<y;
         x += 3;
         if(x==hrz-1){ y += 3;
         }
         if(x==hrz-1){ x = 1;
         }
         if(y==vert-1){ y = 1;
         }
             HDC hdc = GetDC(NULL);
             COLORREF color = GetPixel(hdc, x, y);
             COLORREF colorWanted = RGB( r , g , b );
             if(color == colorWanted){
                    mouse_event(MOUSEEVENTF_MOVE,x,y,0,0);
                    mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
                    Sleep(100000);
                    }
    }
return 0;
}


I still havent figured out how to use SendInput but im doing research.
mouse_event, like SendInput, needs the coords to be scaled. From remarks in MSDN entry:

If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. The event procedure maps these coordinates onto the display surface. Coordinate (0,0) maps onto the upper-left corner of the display surface, (65535,65535) maps onto the lower-right corner.

mouse_event function
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646260%28v=vs.85%29.aspx

So

mouse_event(MOUSEEVENTF_MOVE,x,y,0,0);

needs to be replaced by something like

1
2
3
4
DWORD x_mouse = (DWORD)((double)x * (65535.0/(double)hrz));
DWORD y_mouse = (DWORD)((double)y * (65535.0/(double)vert));

mouse_event(MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE,x_mouse,y_mouse,0,0);


Andy
Last edited on
Topic archived. No new replies allowed.