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)未考慮在多線程環境下的應用。

 

最后附上一個cnblogs.com首頁的截屏,看看效果:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM