Skip to content

Cory Foy

Organizational agility through intersecting business and technology

Menu
  • FASTER Fridays
  • Mapping Mondays
  • Player Embed
  • Search Videos
  • User Dashboard
  • User Videos
  • Video Category
  • Video Form
  • Video Tag
Menu

Screen Print Capture in C# using SendInput()

Posted on April 8, 2005 by Cory Foy

Recently I needed a way to have an app take a screen shot of itself and save it to a directory. I only wanted the currently focused screen to be captured. I looked around on the web, and found lots of crazy interop scripts to handle doing a screen shots.

But it just didn’t seem right. *I* can screen capture by just hitting Alt->Print Screen, why can’t my app? I knew there would be some interop involved, but certainly it doesn’t have to be that hard, does it?

Turns out, it really doesn’t. By sending some Keyboard events using the Win32 SendInput() via User32.dll, and working with the clipboard data in the normal fashion, it’s fairly easy to get it all working. First, you need the capture and save code:


private void CaptureAndSave()
{
uint intReturn = 0;
NativeWIN32.INPUT structInput;
structInput = new NativeWIN32.INPUT();
structInput.type = (uint)1;
structInput.ki.wScan = 0;
structInput.ki.time = 0;
structInput.ki.dwFlags = 0;
structInput.ki.dwExtraInfo = 0;

//Press Alt Key
structInput.ki.wVk = (ushort)NativeWIN32.VK.MENU;
intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));

// Key down the actual key-code
structInput.ki.wVk = (ushort)NativeWIN32.VK.SNAPSHOT;//vk;
intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));

// Key up the actual key-code
structInput.ki.dwFlags = NativeWIN32.KEYEVENTF_KEYUP;
structInput.ki.wVk = (ushort)NativeWIN32.VK.SNAPSHOT;//vk;
intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));

//Keyup Alt
structInput.ki.dwFlags = NativeWIN32.KEYEVENTF_KEYUP;
structInput.ki.wVk = (ushort)NativeWIN32.VK.MENU;
intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));

IDataObject data = Clipboard.GetDataObject();

if (data.GetDataPresent(DataFormats.Bitmap))
{
Image image = (Image)data.GetData(DataFormats.Bitmap,true);

image.Save("image" + ".bmp",System.Drawing.Imaging.ImageFormat.Bmp);
image.Save("image" + ".jpg",System.Drawing.Imaging.ImageFormat.Jpeg);
image.Save("image" + ".gif",System.Drawing.Imaging.ImageFormat.Gif);
}
else
{
Console.WriteLine("The Data In Clipboard is not in an image format");
}
}

As you can see, all the heavy lifting appears to be in a class called NativeWIN32. This class is nothing more than an internal wrapper class around the User32.dll functions. I’ve included the full suite of VK enums since I already had it in my code:


public class NativeWIN32
{
public const ushort KEYEVENTF_KEYUP = 0x0002;

public enum VK : ushort
{
SHIFT = 0x10,
CONTROL = 0x11,
MENU = 0x12,
ESCAPE = 0x1B,
BACK = 0x08,
TAB = 0x09,
RETURN = 0x0D,
PRIOR = 0x21,
NEXT = 0x22,
END = 0x23,
HOME = 0x24,
LEFT = 0x25,
UP = 0x26,
RIGHT = 0x27,
DOWN = 0x28,
SELECT = 0x29,
PRINT = 0x2A,
EXECUTE = 0x2B,
SNAPSHOT = 0x2C,
INSERT = 0x2D,
DELETE = 0x2E,
HELP = 0x2F,
NUMPAD0 = 0x60,
NUMPAD1 = 0x61,
NUMPAD2 = 0x62,
NUMPAD3 = 0x63,
NUMPAD4 = 0x64,
NUMPAD5 = 0x65,
NUMPAD6 = 0x66,
NUMPAD7 = 0x67,
NUMPAD8 = 0x68,
NUMPAD9 = 0x69,
MULTIPLY = 0x6A,
ADD = 0x6B,
SEPARATOR = 0x6C,
SUBTRACT = 0x6D,
DECIMAL = 0x6E,
DIVIDE = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7A,
F12 = 0x7B,
OEM_1 = 0xBA, // ',:' for US
OEM_PLUS = 0xBB, // '+' any country
OEM_COMMA = 0xBC, // ',' any country
OEM_MINUS = 0xBD, // '-' any country
OEM_PERIOD = 0xBE, // '.' any country
OEM_2 = 0xBF, // '/?' for US
OEM_3 = 0xC0, // '`~' for US
MEDIA_NEXT_TRACK = 0xB0,
MEDIA_PREV_TRACK = 0xB1,
MEDIA_STOP = 0xB2,
MEDIA_PLAY_PAUSE = 0xB3,
LWIN =0x5B,
RWIN =0x5C
}


public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public long time;
public uint dwExtraInfo;
};

[StructLayout(LayoutKind.Explicit,Size=28)]
public struct INPUT
{
[FieldOffset(0)] public uint type;
[FieldOffset(4)] public KEYBDINPUT ki;
};


[DllImport("user32.dll")]
public static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

}

And viola! Quick, easy screen capture code.

19 thoughts on “Screen Print Capture in C# using SendInput()”

  1. Anonymous says:
    May 9, 2005 at 12:55 pm

    The native calls to SendInput result in async calls which means that when you reach

    IDataObject data = clipboard.GetDataObject();
    if(data.GetDataPresent(DataFormats.Bitmap))

    the new screenshot has not yet ready.

    What have you done to wait for the call to SendInput to complete? Thread.Sleep()?

  2. Cory Foy says:
    May 9, 2005 at 3:59 pm

    I didn’t have that problem. The tricky part was not calling the method until the thing I wanted screen shotted was ready. But one it was, the call had no problem getting the data to the clipboard in time.

    That might not be true if my machine was under heavy load or something similar, but for what I was using it for it worked fairly seamlessly.

    I suppose you could do a Thread.Sleep() though if you were having that issue but just like I don’t expect it to take long when I hit Ctrl-C, I don’t expect the Print Screen to take very long either.

  3. Anonymous says:
    May 22, 2005 at 3:19 am

    Thanks for this article. I had a go at wrapping SendInput and the structures myself, but not knowing any C++ it was a bit of a doomed undertaking…

  4. Anonymous says:
    September 8, 2005 at 3:08 pm

    what if i want to use the same code and not print the focused window but the entire desktop ? Is there a key/field i can simply change ?

  5. Anonymous says:
    September 8, 2005 at 3:09 pm

    waht if i want to use the same code and not print the focused window but the entire desktopn screen shot ? can i change some value for this in your code ?

  6. Cory Foy says:
    September 8, 2005 at 3:35 pm

    Also, to take screenshots of the entire desktop instead of just the focused window, simply comment out the code which keys down and keys up the Alt Key.

  7. Anonymous says:
    December 2, 2005 at 3:08 pm

    What is Marshal.SizeOf(structInput)

  8. Michael G. Butler, michael.g.butler@gmail.com says:
    February 7, 2006 at 10:52 am

    I had a similar problem. Using a sleep if problematic because system can vary. I used a listener on the clipboard so that the action for the image was only called when the image was ready. Here’s the above code modified.
    -Michael G. Butler
    michael.g.butler@gmail.com

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml;
    using System.Runtime.InteropServices;
    using System.Drawing.Imaging;
    using System.Collections;
    using PowerPoint;
    using System.Runtime.InteropServices.ComTypes;
    using Microsoft.Office.Core;
    using System.Globalization;
    using System.Reflection;

    public partial class Form1 : Form
    {
    // Assembly anAssembly;

    [DllImport(“User32.dll”)]
    protected static extern int SetClipboardViewer(int hWndNewViewer);

    [DllImport(“User32.dll”, CharSet = CharSet.Auto)]
    public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport(“user32.dll”, CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

    // HOT key dlls
    [DllImport(“user32.dll”)]
    public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
    [DllImport(“user32.dll”)]
    public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    IntPtr nextClipboardViewer;

    public Form1()
    {
    InitializeComponent();
    Clipboard.Clear();
    nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);

    // Alt =1 , Ctrl =2, Shift =4, Win =0
    //register hot Ctrl , Alt, and ‘z’
    bool success = Form1.RegisterHotKey(this.Handle, this.GetType().GetHashCode(), 2 ,(int)’Z’);
    if (success)
    {
    Console.WriteLine(“Alt-z hot key successfully registered”);
    }
    else
    MessageBox.Show(“Could not register Hotkey – there is probably a conflict. “, “”, MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    /// Clean up any resources being used.
    protected override void Dispose(bool disposing)
    {
    ChangeClipboardChain(this.Handle, nextClipboardViewer);
    if (disposing)
    {
    if (components != null)
    {
    components.Dispose();
    }
    }
    base.Dispose(disposing);
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
    // defined in winuser.h
    const int WM_DRAWCLIPBOARD = 0x308;
    const int WM_CHANGECBCHAIN = 0x030D;

    switch (m.Msg)
    {
    case WM_DRAWCLIPBOARD:
    DisplayClipboardData();
    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
    break;

    case WM_CHANGECBCHAIN:
    if (m.WParam == nextClipboardViewer)
    nextClipboardViewer = m.LParam;
    else
    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
    break;

    case 0x0312:
    CaptureAndSave();
    break;

    default:
    base.WndProc(ref m);
    break;
    }
    }

    void DisplayClipboardData()
    {
    try
    {
    System.Windows.Forms.IDataObject iData = new DataObject();
    iData = Clipboard.GetDataObject();

    if (iData.GetDataPresent(DataFormats.Rtf))
    Console.WriteLine ((string)iData.GetData(DataFormats.Rtf));
    else if (iData.GetDataPresent(DataFormats.Text))
    Console.WriteLine ((string)iData.GetData(DataFormats.Text));
    else if (iData.GetDataPresent(DataFormats.Bitmap))
    {
    Console.WriteLine(“retrieving image”);
    Bitmap aBitMap = (Bitmap)iData.GetData(DataFormats.Bitmap);
    }
    else
    Console.WriteLine(“[Clipboard data is not RTF or ASCII Text]”);
    }
    catch (Exception e)
    {
    MessageBox.Show(e.ToString());
    }
    }

    private void CaptureAndSave()
    {
    uint intReturn = 0;
    NativeWIN32.INPUT structInput;
    structInput = new NativeWIN32.INPUT();
    structInput.type = (uint)1;
    structInput.ki.wScan = 0;
    structInput.ki.time = 0;
    structInput.ki.dwFlags = 0;
    structInput.ki.dwExtraInfo = 0;
    //Press Alt Key
    structInput.ki.wVk = (ushort)NativeWIN32.VK.MENU;
    intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
    // Key down the actual key-code
    structInput.ki.wVk = (ushort)NativeWIN32.VK.SNAPSHOT;
    //vk;
    intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
    // Key up the actual key-code
    structInput.ki.dwFlags = NativeWIN32.KEYEVENTF_KEYUP;
    structInput.ki.wVk = (ushort)NativeWIN32.VK.SNAPSHOT;
    //vk;
    intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));
    //Keyup Alt
    structInput.ki.dwFlags = NativeWIN32.KEYEVENTF_KEYUP;
    structInput.ki.wVk = (ushort)NativeWIN32.VK.MENU;
    intReturn = NativeWIN32.SendInput((uint)1, ref structInput, Marshal.SizeOf(structInput));

    }

    }

  9. Anonymous says:
    September 7, 2006 at 11:02 pm

    THANKYOU So much!!! Cheers!
    -Edmond

  10. Anonymous says:
    October 16, 2006 at 11:13 am

    This is genius. Thank you very much :)
    – Abbie

  11. Michal Fojtik says:
    February 24, 2007 at 4:10 pm

    just wanted to say thanks for this old but great piece of code :)

  12. Ran 'chaosblade' Sagy says:
    May 31, 2007 at 4:28 pm

    Nice code. Tried using it to simulate pasting, But couldn’t get it to work reliably in Vista. Any idea?

  13. Anonymous says:
    August 10, 2007 at 2:38 pm

    I had the problem with Marshal too. I believe it’s part of System.Runtime.InteropServices. At least in my Visual C#. If do the following it compiles fine:

    using System.Runtime.InteropServices;

  14. Steve Osborne says:
    September 24, 2008 at 10:59 am

    Thank you so much Cory. All I had to do to have a very powerful helper class to handle all sorts of key combinations that would also include characters was to add to NativeWIN32.cs the following function

    [DllImport(“user32.dll”)]
    public static extern short VkKeyScan(char ch);

  15. Anonymous says:
    November 15, 2008 at 8:33 am

    what is exactly your application doing?
    can you please put here a running project?

  16. Anonymous says:
    May 12, 2009 at 11:34 am

    I know this is 4 years later but just wanted to say thanks. I used this to check and set NumLock status. For C#.NET, this was a clearer and easier to implement solution than some of the others out there.

  17. Frank says:
    November 12, 2009 at 8:18 am

    Hi there,

    just stumbled on this post of yours, needed the keyCode for PrintScreen, so thanks for that :)

    Also, as per request, I’m commenting because the sources look awful :D

    Cheers

  18. Vimal says:
    June 21, 2011 at 12:24 am

    Hi,

    Whenever i open my window, I want to copy the highlighted text on the any active application and paste it on the text box in my window.
    How to do this using SendInput in C#?
    Please help

  19. learner says:
    January 17, 2013 at 7:22 am

    using SendKeys.SendWait(“{PRTSC}”);

Comments are closed.

© 2025 Cory Foy | Powered by Superbs Personal Blog theme