C#编写的抓屏程序和源代码

参考了网上的一些代码写了个抓屏程序,主要是根据自己的需求慢慢写的。

自己用着感觉也还不错,满足一般的要求应该是足够了,在这里共享一下程序和代码。

首次原创,转载请注明来源。

(注:运行程序需要Framework 2.0,项目是用VS2008建的)

 


关于程序:
下载

程序图标(用的QQ的图标)只显示在系统栏中,可通过启动程序、左键单击任务栏中的程序图标或输入设置的快捷键(默认为PrintScreen)开始抓屏。

用鼠标左键确定抓屏区域,确定好区域之后双击左键保存抓取区域的图片。

单击右键或点击ESC键取消抓屏操作并将程序隐藏在任务栏中。

右键单击任务栏中的程序图标弹出程序菜单,可退出程序,进行程序的设置和查看关于对话框。

 


关于代码:
下载

抓屏:

本程序主要功能是抓屏,先说说抓屏。

抓屏主窗体为FormCatcher,包含的控件主要是一个用于保存图片和画图的PictureBox,然后是一些成员变量。


得到屏幕图片的主要原理是通过 System.Drawing.Graphics 类的成员函数public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize)将屏幕复制到System.Drawing.Bitmap类的实例中,然后怎么操作就随便了。

用于拷图操作的私有函数:

private  Bitmap GetScreenImage(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize)
ExpandedBlockStart.gifContractedBlock.gif
{
    Bitmap bmp 
= new Bitmap(blockRegionSize.Width, blockRegionSize.Height);
    Graphics g 
= Graphics.FromImage(bmp);
    g.CopyFromScreen(upperLeftSource, upperLeftDestination, blockRegionSize);
    g.Dispose();
    
return bmp;
}

 

初始化抓屏操作的函数,用PictureBox控件pbCatchArea覆盖全屏并将其背景图设为拷屏得到的图片,然后初始化抓屏状态_csCurrentStep,显示激活抓屏窗体

public   void  RestartCatch()
ExpandedBlockStart.gifContractedBlock.gif
{
   
pbCatchArea.Cursor = Cursors.Default;
    this.Bounds = Screen.PrimaryScreen.Bounds;
    
this.pbCatchArea.Bounds = Screen.PrimaryScreen.Bounds;    this.pbCatchArea.BackgroundImage = this.GetScreenImage(new Point(00), new Point(00), new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height));
    _csCurrentStep 
= CatchSatus.Start;
    
this.Show();
    
this.Activate();
}

 

程序主要的代码是在Mouse的MouseDown,MouseMove和MouseDown三个事件中根据不同的抓屏状态_csCurrentStep和鼠标点击时的参数调整各个成员变量的值,然后在pbCatchArea的Paint事件中根据这些成员变量的值进行画图或写字符串的操作,最后在MouseDoubleClick中保存抓屏区域的图。

有必要把几个重要的窗体类的成员变量说明一下:

private  Point _pLeftTopPoint  =   new  Point( 0 0 ), _pRightBottomPoint  =   new  Point( 0 0 );
private  Point _pSizedPoint  =   new  Point( 0 0 ), _pCurrentPoint  =   new  Point( 0 0 );
private  CatchSatus _csCurrentStep  =  CatchSatus.Start;
private  Position _psSizedPosition  =  Position.Outside;
private  DateTime _dtMouseClickTime  =  System.DateTime.Now;
private  TimeSpan MOUSE_DOUBLE_CLICK_MAX_SPAN  =   new  TimeSpan( 0 0 0 0 300 );

首先是4个点,分别是确定抓屏区域时的左上点_pLeftTopPoint、右下点_pRightBottomPoint、移动或调整抓屏区域时的参照点_pSizedPoint和鼠标所在的当前点_pCurrentPoint。前两个点用于确定抓屏区域;_pSizedPoint的值是调整抓屏区域时鼠标左键按下时的点坐标,根据此点和鼠标的移动调整抓屏区域;_pCurrentPoint用于确定在决定抓屏区域时提示字符串应该写在屏幕的左上角还是右上角。
枚举变量_csCurrentStep保存抓屏操作的当前状态。
枚举变量_psSizedPosition用于标示鼠标与已经确定了的抓屏区域的相对位置,在调整抓屏区域时感觉此变量进行不同的操作。
_dtMouseClickTimeMOUSE_DOUBLE_CLICK_MAX_SPAN用来区分MouseDown和MouseDoubleClick事件。


MouseDown事件,单击左键调整成员变量并调用pbCatchArea.Invalidate()激活pbCatchArea的Paint事件,单击右键则隐藏程序:

ContractedBlock.gif ExpandedBlockStart.gif Code
private void pbCatchArea_MouseDown(object sender, MouseEventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
{
    
//双击则返回
    if ((System.DateTime.Now - _dtMouseClickTime) <= MOUSE_DOUBLE_CLICK_MAX_SPAN)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
return;
    }

    
if (e.Button == MouseButtons.Left)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
switch (_csCurrentStep)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
case CatchSatus.Start:
                _csCurrentStep 
= CatchSatus.Pending;
                
break;
            
case CatchSatus.Pending:
                _pRightBottomPoint 
= new Point(e.X, e.Y);
                _csCurrentStep 
= CatchSatus.WaitingSave;
                
break;
            
case CatchSatus.WaitingSave:
                _pSizedPoint 
= new Point(e.X, e.Y);
                _psSizedPosition 
= Compare(_pSizedPoint, new Rectangle(_pLeftTopPoint.X, _pLeftTopPoint.Y, _pRightBottomPoint.X - _pLeftTopPoint.X, _pRightBottomPoint.Y - _pLeftTopPoint.Y));
                _csCurrentStep 
= CatchSatus.Resizing;
                
break;
        }

        _dtMouseClickTime 
= System.DateTime.Now;
        pbCatchArea.Invalidate();
    }

    
else
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
this.Hide();
    }

}

 

MouseMove事件,也是处理逻辑然后激活pbCatchArea的Paint事件:

ContractedBlock.gif ExpandedBlockStart.gif Code
private void pbCatchArea_MouseMove(object sender, MouseEventArgs e)
{
    _pCurrentPoint 
= new Point(e.X, e.Y);
    
switch (_csCurrentStep)
    {
        
case CatchSatus.Start:
            _pLeftTopPoint 
= new Point(e.X, e.Y);
            _pRightBottomPoint 
= new Point(e.X, e.Y);
            
break;
        
case CatchSatus.Pending:
            _pRightBottomPoint 
= new Point(e.X, e.Y);
            
break;
        
case CatchSatus.WaitingSave:
            Position ps 
= Compare(new Point(e.X, e.Y), new Rectangle(_pLeftTopPoint.X, _pLeftTopPoint.Y, _pRightBottomPoint.X - _pLeftTopPoint.X, _pRightBottomPoint.Y - _pLeftTopPoint.Y));
            
switch (ps)
            {
                
case Position.Inside:
                    pbCatchArea.Cursor 
= Cursors.SizeAll;
                    
break;
                
case Position.Outside:
                    pbCatchArea.Cursor 
= Cursors.Default;
                    
break;
                
case Position.Left:
                
case Position.Right:
                    pbCatchArea.Cursor 
= Cursors.SizeWE;
                    
break;
                
case Position.Top:
                
case Position.Bottom:
                    pbCatchArea.Cursor 
= Cursors.SizeNS;
                    
break;
                
case Position.LeftBottom:
                
case Position.RightTop:
                    pbCatchArea.Cursor 
= Cursors.SizeNESW;
                    
break;
                
case Position.LeftTop:
                
case Position.RightBottom:
                    pbCatchArea.Cursor 
= Cursors.SizeNWSE;
                    
break;
            }
            
break;
        
case CatchSatus.Resizing:
            
switch (_psSizedPosition)
            {
                
case Position.Inside:
                    
//限制在屏幕范围内
                    if (_pLeftTopPoint.X + e.X - _pSizedPoint.X >= 0 && _pLeftTopPoint.Y + e.Y - _pSizedPoint.Y >= 0 && _pRightBottomPoint.X + e.X - _pSizedPoint.X <= this.Width && _pRightBottomPoint.Y + e.Y - _pSizedPoint.Y <= this.Height)
                    {
                        _pLeftTopPoint.X 
+= e.X - _pSizedPoint.X;
                        _pLeftTopPoint.Y 
+= e.Y - _pSizedPoint.Y;
                        _pRightBottomPoint.X 
+= e.X - _pSizedPoint.X;
                        _pRightBottomPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    }
                    
break;
                
case Position.Left:
                    _pLeftTopPoint.X 
+= e.X - _pSizedPoint.X;
                    
break;
                
case Position.Right:
                    _pRightBottomPoint.X 
+= e.X - _pSizedPoint.X;
                    
break;
                
case Position.Top:
                    _pLeftTopPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    
break;
                
case Position.Bottom:
                    _pRightBottomPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    
break;
                
case Position.LeftTop:
                    _pLeftTopPoint.X 
+= e.X - _pSizedPoint.X;
                    _pLeftTopPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    
break;
                
case Position.LeftBottom:
                    _pLeftTopPoint.X 
+= e.X - _pSizedPoint.X;
                    _pRightBottomPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    
break;
                
case Position.RightTop:
                    _pRightBottomPoint.X 
+= e.X - _pSizedPoint.X;
                    _pLeftTopPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    
break;
                
case Position.RightBottom:
                    _pRightBottomPoint.X 
+= e.X - _pSizedPoint.X;
                    _pRightBottomPoint.Y 
+= e.Y - _pSizedPoint.Y;
                    
break;
            }
            _pSizedPoint 
= new Point(e.X, e.Y);
            
break;
    }
    pbCatchArea.Invalidate();
}


MouseUp事件,调整抓屏区域的左上点和右下点:

private   void  pbCatchArea_MouseUp( object  sender, MouseEventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
{
    
if (_csCurrentStep == CatchSatus.WaitingSave || _csCurrentStep == CatchSatus.Resizing)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        AdjustPoints();
        _csCurrentStep 
= CatchSatus.WaitingSave;
    }

}

因为确定抓屏区域时鼠标点下的第一个点不一定就是左上点,调整抓屏区域的大小时也一样,AdjustPoints()作用就是重新调整_pLeftTopPoint和_pRightBottomPoint。

 

pbCatchArea的Paint事件响应:

private   void  pbCatchArea_Paint( object  sender, PaintEventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
{
    
switch (_csCurrentStep)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
case CatchSatus.Start:
            DrawReferLine(e.Graphics);
            DrawString(e.Graphics, 
string.Format("{0}", _pRightBottomPoint));
            
break;
        
case CatchSatus.Pending:
            DrawReferLine(e.Graphics);
            DrawCatchRectangle(e.Graphics);
            DrawString(e.Graphics, 
string.Format("{0}->{1} 宽:{2};高:{3}", _pLeftTopPoint, _pRightBottomPoint, _pRightBottomPoint.X - _pLeftTopPoint.X, _pRightBottomPoint.Y - _pLeftTopPoint.Y));
            
break;
        
case CatchSatus.Resizing:
            DrawCatchRectangle(e.Graphics);
            DrawString(e.Graphics, 
string.Format("{0}->{1} 宽:{2};高:{3}", _pLeftTopPoint, _pRightBottomPoint, _pRightBottomPoint.X - _pLeftTopPoint.X, _pRightBottomPoint.Y - _pLeftTopPoint.Y));
            
break;
        
case CatchSatus.WaitingSave:
        
case CatchSatus.Saving:
            DrawCatchRectangle(e.Graphics);
            
break;
        
default:
            
break;
    }

}
 

MouseDoulbeClick事件保存抓屏区域内的图:

ContractedBlock.gif ExpandedBlockStart.gif Code
private void pbCatchArea_MouseDoubleClick(object sender, MouseEventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
{
    
//将图片复制到剪切板并保存
    if (_csCurrentStep == CatchSatus.WaitingSave)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
//Saving时不响应快捷键
        _csCurrentStep = CatchSatus.Saving;
        Bitmap bmpCatched 
= GetScreenImage(new Point(_pRightBottomPoint.X - _pLeftTopPoint.X > 0 ? _pLeftTopPoint.X + 1 : _pRightBottomPoint.X + 1, _pRightBottomPoint.Y - _pLeftTopPoint.Y > 0 ? _pLeftTopPoint.Y + 1 : _pRightBottomPoint.Y + 1), new Point(00), new Size(System.Math.Abs(_pRightBottomPoint.X - _pLeftTopPoint.X - 1), System.Math.Abs(_pRightBottomPoint.Y - _pLeftTopPoint.Y - 1)));
        Clipboard.SetImage(bmpCatched);
        
if (Setting.Default.SavePic)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            SaveFileDialog sfg 
= new SaveFileDialog();
            sfg.Filter 
= "Jpeg(*.jpg)|*.jpg|Bmp(*.bmp)|*.bmp|All|*.*";
            
if (sfg.ShowDialog() == DialogResult.OK)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                System.Drawing.Imaging.ImageFormat formatPicSave 
= System.Drawing.Imaging.ImageFormat.Jpeg;
                
switch (sfg.FilterIndex)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    
case 1:
                        formatPicSave 
= System.Drawing.Imaging.ImageFormat.Jpeg;
                        
break;
                    
case 2:
                        formatPicSave 
= System.Drawing.Imaging.ImageFormat.Bmp;
                        
break;
                    
default:
                        
break;
                }

                bmpCatched.Save(sfg.FileName, formatPicSave);
            }

        }

        
else
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            MessageBox.Show(
"图片已经复制到剪切板""提示");
        }

        _csCurrentStep 
= CatchSatus.WaitingSave;
        
if (Setting.Default.HideAfterCatch)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
this.Hide();
        }

    }

}


到这整个抓屏操作就完成了。

 

全局快捷键的设置和调用:

快捷键处理类:
快捷键处理类的代码主要是参考http://www.cnblogs.com/TianFang/archive/2007/05/14/745489.html写的,就做了一点点的小改动:

  1. Regist:如果注册的函数已经在keymap中存在则不重复注册;
  2. UnRegist:加了同步更新keymap;
  3. ProcessHotKey:传进来的参数由Windows消息改成了消息的值,在这里消息的值即为回调函数的Id。

ContractedBlock.gif ExpandedBlockStart.gif Code
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ScreenCatcher
{
    
public static class HotkeyHelper
    {
        
#region 系统api
        [DllImport(
"user32.dll")]
        [
return: MarshalAs(UnmanagedType.Bool)]
        
static extern bool RegisterHotKey(IntPtr hWnd, int id, ModifierKeys fsModifiers, Keys vkMain);

        [DllImport(
"user32.dll")]
        
static extern bool UnregisterHotKey(IntPtr hWnd, int id);
        
#endregion

        
//Keymap的ID, 消息处理函数的WParam
        static int keyid = 10;
        
static Dictionary<int, HotKeyCallBackHanlder> keymap = new Dictionary<int, HotKeyCallBackHanlder>();
        
public delegate void HotKeyCallBackHanlder();

        
/**/
        
/// <summary> 
        
/// 注册快捷键 
        
/// </summary> 
        
/// <param name="hWnd">持有快捷键窗口的句柄</param> 
        
/// <param name="fsModifiers">组合键</param> 
        
/// <param name="vk">快捷键的虚拟键码</param> 
        
/// <param name="callBack">回调函数</param> 
        public static void Regist(IntPtr hWnd, ModifierKeys fsModifiers, Keys vkMain, HotKeyCallBackHanlder callBack)
        {
            
if (keymap.ContainsValue(callBack))
                
return;
            
int id = keyid++;
            
if (!RegisterHotKey(hWnd, id, fsModifiers, vkMain))
                
throw new Exception("regist hotkey fail.");
            keymap[id] 
= callBack;
        }

        
/**/
        
/// <summary> 
        
/// 注销快捷键 
        
/// </summary> 
        
/// <param name="hWnd">持有快捷键窗口的句柄</param> 
        
/// <param name="callBack">回调函数</param> 
        public static void UnRegist(IntPtr hWnd, HotKeyCallBackHanlder callBack)
        {
            
int iKey = -1;
            
foreach (KeyValuePair<int, HotKeyCallBackHanlder> var in keymap)
            {
                
if (var.Value == callBack)
                {
                    iKey 
= var.Key;
                    UnregisterHotKey(hWnd, var.Key);
                }
            }
            
if (iKey >= 10)
                keymap.Remove(iKey);
        }

        
/**/
        
/// <summary> 
        
/// 快捷键消息处理,在窗体中WndProc中快捷键消息(WM_HOTKEY = 0x312)中调用
        
/// </summary> 
        public static void ProcessHotKey(int callbackId)
        {
            HotKeyCallBackHanlder callback;
            
if (keymap.TryGetValue(callbackId, out callback))
            {
                callback();
            }
        }
    }

    [Flags()]
    
public enum ModifierKeys
    {
        None 
= 0x0,
        Alt 
= 0x1,
        Control 
= 0x2,
        Shift 
= 0x4,
        Windows 
= 0x8
    }
}

 

快捷键的设置:

程序启动时通过Setting.Default(Setting类的实例)读取设置文件Config.xml中设置的快捷键值,然后通过HotkeyHelper.Regist(this.Handle, kModifier, kMain, RestartCatch)注册快捷键。

处理快捷键是在消息处理函数中:

protected   override   void  WndProc( ref  Message m)
ExpandedBlockStart.gifContractedBlock.gif
{
    
switch (m.Msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
case FormCatcher.UM_RESTART:
            
//试图运行程序的多个实例时转为重新开始截图的操作
            this.RestartCatch();
            
break;
        
case FormCatcher.WM_HOTKEY:
            
//正在保存图片时不处理快捷键
            if (_csCurrentStep != CatchSatus.Saving)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                HotkeyHelper.ProcessHotKey(m.WParam.ToInt32());
            }

            
break;
        
default:
            
break;
    }

    
base.WndProc(ref m);
}

 

只运行一个程序实例: 

也是参考网上的代码用互斥变量实现的。
不同之处是如果找到此程序已运行则发送用户消息UM_RESTART给此进程的frmCatcher窗体。
然后在窗体FormCatcher类中的消息处理函数(如上代码)中做处理,在这里是调用RestartCatch()重启抓屏操作。

给已运行的进程中frmCatcher窗体发送消息的代码如下:

private   static   void  HandleRunningInstance()
ExpandedBlockStart.gifContractedBlock.gif
{
    Process pRunning 
= null;
    Process pCurrent 
= Process.GetCurrentProcess();
    Process[] psSameName 
= Process.GetProcessesByName(pCurrent.ProcessName);
    
foreach (Process p in psSameName)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
if (p.Id != pCurrent.Id)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            pRunning 
= p;
            
break;
        }

    }

    
if (pRunning != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        IntPtr mainWindowHandle 
= pRunning.MainWindowHandle == IntPtr.Zero ? FindWindow(null"frmCatcher") : pRunning.MainWindowHandle;
        
//发送消息给程序主窗体
        SendMessage(mainWindowHandle, FormCatcher.UM_RESTART, IntPtr.Zero, IntPtr.Zero);
    }

}

此函数用到了Windows API FindWindow和SendMessage。

 

另外,程序还包括其他三个类:
FormSetting:程序的设置窗体类,包括全局快捷键的设置。
Setting:封装了程序的各个设置选项,并通过XmlHelper进行程序设置的初始化和保存。
XmlHelper:Xml文档操作类,也就是读取和保存简单的Xml文档。

 

转载于:https://www.cnblogs.com/viewercq/archive/2008/09/04/1281019.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值