Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / tools

ScreenCap: C# Screen Capture Application (branched from TeboScreen by GuyThiebaut)

4.87/5 (24 votes)
24 Feb 2012CPOL2 min read 83.1K   7.7K  
A C# screen capture application based on TeboScreen by GuyThiebaut

Introduction

This application runs in the background and takes over the Print Screen button allowing for capture of the screen in two different ways:

  • Capture Screen: This does what it says; it basically captures the whole of the screen.
  • Capture Area: Holding down the left mouse button, users draw a rectangle specifying which part of the screen they wish to capture. The user can then select one of three methods to send the area behind the drawn rectangle (clipboard, printer, email). Once drawn, the rectangle can be resized and moved around the screen before sending the image to the desired destination.

Background

My employer recently migrated from a legacy Telnet application, to a Windows application. The legacy app allowed users to hit the Print Screen button and send their session screen to a printer. Many of our users are not experienced Windows users and it was necessary for them to be able to easily capture a screen and send it to a printer without having to hack around the Windows environment.

After doing some searches on the internet for a good screen capture program to accomplish this, most of what we found was weak and costly. I knew this project shouldn't be much trouble for me to write; So, I set out to find some open source to help me accomplish the task quickly and came across the "TeboScreen" Application by GuyThiebaut here on Code Project: TeboScreen

Using the Code

There were a few things I focused on to enhance the original TeboScreen:

  • Run in the background and become activated when the Print Screen button is pressed
  • Support for dual monitors, regardless whether there is a size difference in monitors
  • Automatically send screen capture to three devices: Clipboard, Printer, and EMail
  • Ease of use for the end user

With this in mind, I won't go into redundant details covered in the original project.

Running in Background

All we need to do is add a handler for the Shown event of the main form:

C#
private void ControlPanel_Shown(object sender, EventArgs e)
{
 this.Hide();
}

Handling Print Screen Button

This required writing an assembly, per this discussion KeyHook.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Reflection;

namespace KeyHook
{
    public class KeyHooker
    {
        public static event EventHandler PrintScreenBtnEvent = null;

        [StructLayout(LayoutKind.Sequential)]
        public struct KBDLLHOOKSTRUCT
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;

            public int extraInfo;
        }

        public delegate int HookProc(int nCode, int wParam, IntPtr ptrKBDLLHOOKSTRUCT);

        [DllImport("user32.dll", CharSet = CharSet.Auto, 
         CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx
        (int idHook, HookProc callBack, IntPtr hMod, int threadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, 
         CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int CallNextHookEx(IntPtr hhk, int nCode, int wParam, IntPtr lParam);

        private static IntPtr kbh_Handle;
        private static HookProc kbh_HookProc;

        private const int VK_SNAPSHOT = 0x2C;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_SYSKEYDOWN = 0x0104;
        private const int WH_KEYBOARD_LL = 13;

        private static int LowLevelKeyboardProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode < 0)
            {
                CallNextHookEx(kbh_Handle, nCode, wParam, lParam);
                return 0;
            }

            if (wParam == WM_KEYDOWN)
            {
                IntPtr kbdll = lParam;
                KBDLLHOOKSTRUCT kbdllstruct = 
                    (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(kbdll, typeof(KBDLLHOOKSTRUCT));

                if (kbdllstruct.vkCode == VK_SNAPSHOT)
                {
                    if (PrintScreenBtnEvent != null)
                        PrintScreenBtnEvent(null, new EventArgs());

                    return -1;
                }
            }

            return CallNextHookEx(kbh_Handle, nCode, wParam, lParam);
        }

        public static void HookKeyboard()
        {
            try
            {
                kbh_HookProc = LowLevelKeyboardProc;

                kbh_Handle = SetWindowsHookEx(WH_KEYBOARD_LL, kbh_HookProc, 
                   Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);

                if (kbh_Handle == IntPtr.Zero)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(String.Format("ERROR: {0}", ex.Message));
            }
        }
    }
}

To implement this, I added a delegate to the KeyHooker class:

C#
public static event EventHandler PrintScreenBtnEvent = null;

Dual Monitor Support

To do this, I had to look at the System.Windows.Forms.Screen class. Below is a snippet of code used to store the index of the main monitor. The main monitor is what the TeboScreen application is based on, so any code in this project for the main monitor will be the same. I had to make adjustments to the code if monitor being processed is not the main monitor.

C#
private int GetPrimaryMonIdx()
{
    Screen[] sc;
        sc = Screen.AllScreens;
        int idx = 0;

        foreach (Screen s in sc)
        {
            if (s.Bounds.Left == System.Windows.Forms.Screen.PrimaryScreen.Bounds.Left)
                    break;
                else
                    idx++;
        }

    return (idx <= sc.Length) ? idx : 0;
}

Exiting the Program

When the main screen (from the screen shot above) is showing and has focus, hit Ctrl-Alt-S and then X.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)