c# wpf 利用截屏键实现截屏功能
2021-03-23 00:26
? ? 最近做一个wpf程序需要截图功能,查找资料费了一些曲折,跟大家分享一下。
????先是找到了这样一份代码:
????static?class?ScreenCut
????{
????????public?static?System.Drawing.Bitmap?GetScreenSnapshot()
????????{
????????????System.Drawing.Rectangle?rc = System.Windows.Forms.SystemInformation.VirtualScreen;
????????????System.Drawing.Bitmap?bitmap = new?System.Drawing.Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
????????????using?(System.Drawing.Graphics?g = System.Drawing.Graphics.FromImage(bitmap))
????????????{
????????????????g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, System.Drawing.CopyPixelOperation.SourceCopy);
????????????}
????????????return?bitmap;
????????}
????????public?static?BitmapSource?ToBitmapSource(this?System.Drawing.Bitmap?bmp)
????????{
????????????BitmapSource?returnSource;
????????????try
????????????{
????????????????returnSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
????????????}
????????????catch
????????????{
????????????????returnSource = null;
????????????}
????????????return?returnSource;
????????}
?
????????public?static?BitmapSource?CopyFromScreenSnapshot(BitmapSource?screenSnapshot, Rect?region)
????????{
????????????var?sourceRect = new?System.Drawing.Rectangle((int)region.Left, (int)region.Top, (int)region.Width, (int)region.Height);
????????????var?destRect = new?System.Drawing.Rectangle(0, 0, sourceRect.Width, sourceRect.Height);
?
????????????if?(screenSnapshot != null)
????????????{
????????????????var?bitmap = new?System.Drawing.Bitmap(sourceRect.Width, sourceRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
????????????????using?(System.Drawing.Graphics?g = System.Drawing.Graphics.FromImage(bitmap))
????????????????{
????????????????????g.DrawImage(bitmap, destRect, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, System.Drawing.GraphicsUnit.Pixel);
????????????????}
?
????????????????return?bitmap.ToBitmapSource();
????????????}
?
????????????return?null;
????????}
}
?
调用时包装一下,下面的函数截下屏幕指定区域并保存:
????System.Drawing.Image?takePicture(Rect?wnd, String?saveLocation, String?pictureName)
{ ???
????????BitmapSource?bitmapScr = ScreenCut.CopyFromScreenSnapshot(ScreenCut.ToBitmapSource(ScreenCut.GetScreenSnapshot()),wnd);
????????PngBitmapEncoder?encoder = new?PngBitmapEncoder();
????????encoder.Frames.Add(BitmapFrame.Create(bitmapScr));
????????FileStream?fileStream = new?FileStream(saveLocation + "\\"?+ pictureName ".png", FileMode.Create, FileAccess.Write);
????????encoder.Save(fileStream);
????????fileStream.Close();
}
?
但实际效果保存下来的图是空的,经验证前两个函数没有问题。于是我查资料找到了另一种切割BitmapSource的方法,将第三个函数改成这样:
BitmapSource?CopyFromScreenSnapshot(BitmapSource?screenSnapshot, Rect?region)
{
????????Int32Rect?window?= new Int32Rect(region.X, region.Y, region.Width, region.Height);
????????BitmapSource?bitmapScr = ScreenCut.ToBitmapSource(ScreenCut.GetScreenSnapshot());
????????//计算Stride
????????int?stride = bitmapScr.Format.BitsPerPixel * window.Width / 8;
????????//声明字节数组
????????byte[] data = new?byte[window.Height * stride];
????????//调用CopyPixels
????????bitmapScr.CopyPixels(window, data, stride, 0);
????????PngBitmapEncoder?encoder = new?PngBitmapEncoder();
????return?BitmapSource.Create(window.Width, window.Height, 0, 0, PixelFormats.Bgr32, null, data, stride);
}
?
这样总算能截到屏了。不过后来发现在某些主题下有些程序的窗口在截下的图中是透明的。这个方法还是不够完美。
我想到了利用键盘的截屏键截图再进行剪切的方法。分为三步:1、模拟按下截屏键 2、从剪贴板获取图片 3、截取和保存图片。
对于第一点,利用SendMessage函数发送WM_KEY事件没有用,不过我找到了另一个模拟键盘输入的方法:
using?System.Runtime.InteropServices;
????[DllImport("user32.dll")]
????static?extern?void?keybd_event
????(
????????byte?bVk,// 虚拟键值
????????byte?bScan,// 硬件扫描码 ???
????????uint?dwFlags,// 动作标识 ???
????????IntPtr?dwExtraInfo// 与键盘动作关联的辅加信息 ???
);
?
上面这个函数对本程序发送按钮事件,模拟截屏键的话像下面这样写,wpf需要在项目添加引用System.Windows.Forms
const?int?VK_SNAPSHOT = 0x2C;
????public?void?PrintScreen()
????{
????????keybd_event((byte)VK_SNAPSHOT, 0, 0x0, IntPtr.Zero);//down ?
????????System.Windows.Forms.Application.DoEvents();//强制窗口响应按钮事件
????????keybd_event((byte)VK_SNAPSHOT, 0, 0x2, IntPtr.Zero);//up ?
????????System.Windows.Forms.Application.DoEvents();
}
?
接下来从剪贴板获取图片,wpf需要在项目添加引用System.Windows.Forms??System.Drawing
????if?(System.Windows.Forms.Clipboard.ContainsImage())
????{
???????System.Drawing.Image?image = System.Windows.Forms.Clipboard.GetImage();
}
?
然后剪切。
????System.Drawing.Image?cutPicture(System.Drawing.Image?image, Int32Rect?window)
????{
????????PrintScreen();
????????if?(window.X + window.Width > image.Width)
????????????window.Width = image.Width - window.X;
????????if?(window.Y + window.Height > image.Height)
????????????window.Height = image.Height - window.Y;
????????if?(image == null)
????????{
????????????//新建一个bmp图片
????????????System.Drawing.Image?bitmap = new?System.Drawing.Bitmap(window.Width, window.Height);
????????????//新建一个画板
????????????System.Drawing.Graphics?graphic = System.Drawing.Graphics.FromImage(bitmap);
????????????//设置高质量查值法
????????????graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
????????????//设置高质量,低速度呈现平滑程度
????????????graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
????????????//清空画布并以透明背景色填充
????????????graphic.Clear(System.Drawing.Color.Transparent);
????????????//在指定位置并且按指定大小绘制原图片的指定部分
????????????graphic.DrawImage(image, new?System.Drawing.Rectangle(0, 0, window.Width, window.Height), new?System.Drawing.Rectangle(window.X, window.Y, window.Width, window.Height), System.Drawing.GraphicsUnit.Pixel);
????????????return?bitmap;
????????}
????????else
????????{
????????????return?null;
????????}
}
?
最后包装一下
????System.Drawing.Image?cutScreen(Int32Rect?window)
????{
????????PrintScreen();
????????Thread.Sleep(1000);
????????if?(System.Windows.Forms.Clipboard.ContainsImage())
????????{
????????????System.Drawing.Image?image?=?cutPicture(System.Windows.Forms.Clipboard.GetImage(), Int32Rect?window);
????????????return?image;
????????}
????????else
????????{
????????????Console.WriteLine("clipboard doesn‘t contain picture");
????????????return?null;
????????}
}
System.Drawing.Image类有实例函数Save,保存很简单,就不细说了。
资料来源:
http://www.cnblogs.com/zhouyinhui/archive/2010/08/20/1804762.html
https://www.mgenware.com/blog/?p=285
http://blog.csdn.net/testcs_dn/article/details/39762017
文章标题:c# wpf 利用截屏键实现截屏功能
文章链接:http://soscw.com/index.php/essay/67712.html