浅析如何用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()里面处理我们需要的工作。

 

终于完成了。谢谢观看。

posted @ 2010-03-19 12:23  一修先生  阅读(4015)  评论(6编辑  收藏  举报