zoukankan      html  css  js  c++  java
  • IE浏览器整页截屏程序

    最近项目中涉及到浏览器整页截屏的功能,有点复杂,研究了一天,终于在IE浏览器下实现,至于其他浏览器,以后再研究。

    所谓整页截屏,就是说把整个页面全部截进去,包括通过滚动才能看到的部分。

    在网上搜了一下,大家用的都是同一种办法:通过滚动页面,截取每一屏的图片,然后再合并成一张整的图片。

    方法是好的,悲催的是,没有一个代码是能正常运行的,相信很多人都有同感!没办法,自己动手,丰衣足食。

    我需要用.NET来实现。分析一下,主要有以下几个技术点:

    1、如何取得浏览器对象。首先要确定IE版本,我用的是IE8浏览器,对象结构和IE6、IE7有点区别。这个可以通过Win32API中的FindWindow函数来实现。

    2、对指定控件截屏。这个可以通过Win32API中的PrintWindow函数来实现,这个函数有一个优点:即使浏览器被其它窗口挡住,也可以正常截屏。但是,如果浏览器窗口最小化了,那就漆黑一片了。。。

    3、合并图片。这个用GDI+可以很方便地实现。在内存中创建一个大的画布,然后将图片从上至下依次画上去即可。

    开始动手。

    首先,创建一个Console应用程序(用Form程序也没关系)。

    (1)添加对System.Drawing和System.Windows.Forms的引用。

    (2)添加对两个COM组件的引用:SHDocVw、MSHTML,并设置属性“嵌入互操作类型”为False(这个很重要,否则无法接下来的程序无法编译通过)。

    (3)将程序入口Main方法标记为[STAThread](这个也很重要,否则接下来的程序会运行失败)。

    然后,加入Win32API类,该类对几个重要的API进行了封装,接下来我们会用到这些API。代码如下:

    复制代码
    using System;
    using System.Runtime.InteropServices;
    
    namespace IECapture
    {
        /// <summary>
        /// Win32API封装类。
        /// </summary>
        internal static class Win32API
        {
            //User32
            [DllImport("User32.dll")]
            public static extern int FindWindow(string lpClassName, string lpWindowName);
            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowDC(IntPtr hWnd);
            [DllImport("User32.dll")]
            public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, int nFlags);
    
            //gdi32
            [DllImport("gdi32.dll")]
            public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop);
            [DllImport("gdi32.dll")]
            public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);
            [DllImport("gdi32.dll")]
            public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
            [DllImport("gdi32.dll")]
            public static extern bool DeleteDC(IntPtr hDC);
            [DllImport("gdi32.dll")]
            public static extern bool DeleteObject(IntPtr hObject);
            [DllImport("gdi32.dll")]
            public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
    
            public struct RECT
            {
                public int left;
                public int top;
                public int right;
                public int bottom;
            }
        }
    }
    复制代码

    最后,加入主程序。代码逻辑如下:

    (1)获取当前IE浏览器对象。

    (2)获取浏览器核心控件的矩形区域,计算整个页面一共有多少屏。

    (3)通过滚动窗口的方式,对每一屏的页面进行截屏。

    (4)将所有图片合并为一张整的图片。

    主程序的源代码如下:

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace IECapture
    {
        class Program
        {
            //必须指定COM线程模型为单线程
            [STAThread]
            static void Main(string[] args)
            {
                //获取浏览器对象
                SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindowsClass();
                var webBrowser = shellWindows.Cast<SHDocVw.WebBrowser>().FirstOrDefault(c => c.Name == "Windows Internet Explorer");
                if (webBrowser == null)
                {
                    Console.WriteLine("当前未打开任何IE浏览器");
                    Console.ReadLine();
                    return;
                }
    
                //查找浏览器核心控件
                IntPtr childHandle1 = Win32API.FindWindowEx(new IntPtr(webBrowser.HWND), IntPtr.Zero, "Frame Tab", IntPtr.Zero);
                IntPtr childHandle2 = Win32API.FindWindowEx(childHandle1, IntPtr.Zero, "TabWindowClass", IntPtr.Zero);
                IntPtr childHandle3 = Win32API.FindWindowEx(childHandle2, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
                IntPtr childHandle4 = Win32API.FindWindowEx(childHandle3, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
                if (childHandle4 == IntPtr.Zero)
                {
                    Console.WriteLine("当前未打开任何IE浏览器");
                    Console.ReadLine();
                    return;
                }
    
                //获取浏览器核心控件的矩形区域
                Win32API.RECT rc = new Win32API.RECT();
                Win32API.GetWindowRect(childHandle4, ref rc);
                int pageHeight = rc.bottom - rc.top;
    
                //获取HTML文档对象
                mshtml.IHTMLDocument2 htmlDoc = (mshtml.IHTMLDocument2)webBrowser.Document;
    
                //计算总共有多少页,以及最后一页的高度
                int pageCount = htmlDoc.body.offsetHeight / pageHeight;
                int lastPageHeight = htmlDoc.body.offsetHeight % pageHeight;
                if (lastPageHeight > 0) pageCount++;
                int scrollBarWidth = pageCount > 1 ? SystemInformation.VerticalScrollBarWidth : 0;
    
                //图片列表,用于放置每一屏的截图
                List<Image> pageImages = new List<Image>();
    
                //一页一页地滚动截图
                htmlDoc.parentWindow.scrollTo(0, 0);
                for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
                {
                    using (Image image = CaptureWindow(childHandle4))
                    {
                        //去掉边框,以及垂直滚动条的宽度
                        Rectangle innerRect = new Rectangle(2, 2, image.Width - scrollBarWidth - 4, image.Height - 4);
                        if (pageCount > 1 && pageIndex == pageCount - 1 && lastPageHeight > 0)
                            innerRect = new Rectangle(2, pageHeight - lastPageHeight + 2, image.Width - scrollBarWidth - 4, lastPageHeight - 4);
    
                        pageImages.Add(GetImageByRect(image, innerRect));
                    }
                    htmlDoc.parentWindow.scrollBy(0, pageHeight);
                }
    
                //拼接所有图片
                Image fullImage = MergeImages(pageImages);
                if (fullImage == null)
                {
                    Console.WriteLine("截屏失败,未获得任何图片");
                    Console.ReadLine();
                    return;
                }
    
                //将截屏图片保存到指定目录
                try
                {
                    string fileName = @"c:\IE整屏截图.png";
                    fullImage.Save(fileName);
                    Console.WriteLine("截屏成功,图片位置:" + fileName);
                }
                finally
                {
                    fullImage.Dispose();
                }
    
                Console.ReadLine();
            }
    
            /// <summary>
            /// 合并图片。
            /// </summary>
            /// <param name="imageList">图片列表。</param>
            /// <returns>合并后的图片。</returns>
            static Image MergeImages(List<Image> imageList)
            {
                if (imageList == null || imageList.Count == 0) return null;
                if (imageList.Count == 1) return imageList[0];
    
                int pageWidth = imageList[0].Width;
                int totalPageHeight = imageList.Sum(c => c.Height);
    
                Bitmap fullImage = new Bitmap(pageWidth, totalPageHeight);
                using (Graphics g = Graphics.FromImage(fullImage))
                {
                    int y = 0;
                    for (int i = 0; i < imageList.Count; i++)
                    {
                        g.DrawImageUnscaled(imageList[i], 0, y, imageList[i].Width, imageList[i].Height);
                        y += imageList[i].Height;
                        imageList[i].Dispose();//释放图片资源
                    }
                }
                return fullImage;
            }
    
            /// <summary>
            /// 获取图片的指定区域。
            /// </summary>
            /// <param name="image">原始图片。</param>
            /// <param name="rect">目标区域。</param>
            /// <returns></returns>
            static Image GetImageByRect(Image image, Rectangle rect)
            {
                if (image == null || rect.IsEmpty) return image;
    
                Bitmap bitmap = new Bitmap(rect.Width, rect.Height);
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.DrawImage(image, 0, 0, rect, GraphicsUnit.Pixel);
                }
                return bitmap;
            }
    
            /// <summary>
            /// 为指定窗口或控件截屏。
            /// </summary>
            /// <param name="hWnd">句柄。</param>
            /// <returns>截屏图片。</returns>
            static Image CaptureWindow(IntPtr hWnd)
            {
                IntPtr hscrdc = Win32API.GetWindowDC(hWnd);
                if (hscrdc == IntPtr.Zero) return null;
    
                Win32API.RECT windowRect = new Win32API.RECT();
                Win32API.GetWindowRect(hWnd, ref windowRect);
                int width = windowRect.right - windowRect.left;
                int height = windowRect.bottom - windowRect.top;
    
                IntPtr hbitmap = Win32API.CreateCompatibleBitmap(hscrdc, width, height);
                IntPtr hmemdc = Win32API.CreateCompatibleDC(hscrdc);
                Win32API.SelectObject(hmemdc, hbitmap);
                Win32API.PrintWindow(hWnd, hmemdc, 0);
    
                Image bmp = Image.FromHbitmap(hbitmap);
                Win32API.DeleteDC(hscrdc);
                Win32API.DeleteDC(hmemdc);
    
                return bmp;
            }
        }
    }
    复制代码

    【总结】

    要想写一个好的整页截屏程序,还是很困难的。就拿本文的程序来说,就存在以下几点不足之处:

    (1)仅在IE8浏览器上测试通过,无法在FireFox、Chrome上运行。即使同是IE家族的IE6、IE7、IE9,我也不敢保证能正常运行,各位同学可以测试一下,并尝试修改,欢迎交流。

    (2)如果有浮动DIV随着页面一起滚动,在每一屏上都会被截屏。

    (3)未考虑水平滚动条的影响。

    (4)未考虑在多线程环境下的应用。

    作者:返回主页 linux运维-loring
    出处:http://www.cnblogs.com/zlf344242525/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    如果文中有什么错误,欢迎指出。以免更多的人被误导。
  • 相关阅读:
    py 6.4
    py 5.31
    Java集合之LinkedHashMap常用方法解析
    Java集合之HashMap常用方法解析
    Java集合之LinkedList常用方法解析
    Java集合之ArrayList常用方法解析
    Java 基础知识
    Java wait和notifyAll的使用,简单的阻塞队列实现
    The main resource set specified [/tmp/tomcat-docbase.5063501203886177649.7000] is not valid
    zuul网关过滤器实现对GET请求的参数替换
  • 原文地址:https://www.cnblogs.com/zlf344242525/p/2631905.html
Copyright © 2011-2022 走看看