65.9K
CodeProject 正在变化。 阅读更多。
Home

ScreenCap:C# 屏幕捕获应用程序(基于 GuyThiebaut 的 TeboScreen 分支)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (24投票s)

2012 年 2 月 16 日

CPOL

2分钟阅读

viewsIcon

84593

downloadIcon

7788

一个基于 GuyThiebaut 的 TeboScreen 的 C# 屏幕捕获应用程序。

引言

此应用程序在后台运行,并接管“Print Screen”按钮,允许以两种不同的方式捕获屏幕。

  • 捕获屏幕:这就像字面意思一样;它基本上捕获整个屏幕。
  • 捕获区域:按住鼠标左键,用户可以绘制一个矩形,指定他们希望捕获的屏幕部分。然后,用户可以选择三种方法之一来发送绘制矩形后面的区域(剪贴板打印机电子邮件)。绘制完成后,可以在将图像发送到所需目的地之前调整矩形的大小和移动。

背景

我的雇主最近从一个旧的 Telnet 应用程序迁移到一个 Windows 应用程序。旧应用程序允许用户点击“Print Screen”按钮并将其会话屏幕发送到打印机。我们的许多用户没有 Windows 使用经验,他们需要能够轻松地捕获屏幕并将其发送到打印机,而无需费力地绕过 Windows 环境。

在互联网上搜索好的屏幕截图程序来实现这一点后,我们发现的大部分内容都比较弱而且昂贵。我知道这个项目对我来说应该不会很难编写;所以,我开始寻找一些开源来帮助我快速完成任务,并发现了 Code Project 上的 GuyThiebaut 的“TeboScreen”应用程序:TeboScreen

Using the Code

我重点关注了几个方面来增强原始的 TeboScreen

  • 在后台运行,并在按下“Print Screen”按钮时激活
  • 支持双显示器,无论显示器之间是否存在尺寸差异
  • 自动将屏幕截图发送到三个设备:剪贴板打印机电子邮件
  • 方便最终用户使用

考虑到这一点,我不会详细介绍原始项目中涵盖的冗余细节。

在后台运行

我们所需要做的就是为主窗体的 Shown 事件添加一个处理程序

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

处理 Print Screen 按钮

这需要编写一个程序集,根据这个讨论 KeyHook

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

为了实现这一点,我在 KeyHooker 类中添加了一个委托

public static event EventHandler PrintScreenBtnEvent = null;

双显示器支持

为此,我不得不查看 System.Windows.Forms.Screen 类。下面是一段代码片段,用于存储主显示器的索引。主显示器是 TeboScreen 应用程序的基础,因此此项目中主显示器的任何代码都将相同。如果正在处理的显示器不是主显示器,我必须对代码进行调整。

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

退出程序

当主屏幕(来自上面的屏幕截图)显示并具有焦点时,按 Ctrl-Alt-S 然后按 X。

© . All rights reserved.