Draw on the Screen Without a Form

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.

Draw on multiple Screen without a form

The solution linked describes using a Display Context Handle (hdc) to get a drawing area for GDI+. An hdc is typically a windows's drawing area or the primary display. As keith mentioned in the comments, you will need to get an HDC for the 2nd display and draw to that as well.

https://msdn.microsoft.com/en-us/library/dd144947%28v=vs.85%29.aspx

Here is another question that is attempting a similar result in C++. The concepts should be pretty much the same.
How to draw over second monitor with GDIPLUS

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 draw to a pixel on the screen without a window in F#?

Here is a minimal example that uses the GetDC and SetPixel functions to do exactly the same thing as the Python example in the referenced question:

open System
open System.Runtime.InteropServices

[<DllImport("user32.dll",EntryPoint="GetDC")>]
extern IntPtr GetDC(IntPtr ptr)

[<DllImport("gdi32.dll")>]
extern uint32 SetPixel(IntPtr hdc, int X, int Y, uint32 crColor);

let dc = GetDC(IntPtr.Zero)

for i in 0 .. 255 do
let r, g, b = 255, i, 255
let clr = (r <<< 16) ||| (g <<< 8) ||| b
SetPixel(dc, i, 0, uint32 clr) |> ignore

That said, I cannot imagine a scenario where this would be a good thing to do. If you are creating a sensible windows application, you will most certainly want to draw anything in the window belonging to your app.

Drawing C# graphics without using Windows Forms

Right, the way i've done it is with a windows form, but make the background transparent, and then get rid of all the borders...

Thanks for the replys anyway..

J

How can i capture the screen without the Form?

Um.. Hide the form?

this.Visible = false; and THEN run the screenshot method.

Like this:

protected Bitmap TakeScreenshot(bool cursor)
{
Bitmap bitmap;
this.Visible = false;
bitmap = CaptureScreen(cursor);
this.Visible = true;
return bitmap;
}

and use it in your code the way you wanted:

 private void timer1_Tick(object sender, EventArgs e)
{
using (bitmap = (Bitmap)ScreenCapture.TakeScreenshot(true))
{
ffmp.PushFrame(bitmap);
}
}

How do I draw graphics in C# without a form

EDIT - based on CuddleBunny's comment, I have created a class that will basically "draw graphics on the screen."

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
class test : Form
{
public test() : base()
{
this.TopMost = true;
this.DoubleBuffered = true;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
this.BackColor = Color.Purple;
this.TransparencyKey = Color.Purple;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Black, 0, 0, 200, 200);
this.Invalidate(); //cause repaint
}
public static void Main(String[] args)
{
Application.Run(new test());
}
}
}

Hope it helps.

old faulty answer


You can get the hwnd of another window and draw on that. I'm not sure how to draw on the entire screen though, I've always wondered that myself.

A simple example :

            Process p = Process.GetProcessById(0); //id of the process or some other method that can get the desired process
using (Graphics g = Graphics.FromHwnd(p.MainWindowHandle))
{
g.DrawRectangle(Pens.Black, 0, 0, 100, 100);
}

How to draw an empty rectangle on screen with Python

In order to refresh the old drawn area, you need to either call win32gui.UpdateWindow or something similar to update your specific window, but since you're not technically drawing on a surface, but the entire monitor. You'll need to invalidate the entire region of your monitor in order to tell windows to re-draw anything on it (or so I understand it).

And to overcome the slowness, instead of using for loops to create the boundary which will take X cycles to iterate over before completing the rectangle, you could use win32ui.Rectangle to draw it in one go:

import win32gui, win32ui
from win32api import GetSystemMetrics

dc = win32gui.GetDC(0)
dcObj = win32ui.CreateDCFromHandle(dc)
hwnd = win32gui.WindowFromPoint((0,0))
monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))

while True:
m = win32gui.GetCursorPos()
dcObj.Rectangle((m[0], m[1], m[0]+30, m[1]+30))
win32gui.InvalidateRect(hwnd, monitor, True) # Refresh the entire monitor

Further optimizations could be done here, like not update the entire monitor, only the parts where you've drawn on and so on. But this is the basic concept :)

And to create a rectangle without the infill, you could swap Rectangle for DrawFocusRect for instance. Or for more control, even use win32gui.PatBlt

And apparently setPixel is the fastest, so here's my final example with color and speed, altho it's not perfect as the RedrawWindow doesn't force a redraw, it simply asks windows to do it, then it's up to windows to honor it or not. InvalidateRect is a bit nicer on performance as it asks the event handler to clear the rect when there's free time to do so. But I haven't found a way more agressive than RedrawWindow, even tho that is still quite gentle. An example to this is, hide the desktop icons and the below code won't work.

import win32gui, win32ui, win32api, win32con
from win32api import GetSystemMetrics

dc = win32gui.GetDC(0)
dcObj = win32ui.CreateDCFromHandle(dc)
hwnd = win32gui.WindowFromPoint((0,0))
monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))

red = win32api.RGB(255, 0, 0) # Red

past_coordinates = monitor
while True:
m = win32gui.GetCursorPos()

rect = win32gui.CreateRoundRectRgn(*past_coordinates, 2 , 2)
win32gui.RedrawWindow(hwnd, past_coordinates, rect, win32con.RDW_INVALIDATE)

for x in range(10):
win32gui.SetPixel(dc, m[0]+x, m[1], red)
win32gui.SetPixel(dc, m[0]+x, m[1]+10, red)
for y in range(10):
win32gui.SetPixel(dc, m[0], m[1]+y, red)
win32gui.SetPixel(dc, m[0]+10, m[1]+y, red)

past_coordinates = (m[0]-20, m[1]-20, m[0]+20, m[1]+20)

Issues with positions and resolution? Be aware that high DPI systems tend to cause a bunch of issues. And I haven't found many ways around this other than going over to a OpenGL solution or using frameworks such as wxPython or OpenCV other than this post: Marking Your Python Program as High DPI Aware Seamlessly Windows

Or changing the Windows display scale to 100%:

100%

This causes the positioning issue to go away, perhaps take this to account by querying the OS for the scale and compensate.


The only reference I could find on the "clearing the old drawings" was this post: win32 content changed but doesn't show update unless window is moved tagged c++ win winapi. Hopefully this saves some people from searching before finding a good example.

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);
}
}


Related Topics



Leave a reply



Submit