How to Draw Directly on the Windows Desktop, C#

How to draw directly on the Windows desktop, C#?

Try the following:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;

class Program {

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

[DllImport("User32.dll")]
static extern int ReleaseDC(IntPtr hwnd, IntPtr dc);

static void Main(string[] args) {
IntPtr desktop = GetDC(IntPtr.Zero);
using (Graphics g = Graphics.FromHdc(desktop)) {
g.FillRectangle(Brushes.Red, 0, 0, 100, 100);
}
ReleaseDC(IntPtr.Zero, desktop);
}
}

Drawing on the Desktop using C#

probably windows would have refreshed your screen and that's why you don't see the rectangle in your screen.

The below suggestion may not be a perfect solution, but it may help you to get your code to work.

Add a paint handler to your form as @vivek verma suggested and move your code inside this paint handler

private void Form1_Paint(object sender, PaintEventArgs e)
{
IntPtr desktop = GetDC(IntPtr.Zero);
using (Graphics g = Graphics.FromHdc(desktop))
{
g.FillRectangle(Brushes.Red, 0, 0, 100, 100);
}
ReleaseDC(IntPtr.Zero, desktop);
}

This will make the rectangle being redrawn in your screen when your form will be repainted. But still remember that your drawing on the screen will be gone when the screen is refreshed by windows.

EDIT:
There is also a good post here draw on screen without form that suggests an alternate solution of using borderless form.

How to effectively draw on desktop in C#?

When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.

There are a couple of options, depending upon what you want to achieve:

  1. create a borderless, possibly
    non-rectangular window. (Use
    SetWindowRgn to make a window
    non-rectangular.) You can make this a child of the desktop window.
  2. subclass the desktop window. This is not straightforward, and involves
    injecting a DLL into the
    Explorer.exe process.

How to draw directly on the screen in C#? (Example causes PInvokeStackImbalance)

You could create a top level window that does not have any chrome and then push it to the back of the z-order for the current set of top level windows. Then you would draw into the client area of that window just like any other C# window.

C# drawing on windows

As said in the comments, you cannot draw on the screen directly.
What you can do is build some "overlay" window (transparent and click-through) and draw on it.

Here is a C# Console app sample that demonstrates that and also uses UI Automation that track opened windows and draw a yellow rectangle around them.

static class Program
{
[STAThread]
static void Main()
{
var overlay = new Overlay();

// track windows open (requires WindowsBase, UIAutomationTypes, UIAutomationClient)
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Subtree, (s, e) =>
{
var element = (AutomationElement)s;
if (element.Current.ProcessId != Process.GetCurrentProcess().Id)
{
Console.WriteLine("Added window '" + element.Current.Name + "'");
overlay.AddTrackedWindow(element);

// track window close
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, element, TreeScope.Element, (s2, e2) =>
{
overlay.RemoveTrackedWindow(element);
});
}
});

Application.Run(overlay);
}
}

// adapted from https://stackoverflow.com/questions/11077236/transparent-window-layer-that-is-click-through-and-always-stays-on-top
public class Overlay : Form // standard Windows Form
{
private readonly HashSet<AutomationElement> _windows = new HashSet<AutomationElement>();

public Overlay()
{
TopMost = true;
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
BackColor = Color.White;
TransparencyKey = BackColor;
}

protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
const int WS_EX_TRANSPARENT = 0x20;
const int WS_EX_LAYERED = 0x80000;
const int WS_EX_NOACTIVATE = 0x8000000;
cp.ExStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
return cp;
}
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);

foreach (var window in _windows.ToArray())
{
Rect rect;
try
{
rect = window.Current.BoundingRectangle;
}
catch
{
// error, window's gone
_windows.Remove(window);
continue;
}

// draw a yellow rectangle around window
using (var pen = new Pen(Color.Yellow, 2))
{
e.Graphics.DrawRectangle(pen, (float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height);
}
}
}

// ensure we call Invalidate on UI thread
private void InvokeInvalidate() => BeginInvoke((Action)(() => { Invalidate(); }));

public void RemoveTrackedWindow(AutomationElement element)
{
_windows.Remove(element);
InvokeInvalidate();
}

public void AddTrackedWindow(AutomationElement element)
{
_windows.Add(element);
InvokeInvalidate();

// follow target window position
Automation.AddAutomationPropertyChangedEventHandler(element, TreeScope.Element, (s, e) =>
{
InvokeInvalidate();
}, AutomationElement.BoundingRectangleProperty);
}
}

To test it, run it, and open notepad for example. This is what you should see:

Sample Image

Draw on the screen without a form

Method 1: Call the Windows API

You need System.Drawing and System.Runtime.InteropServices. You may need to add project references to them.

using System.Runtime.InteropServices;
using System.Drawing;

Add the methods to your class with P/Invoke

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

Get a Graphics object for the entire screen and draw a rectangle with it:

IntPtr desktopPtr = GetDC(IntPtr.Zero);
Graphics g = Graphics.FromHdc(desktopPtr);

SolidBrush b = new SolidBrush(Color.White);
g.FillRectangle(b, new Rectangle(0, 0, 1920, 1080));

g.Dispose();
ReleaseDC(IntPtr.Zero, desktopPtr);

The problem with this method is that if the screen refreshes at all, the rectangle will be overwritten, making it useless for most practical applications.

Method 2: Create a borderless form

As before, you need a project reference. This time to System.Windows.Forms. You'll also need System.Drawing again:

using System.Drawing;
using System.Windows.Forms;

Make the new form, remove its borders, fill the screen with it, and put it on top of the taskbar:

Form f = new Form();
f.BackColor = Color.White;
f.FormBorderStyle = FormBorderStyle.None;
f.Bounds = Screen.PrimaryScreen.Bounds;
f.TopMost = true;

Application.EnableVisualStyles();
Application.Run(f);

A possible issue with this is that the user can just alt+tab away from the window. If you want to do any more complicated graphics, you'll need to write some drawing code like this. To make the form background transparent, set its TransparentKey to the same as its Backolor.

I've just tested both of these in .NET 4.5 and Windows 7, so it may be different for earlier versions. More information here and here.



Related Topics



Leave a reply



Submit