Simple .NET HEX PixelColor Utility

A simple .NET Pixel Color Utility to get a color and copy to clipboard

For a long time I was using utilities like DotColor to show the color under the mouse. This is useful where for example you have to set an HTML element's color the same as a color on an image, and so on. But when Vista and Windows 7 came around, almost every one of these types of utilities refused to work - they would freeze the desktop and you would have to kill them from Task Manager - assuming you could get it to come up.

Frustration is often the mother of invention - so a couple of days ago I set out to build my own. I knew what I wanted - a small Windows Form with a graphic panel that would show the color under the mouse pointer, and also show the HEX (.e.g #FFFFFF) value in a label. Finally, I wanted to be able to press a "hotkey" and this would be copied to the clipboard so I could paste it into my other work.

It turns out that with some OPC ("Other People's Code") and a minimum amount of work, I had what I wanted.
The frst part is easy. We need to use P/Invoke to gain access to the GetPixel, GetDC (device context) and ReleaseDC Win32 functions:

[DllImport("Gdi32.dll")]
public static extern int GetPixel(
System.IntPtr hdc, // handle to DC
int nXPos, // x-coordinate of pixel
int nYPos // y-coordinate of pixel
);

[DllImport("User32.dll")]
public static extern IntPtr GetDC( IntPtr wnd );

[DllImport("User32.dll")]
public static extern void ReleaseDC( IntPtr dc );

With that done, we need a timer that will grab the current pixel under the mouse at say, 100ms intervals:

private void timer1_Elapsed( object sender, System.Timers.ElapsedEventArgs e )
{
Point p = Control.MousePosition;
IntPtr dc = GetDC(IntPtr.Zero);
this.panel1.BackColor = ColorTranslator.FromWin32(GetPixel(dc, p.X, p.Y));
this.label1.Text = ColorUtil.ColorToHexString(panel1.BackColor);
ReleaseDC(dc);
}

Then, we need to be able to translate that Color struct to a HEX String:


using System;
using System.Drawing;
namespace pixelcolor
{
public class ColorUtil
{
#region -- Data Members --
static char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
#endregion

public ColorUtil()
{
}

/// <summary>
/// Convert a .NET Color to a hex string.
/// </summary>
/// <returns>ex: "FFFFFF", "AB12E9"</returns>
public static string ColorToHexString( Color color )
{
byte[] bytes = new byte[3];
bytes[0] = color.R;
bytes[1] = color.G;
bytes[2] = color.B;
char[] chars = new char[bytes.Length * 2];
for (int i = 0; i < bytes.Length; i++)
{
int b = bytes[i];
chars[i * 2] = hexDigits[b >> 4];
chars[i * 2 + 1] = hexDigits[b & 0xF];
}
return new string(chars);
}
}
}

The final piece of the puzzle is a global keboard hook - we need to be able to press a key when the focus is not in our application, and still get access to the KeyUp event. For that I used George Mamaladze's managed "UserActivity" code. We just set our KeyUp delegate:

public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);
HookManager.KeyUp += new KeyEventHandler(HookManager_KeyUp);
}

void HookManager_KeyUp( object sender, KeyEventArgs e )
{
if(e.KeyCode ==Keys.X)
Clipboard.SetText("#" +this.label1.Text);
}

So if the key pressed is "X", we stick the HEX color into the clipboard, and that's it! If you do not want to use George's entire library, you can use the sample code found at PInvoke.net here. Here is an alternate implementation using the code from PInvoke.net:

using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace pixelcolor
{
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
public partial class Form1 : System.Windows.Forms.Form
{
delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
HookProc myCallbackDelegate = null;

[DllImport("Gdi32.dll")]
public static extern int GetPixel(
System.IntPtr hdc, // handle to DC
int nXPos, // x-coordinate of pixel
int nYPos // y-coordinate of pixel
);

[DllImport("User32.dll")]
public static extern IntPtr GetDC( IntPtr wnd );

[DllImport("User32.dll")]
public static extern void ReleaseDC( IntPtr dc );

private System.Windows.Forms.Panel panel1;
private System.Timers.Timer timer1;
private Label label1;

private System.ComponentModel.Container components = null;

[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadID);

[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.myCallbackDelegate = new HookProc(this.MyCallbackFunction);
// setup a keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD, this.myCallbackDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
}

public int MyCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0)
{
//you need to call CallNextHookEx without further processing
//and return the value returned by CallNextHookEx
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
// we can convert the 2nd parameter (the key code) to a System.Windows.Forms.Keys enum constant
Keys keyPressed = (Keys)wParam.ToInt32();
if( keyPressed ==Keys.X )
Clipboard.SetText("#" + this.label1.Text);
//return the value returned by CallNextHookEx
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}

protected override void Dispose( bool disposing )
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.timer1 = new System.Timers.Timer();
this.label1 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
this.SuspendLayout();
//
// panel1
//
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.panel1.Location = new System.Drawing.Point(30, 12);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(64, 56);
this.panel1.TabIndex = 0;
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.SynchronizingObject = this;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(27, 71);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(0, 13);
this.label1.TabIndex = 1;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(116, 114);
this.Controls.Add(this.label1);
this.Controls.Add(this.panel1);
this.Name = "Form1";
this.Text = "PixelColor";
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion

[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void timer1_Elapsed( object sender, System.Timers.ElapsedEventArgs e )
{
Point p = Control.MousePosition;
IntPtr dc = GetDC(IntPtr.Zero);
this.panel1.BackColor = ColorTranslator.FromWin32(GetPixel(dc, p.X, p.Y));
this.label1.Text = ColorUtil.ColorToHexString(panel1.BackColor);
ReleaseDC(dc);
}
}
}

The above example uses a lot less code, and can be compiled into a single executable that weighs in at just 11K!

This little utility, while not so elegant, is simple enough to run on any Windows OS without giving you grief. It comes up fast, and it won't jam up your machine. Download here and enjoy.

By Peter Bromberg   Popularity  (2615 Views)