9

I'm trying to make an auto-cliker for an windows app. It works well, but it's incredibly slow! I'm currently using the method "getPixel" which reloads an array everytime it's called.

Here is my current code:

hdc = GetDC(HWND_DESKTOP);
bx = GetSystemMetrics(SM_CXSCREEN);
by = GetSystemMetrics(SM_CYSCREEN);
start_bx = (bx/2) - (MAX_WIDTH/2);
start_by = (by/2) - (MAX_HEIGHT/2);
end_bx = (bx/2) + (MAX_WIDTH/2);
end_by = (by/2) + (MAX_HEIGHT/2);

for(y=start_by; y<end_by; y+=10)
{   
    for(x=start_bx; x<end_bx; x+=10)
    {
        pixel = GetPixel(*hdc, x, y);
        if(pixel==RGB(255, 0, 0))
        {
            SetCursorPos(x,y);
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            Sleep(50);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
            Sleep(25);
        }
    }
}

So basically, it just scan a range of pixel in the screen and starts a mouse event if it detects a red button.

I know there are other ways to get the pixel color, such as bitblt. But I've made some researches, and I don't understand how I'm supposed to do, in order to scan a color array. I need something which scans screen very fast in order to catch the button.

Could you please help me?

Thanks.

Manitoba
  • 8,522
  • 11
  • 60
  • 122
  • When your code is called? in idle time? when the user moves the mouse? There are different ways to detect a red button, using FindWindow() for example. – Adriano Repetti May 09 '12 at 12:08
  • Btw, my code is in an infinite loop. But I want to use a color catcher whereas a offset one. So, when I start my code, it just go into the loop and check for the red button. – Manitoba May 09 '12 at 12:13
  • 2
    If this code is in an infinite loop, then the problem is not `GetPixel`. It's the fact that your app is not letting other apps have any CPU time. – tenfour May 09 '12 at 12:15
  • Btw, all my stuff is in a Thread. And as I said, it works well with getPixel. i'm just looking for a better way to do that – Manitoba May 09 '12 at 12:19
  • It doesn't work well -- you said it's slow. Without profiling data, I gave the best reason it's slow. – tenfour May 09 '12 at 12:24

2 Answers2

15

I found a perfect way which is clearly faster than the GetPixel one:

HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;

while(true)
{
    hdc = GetDC(HWND_DESKTOP);
    GetWindowRect(hWND_Desktop, &rect);
            int MAX_WIDTH = rect.right;
        int MAX_HEIGHT = rect.bottom;

    hdcTemp = CreateCompatibleDC(hdc);
    BITMAPINFO bitmap;
    bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
    bitmap.bmiHeader.biWidth = MAX_WIDTH;
    bitmap.bmiHeader.biHeight = MAX_HEIGHT;
    bitmap.bmiHeader.biPlanes = 1;
    bitmap.bmiHeader.biBitCount = 32;
    bitmap.bmiHeader.biCompression = BI_RGB;
    bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
    bitmap.bmiHeader.biClrUsed = 0;
    bitmap.bmiHeader.biClrImportant = 0;
    HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
    SelectObject(hdcTemp, hBitmap2);
    BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);

    for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
    {
        red = (int)bitPointer[i];
        green = (int)bitPointer[i+1];
        blue = (int)bitPointer[i+2];
        alpha = (int)bitPointer[i+3];

        x = i / (4 * MAX_HEIGHT);
        y = i / (4 * MAX_WIDTH);

        if (red == 255 && green == 0 && blue == 0)
        {
            SetCursorPos(x,y);
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            Sleep(50);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
            Sleep(25);
        }
    }
}

I hope this could help someone else.

user1642826
  • 219
  • 1
  • 2
  • 15
Manitoba
  • 8,522
  • 11
  • 60
  • 122
  • is this checking every pixel? I don't understand your x and y assignments. – user1397417 Feb 27 '14 at 01:22
  • `X` and `Y` are the location of the pixel being reviewed. They are useless, unless you want to do an action on this pixel. If its color is full red, it will set the mouse to this pixel and do a left click. – Manitoba Feb 27 '14 at 08:59
  • 1
    Nice work, but there is something I do not understand. Why take into account byte assigned to the Alpha component of the pixel? Because as it is a screenshot, the value of alpha will always be 255, right? So, Would not it be better make the biBitCount parameter worth 24? – Delgan Apr 19 '14 at 16:49
  • You are perfectly right. The Aplha part is useless. – Manitoba Apr 19 '14 at 18:38
  • GetWindowRect with HWND_DESKTOP handle returns FALSE. need to add hWND_Desktop = GetDesktopWindow()? or use GetSystemMetrics() instead.. – xakepp35 Jun 24 '17 at 07:42
  • Why do you have to multiply the biSizeImage by 4? – Scdev Sep 07 '19 at 14:43
  • Red and blue need to be swapped: blue = (int)bitPointer[i];green = (int)bitPointer[i + 1];red = (int)bitPointer[i + 2]; – Gary Davies Oct 09 '19 at 09:42
  • Unfortunatelly it is still slow for my purporse. I want to set mouse up in the exact same time that the pixel gets a specific color. But my context is when someone is drawing on the screen. If the user is drawing much fast with their hand, the method only triggers the event after the user already passed this pixel. I need to disable the mouse in the exact moment the pixel is colored so the user can't paint the next pixels. I think there is no way to achieve it. – Eduardo M Feb 11 '20 at 18:59
0

The simple answer is that if this is the method you insist on using then there isn't much to optimize. As others have pointed out in comments, you should probably use a different method for locating the area to click. Have a look at using FindWindow, for example.

If you don't want to change your method, then at least sleep your thread for a bit after each complete screen scan.

korona
  • 2,308
  • 1
  • 22
  • 37
  • Hello, Can you tell me more about Findwindow? I'm using it like that: `mhwnd = FindWindow(NULL, "Application Window Name"); hdc = GetDC(mhwnd);` What's next? – Manitoba May 09 '12 at 14:47
  • 1
    The button you want to click probably has a window handle of its own. Try using a window inspector program such as Spy++ to find the window handle of the thing you want to click, and you can probably locate that specific item with some clever code. – korona May 09 '12 at 15:02