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