最近用到截屏功能,有点小波折,写下心得。
1. CopyFromScreen
最开始使用的方法,如下,还附带了如何截取鼠标形状。但是很快发现了问题,有些窗口的控件不能截取图像。找了很久原因也不清楚,猜测可能是WPF程序。
public static void CaptureEx(string fileName, bool withCursor = false)
{
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
using (Bitmap bitmap = new Bitmap(width, height))
{
using(Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
if (withCursor)
DrawCursor(graphics);
try
{
bitmap.Save(fileName, ImageFormat.Jpeg);
}
catch
{}
graphics.ReleaseHdc(graphics.GetHdc());
}
}
}
private static void DrawCursor(Graphics graphics)
{
CURSORINFO pci = new CURSORINFO();
pci.cbSize = Marshal.SizeOf(pci);
API.GetCursorInfo(ref pci);
if (pci.hCursor == IntPtr.Zero)
return;
Cursor cursor =new Cursor(pci.hCursor);
Point point =new Point(pci.ptScreenPos.X -10, pci.ptScreenPos.Y -10);
cursor.Draw(graphics, new Rectangle(point, cursor.Size));
}
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
public int cbSize;
public int flags;
public IntPtr hCursor;
public Point ptScreenPos;
}
2. SendKeys.SendWait("{PRTSC}")
试了试手工截图,可以解决方法一中的问题,于是想模拟键盘操作。
public static void Capture(string fileName)
{
System.Windows.Forms.SendKeys.SendWait("{PRTSC}");
Image image = Clipboard.GetImage();
if (image == null)
return;
image.Save(fileName, ImageFormat.Jpeg);
}
开始以为可以了,后经测试发现SendKeys.SendWait("{PRTSC}")的效果和SendKeys.SendWait("%{PRTSC}")一样,也就是"Alt"+"PrtSc"。郁闷了,原来Sendkeys还不支持截屏键的使用。Google搜索后发现如下说明,虽然说得不太清楚,或者已经过时了,但是不能用SendKeys截取全屏是事实。
注意 不能用SendKeys将按键消息发送到这样一个应用程序,这个应用程序并没有被设计成在 Microsoft Windows 中运行。Sendkeys 也无法将 PRINT SCREEN 按键 {PRTSC
} 发送到任何应用程序。
3. keybd_event
终于解决了问题。注意一定要加上Application.DoEvents(),否则截图总是会出问题。
Application.DoEvents()表示交出CPU控制权,让系统可以处理队列中的所有Windows消息。比如在大运算量循环内,加Application.DoEvents可以防止界面停止响应。
因为winform的消息循环是一个线程来处理,那么假如你的某个操作比较耗时,那么消息处理得等你这个耗时操作做完了才能继续,而Application.DoEvents方法就是允许你在耗时操作的内部调用它,而去处理消息队列中的消息。
像鼠标移动鼠标点击都是windows消息,如果耗时操作一直进行,那么界面就像死锁一样。
[DllImport("user32.dll", EntryPoint = "keybd_event")]
public static extern void keybd_event(int bVk, int bScan, int dwFlags, int dwExtraInfo);
public const int KEYEVENTF_KEYUP = 0x0002;
public const int KEYEVENTF_KEYDOWN = 0x0000;
public static void Capture(string fileName)
{
keybd_event((int)Keys.PrintScreen, 0, KEYEVENTF_KEYDOWN, 0);
keybd_event((int)Keys.PrintScreen, 0, KEYEVENTF_KEYUP, 0);
Application.DoEvents();
Image image = Clipboard.GetImage();
if (image == null)
return;
image.Save(fileName, ImageFormat.Jpeg);
}