C# CEF 使用内置devtools对整个网页截图

22.01.19 更新:刚刚发现这个并不是整页截图。这个CaptureScreenshotAsync如果不事先设置的话确实默认只能截取当前视口范围内显示的图像,如果要整页截图的话得先调用 CefSharp.DevTools.Emulation.EmulationClient.SetDeviceMetricsOverrideAsync开启设备模拟,把模拟设备分辨率的高度改成和页面高度一致,然后再执行前者方法来截图。截图完了再调用 CefSharp.DevTools.Emulation.EmulationClient.ClearDeviceMetricsOverrideAsync关闭设备模拟。手头没有 C# 的开发环境,代码暂时先不贴了。

21.01.18更新:CSDN论坛的大佬给出了一个解决方案,可以多次调用截图了。这里也贴出来供参考。
这里改成了用全局变量的PageClient来暂存页面对象。因为如果把这个定义语句放在方法里面的话还是会报错“Generated MessageID 100002 doesn’t match returned Message Id 100001”,所以要放在全局变量里面才行。

        CefSharp.DevTools.Page.PageClient pageClien= null;
        private async void invokeCapture()
        {
           if(pageClien==null)
            {
                pageClien =  webBrowser.GetBrowser().GetDevToolsClient().Page;
            }

            var result = await pageClien.CaptureScreenshotAsync();
            
            if (result.Data != null)
            {

                MemoryStream ms = new MemoryStream(result.Data);
                ms.Write(result.Data, 0, result.Data.Length);

                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "PNG图片 (*.PNG)|*.PNG";
                DialogResult dresult = dialog.ShowDialog();
                if (dresult == DialogResult.OK)
                {
                    string path = dialog.FileName;
                    try
                    {
                        File.WriteAllBytes(path, result.Data);
                        MessageBox.Show(path + "保存成功。");
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(path + "保存失败!错误信息:" + e.Message);
                    }
                }
            }
        }

这样做的一个好处是不需要再调用Win32底层API。直接走CEF组件的方法就可以截图,也不需要专门整个OffScreen的组件,毕竟还要复制和继承,并且也占内存。而且即使是CEF视窗超出屏幕,或者被其他窗口挡住,甚至使用特殊手段把窗口调大到大于屏幕的分辨率,此方法也可以截取得到。
但是这个办法还只能截一次图,不能截多次,必须退出重开才能继续截图。第二次截图会报错“Generated MessageID 100002 doesn’t match returned Message Id 100001”。网上尚无解决方案,包括外国社区。鄙人就此问题已在StackOverflow提问。
把Github上面那个代码https://github.com/cefsharp/CefSharp/blob/master/CefSharp.Example/DevTools/DevToolsExtensions.cs拷过来,放到项目里面,改一下命名空间。然后就可以对CEF控件直接调用了。这里用的是WinForm显示的GUI。然后在代码里面这样写方法就可以调用了。

        private async void invokeCapture()
        {
            try
            {
                byte[] result = await CefSharp.Example.DevTools.DevToolsExtensions.CaptureScreenShotAsPng(browser); // browser是CEF控件实例
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "PNG图片 (*.PNG)|*.PNG";
                DialogResult dresult = dialog.ShowDialog();
                if (dresult == DialogResult.OK)
                {
                    string path = dialog.FileName;
                    try
                    {
                        File.WriteAllBytes(path, result);
                        MessageBox.Show(path + "保存成功。");
                        
                    } catch (Exception e)
                    {
                        MessageBox.Show(path + "保存失败!错误信息:" + e.Message);
                    }
                }

            }
            catch (Exception ee)
            {
                MessageBox.Show("目前暂时只支持截一次图,暂时不支持截更多次数的图片,如果要继续截图得退出程序重开。作者确实没法解决这个问题了,谁有好的想法也欢迎提出来,具体详情请关注https://stackoverflow.com/questions/65334430/message-id-went-wrong-when-using-cef-devtools-executedevtoolsmethodasync-and-pag 。相关技术细节:" + ee.Message, "暂不支持的操作", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
CefSharpCEF在.NET平台下的封装,它提供了一个简单的方式来在.NET程序中嵌入Chromium浏览器。 在CefSharp中,我们可以通过调用CefBrowser的GetHost方法获取到CefBrowserHost对象,然后通过调用CefBrowserHost的GetImage方法来获取当前网页截图。但是,对于超长网页,由于GetImage方法只能获取当前可视区域的截图,因此我们需要进行一些额外的操作才能获取整个网页截图。 具体方法如下: 1. 获取网页高度 ``` int totalHeight = (int)(browser.ExecuteScriptAsync("return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight);").Result); ``` 这里我们调用JavaScript代码来获取网页的高度。 2. 分段获取截图整个网页分为多个部分,分别获取每个部分的截图,然后将这些截图拼接起来就可以得到整个网页截图。 ``` int partHeight = 32767; // 每个部分的高度 int yOffset = 0; Bitmap bitmap = new Bitmap(1, 1); Graphics graphics = Graphics.FromImage(bitmap); while (yOffset < totalHeight) { int height = Math.Min(partHeight, totalHeight - yOffset); int width = (int)(browser.Width / browser.ZoomLevel); int x = (int)(browser.HorizontalScroll.Value / browser.ZoomLevel); int y = (int)((yOffset + browser.VerticalScroll.Value) / browser.ZoomLevel); // 获取当前部分的截图 Bitmap partBitmap = new Bitmap(width, height, graphics); Rectangle rect = new Rectangle(x, y, width, height); graphics.CopyFromScreen(browser.PointToScreen(rect), new Point(0, 0), rect.Size); partBitmap = partBitmap.Clone(new Rectangle(0, 0, partBitmap.Width, partBitmap.Height), PixelFormat.Format32bppArgb); // 拼接截图 fullBitmap = CombineBitmaps(fullBitmap, partBitmap, yOffset); yOffset += partHeight; } ``` 在以上代码中,我们使用了CombineBitmaps方法来将多个部分的截图拼接起来。 ``` private static Bitmap CombineBitmaps(Bitmap bitmap1, Bitmap bitmap2, int yOffset) { if (bitmap1 == null) { return bitmap2; } Bitmap bitmap = new Bitmap(bitmap1.Width, yOffset + bitmap2.Height); using (Graphics g = Graphics.FromImage(bitmap)) { g.DrawImage(bitmap1, new Rectangle(0, 0, bitmap1.Width, bitmap1.Height)); g.DrawImage(bitmap2, new Rectangle(0, yOffset, bitmap2.Width, bitmap2.Height)); } return bitmap; } ``` 通过以上方法,我们就可以实现获取超长网页截图了。需要注意的是,如果网页过长,可能会出现内存不足的问题,因此我们可以根据实际情况调整每个部分的高度。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值