下面来说第三种方法:模拟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()里面处理我们需要的工作。
出处:http://1971ruru.cnblogs.com/