• 浅析如何用C#.NET做屏幕截图软件以及注册全局快捷键(下)


    续:浅析如何用C#.NET做屏幕截图软件以及注册全局快捷键(上)

    下面来说第三种方法:模拟PrintScreen按键,访问Clipboard来获得屏幕截图,继而执行截取操作。
    1、模拟PrintScreen按键为我们省去了很多代码,但刚开始也给我带来了很大的费解,假如你想通过button_click事件调用API来模拟按下PrintScreen键,然后通过Clipboard.GetImage()去获取剪贴板里面的图片,那样结果只有一个:失败!也许你认为是我们在调用Clipboard.GetImage()时模拟按键还未完成操作,然后我们在它们中间加个Thread.Sleep(0),很遗憾,结果还是一样。我怀疑是鼠标点击button和模拟PrintScreen按键两者之间的紊乱造成的,在此就不多说废话了,本人想到的解决方法就是使用backgroundWorker来执行按键处理,这样就分离了窗体线程和模拟按键之间的紊乱问题。
    C#模拟键盘按键的实现:
    模拟键盘要用到user32.dll里面的keybd_event
    原型如下:

    [DllImport("user32.dll")]
    static extern void keybd_event(
     byte bVk,// 虚拟键值
     byte bScan,// 硬件扫描码
     uint dwFlags,// 动作标识
     UIntPtr dwExtraInfo// 与键盘动作关联的辅加信息
    );
    
    

    其中,bVk表示虚拟键值,其实它是一个BYTE类型值的宏,其取值范围为1-254。有关虚拟键值表请在MSDN上使用关键字“Virtual-Key Codes”查找相关资料。bScan表示当键盘上某键被按下和放开时,键盘系统硬件产生的扫描码我们可以MapVirtualKey()函数在虚拟键值与扫描码之间进行转换,一般可以设置为0。dwFlags表示各种各样的键盘动作,应用程序可使用如下一些预定义常数的组合设置标志位,KEYEVENTF_EXETENDEDKEY=1:若指定该值,则扫描码前一个值为OXEO(224)的前缀字节。DEYEVENTF_KEYUP=2:若指定该值,该键将被释放;若未指定该值,该键交被接下。dwExtralnfo:定义与击键相关的附加的32位值。C#模拟PrintScreen按键示例如下,

    public static void PrintScreen() {
                IceApi.keybd_event( (byte)0x2c, 0, (uint)0, IntPtr.Zero );//down
                IceApi.keybd_event( (byte)0x2c, 0, (uint)2, IntPtr.Zero );//up
            }
    

    贴一下button的处理,见截图~:

    我们把click之后的事情都交给backgroundWorker去做,省去了很多很多麻烦问题。


    接下来是开始进入真正的截图阶段。首先New一个窗体SnapForm,设置如下属性:

    this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
    this.Opacity = 0.99;
    this.ShowIcon = false;
    this.ShowInTaskbar = false;
    this.TopMost = true;
    this.WindowState =System.Windows.Forms.FormWindowState.Maximized;
    
    

    修改构造函数为(见截图):

    当窗体显示时,就已经是全屏幕的截图了。

    之后,我们可以在form上面放个PictureBox,然后在PictureBox_Paint里面画线和矩形。
    示例:

    picBox_Paint
    private void picBox_Paint( object sender, PaintEventArgs e ) {
    Graphics g
    = e.Graphics;
    if (isDrawing) {
    //g.DrawRectangle( penRect, captureRect);
    DrawScableRect( captureRect, g );
    }
    else if (!isDrawned) {
    g.DrawLine( penLine,
    0, currentPoint.Y, fullScreen.Width, currentPoint.Y );
    g.DrawLine( penLine, currentPoint.X,
    0, currentPoint.X, fullScreen.Height );
    }
    }

    首先我们需要在鼠标按下时记录矩形的起点,

    if (!isDrawing) {
                        startPoint = e.Location;
                        captureRect.Location = startPoint;
                        isDrawing = true;
                        return;
                    }
    
    

    然后在MouseMove事件里面刷新矩形大小,最后调用this.Refresh(),强制窗体重绘,调用Paint方法。
    最最后是在MouseUp事件里面检测画图是否完成。这样就停止调用this.Refresh()了。
    至于画图形,就要用到Graphics.DrawRectangle()和Graphics.DrawLine(),可以到MSDN查询它们的使用方法。
    在MouseMove事件里面,我们可以检测鼠标的当前位置,然后设置鼠标形状,继而可以实现矩形的拖动以及扩大和缩小。
    在此不详赘述了。

    关于截图保存,我们可以加个MouseDoubleClick事件。在下面,我是直接从原始图片截取矩形覆盖区域的,
    利用orgbmp.Clone( captureRect, screenSnap.PixelFormat )来复制我们需要的区域,这样既保证了截取图片的
    图像质量,也避免了考虑矩形边框问题。

    MouseDoubleClick
    private void SnapForm_MouseDoubleClick( object sender, MouseEventArgs e ) {
    if (e.Button==MouseButtons.Left) {
    Bitmap orgbmp
    = new Bitmap( screenSnap );
    try {
    Bitmap ab
    = orgbmp.Clone( captureRect, screenSnap.PixelFormat );
    if (saveDlg.ShowDialog() == DialogResult.OK) {
    ab.Save( saveDlg.FileName, imgFormat[Path.GetExtension( saveDlg.FileName )] );
    MessageBox.Show(
    "Completed!" );
    }
    }
    catch { }
    finally { orgbmp.Dispose(); }
    }
    }

    这样C#截图就基本说完了,为了美化,我们可以在截图窗体上面放个半透明图层,做出类似QQ截图的效果。
    最后说说在C#里面如何来自定义鼠标的样式,以及定义全局快捷键来实现截图。

    1、定义鼠标的样式:假如你只用C#里面的Managed Code,自定义出的鼠标结果显示出来的会变成单色。
    我们需要user32.dll里面的几个API来实现自定义彩色鼠标样式。

    C# Signature:
    [DllImport("user32.dll")]
    static extern IntPtr LoadCursorFromFile(string lpFileName);
    
    [DllImport( "user32.dll" )]
    public static extern uint DestroyCursor( IntPtr cursorHandle );
    

    这两个都比较简单,我们可以在form_load时加载鼠标句柄,记得最后要在form_closing时释放句柄资源~
    this.Cursor = new Cursor( IntPtr handle )用来实例化鼠标。

    2、全局快捷键或称热键,则需要调用user32里面的另外两个API来实现。

        [DllImport( "user32.dll", SetLastError = true )]
        public static extern bool RegisterHotKey(
                IntPtr hWnd, // handle to window   
                int id, // hot key identifier   
                KeyModifiers fsModifiers, // key-modifier options   
                System.Windows.Forms.Keys vk // virtual-key code   
        );
    
        [DllImport( "user32.dll", SetLastError = true )]
        public static extern bool UnregisterHotKey(
            IntPtr hWnd, // handle to window   
            int id // hot key identifier   
        );
        [Flags]
        public enum KeyModifiers
        {
            None = 0,
            Alt = 1,
            Control = 2,
            Shift = 4,
            Windows = 8
        }
    
    
    

    比如注册F3键为截图快捷键:RegisterHotKey( this.Handle, 7890, IceApi.KeyModifiers.None, Keys.F3 );
    这两个API分别要放到Form_Load和Form_FormClosed事件里面。
    最后,我们需要截获系统消息来为我们定义的快捷键执行相应操作。此时我们需要重写WndProc

    WndProc
    protected override void WndProc( ref Message m ) {
    switch (m.Msg) {
    //hotkey pressed
    case 0x0312:
    if (m.WParam.ToString() == "7890") {
    GlobalKeyProc(
    this.WindowState == FormWindowState.Minimized );
    }
    break;
    }
    base.WndProc( ref m );
    }

    在GlobalKeyProc()里面处理我们需要的工作。

    终于完成了。谢谢观看。

  • 相关阅读:
    机器学习十四--深度学习-卷积
    机器学习十三——垃圾邮件分类2
    机器学习十二----朴素贝叶斯-垃圾邮件分类
    机器学习十一 ——分类与监督学习,朴素贝叶斯分类算法
    机器学习九----主成分分析
    机器学习八——特征选择
    基于D3.js 绘制一个折柱混合图
    一个轮播图
    贪吃蛇
    数组中哪些方法是响应式的
  • 原文地址:https://www.cnblogs.com/1971ruru/p/screencap2.html
Copyright © 2020-2023  润新知