How to read the Color of a Screen Pixel

Solution 1:

This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;

namespace FormTest
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        static extern bool GetCursorPos(ref Point lpPoint);

        [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);

        public Form1()
        {
            InitializeComponent();
        }

        private void MouseMoveTimer_Tick(object sender, EventArgs e)
        {
            Point cursor = new Point();
            GetCursorPos(ref cursor);

            var c = GetColorAt(cursor);
            this.BackColor = c;

            if (c.R == c.G && c.G < 64 && c.B > 128)
            {
                MessageBox.Show("Blue");
            }
        }

        Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
        public Color GetColorAt(Point location)
        {
            using (Graphics gdest = Graphics.FromImage(screenPixel))
            {
                using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hSrcDC = gsrc.GetHdc();
                    IntPtr hDC = gdest.GetHdc();
                    int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
                    gdest.ReleaseHdc();
                    gsrc.ReleaseHdc();
                }
            }

            return screenPixel.GetPixel(0, 0);
        }
    }
}

Now, obviously, you don't have to use the cursor's current location, but this is the general idea.

EDIT:

Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:

private void PollPixel(Point location, Color color)
{
    while(true)
    {
        var c = GetColorAt(location);

        if (c.R == color.R && c.G == color.G && c.B == color.B)
        {
            DoAction();
            return;
        }

        // By calling Thread.Sleep() without a parameter, we are signaling to the
        // operating system that we only want to sleep long enough for other
        // applications.  As soon as the other apps yield their CPU time, we will
        // regain control.
        Thread.Sleep()
    }
}

You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.

Solution 2:

Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);

public static Color GetColorAt(int x, int y)
{
    IntPtr desk = GetDesktopWindow();
    IntPtr dc = GetWindowDC(desk);
    int a = (int) GetPixel(dc, x, y);
    ReleaseDC(desk, dc);
    return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}

I think this is the cleanest and quickest way.

Note:

If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.