Asp.Net 之 网页快照

  此文做法不是 Control.DrawToBitmap ,而是直接QueryInterface 浏览器Com对象的 IViewObject 接口,用它实现的Draw方法,画到图像上。

  首先,定义IViewObject的接口声名,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Security;
using System.Runtime.InteropServices;

namespace SnapUtility
{
    /// <summary>
    /// 从 .Net 2.0 的 System.Windows.Forms.Dll 库提取
    /// 版权所有:微软公司
    /// </summary>
    [SuppressUnmanagedCodeSecurity]
    internal static class UnsafeNativeMethods
    {
        public static Guid IID_IViewObject = new Guid("{0000010d-0000-0000-C000-000000000046}");

        [ComImport, Guid("0000010d-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IViewObject
        {
            [PreserveSig]
            int Draw([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] NativeMethods.tagDVTARGETDEVICE ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [In] NativeMethods.COMRECT lprcBounds, [In] NativeMethods.COMRECT lprcWBounds, IntPtr pfnContinue, [In] int dwContinue);
            [PreserveSig]
            int GetColorSet([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] NativeMethods.tagDVTARGETDEVICE ptd, IntPtr hicTargetDev, [Out] NativeMethods.tagLOGPALETTE ppColorSet);
            [PreserveSig]
            int Freeze([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze);
            [PreserveSig]
            int Unfreeze([In, MarshalAs(UnmanagedType.U4)] int dwFreeze);
            void SetAdvise([In, MarshalAs(UnmanagedType.U4)] int aspects, [In, MarshalAs(UnmanagedType.U4)] int advf, [In, MarshalAs(UnmanagedType.Interface)] System.Runtime.InteropServices.ComTypes.IAdviseSink pAdvSink);
            void GetAdvise([In, Out, MarshalAs(UnmanagedType.LPArray)] int[] paspects, [In, Out, MarshalAs(UnmanagedType.LPArray)] int[] advf, [In, Out, MarshalAs(UnmanagedType.LPArray)] System.Runtime.InteropServices.ComTypes.IAdviseSink[] pAdvSink);
        }
    }
}

  该接口.net 自己带了,只是internal形式,所以只有想办法用Reflector 将它弄出来,相关的还有几个类,分别是tagLOGPALETTE,COMRECT,tagDVTARGETDEVICE.
定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Runtime.InteropServices;

namespace SnapUtility
{
    /// <summary>
    /// 从 .Net 2.0 的 System.Windows.Forms.Dll 库提取
    /// 版权所有:微软公司
    /// </summary>
    internal static class NativeMethods
    {
        [StructLayout(LayoutKind.Sequential)]
        public sealed class tagDVTARGETDEVICE
        {
            [MarshalAs(UnmanagedType.U4)]
            public int tdSize;
            [MarshalAs(UnmanagedType.U2)]
            public short tdDriverNameOffset;
            [MarshalAs(UnmanagedType.U2)]
            public short tdDeviceNameOffset;
            [MarshalAs(UnmanagedType.U2)]
            public short tdPortNameOffset;
            [MarshalAs(UnmanagedType.U2)]
            public short tdExtDevmodeOffset;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class COMRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
            public COMRECT() { }

            public COMRECT(Rectangle r)
            {
                this.left = r.X;
                this.top = r.Y;
                this.right = r.Right;
                this.bottom = r.Bottom;
            }

            public COMRECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }

            public static NativeMethods.COMRECT FromXYWH(int x, int y, int width, int height)
            {
                return new NativeMethods.COMRECT(x, y, x + width, y + height);
            }

            public override string ToString()
            {
                return string.Concat(new object[] { "Left = ", this.left, " Top ", this.top, " Right = ", this.right, " Bottom = ", this.bottom });
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public sealed class tagLOGPALETTE
        {
            [MarshalAs(UnmanagedType.U2)]
            public short palVersion;
            [MarshalAs(UnmanagedType.U2)]
            public short palNumEntries;
        }
    }
}

  现在可以通过 Marshal.QueryInterface 将浏览器COM实例的IViewObject接口取出,添加 System.Runtime.InteropServices 命名空间引用,Marshal类位于此命名空间下:

object hret = Marshal.QueryInterface(Marshal.GetIUnknownForObject(pUnknown),           ref UnsafeNativeMethods.IID_IViewObject, out pViewObject);

  pUnknown为 com对象实例,将IViewObject 指针对象 pViewObject 转化为接口对象.

ViewObject = Marshal.GetTypedObjectForIUnknown(pViewObject,           typeof(SnapLibrary.UnsafeNativeMethods.IViewObject)) as SnapLibrary.UnsafeNativeMethods.IViewObject;  

  调用draw方法,绘制到图象上,以下是TakeSnapshot方法的完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;

using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using mshtml;

namespace SnapUtility
{
    /// <summary>
    ///WebSnapshot 的摘要说明
    /// </summary>
    public class WebSnapshot
    {
        public WebSnapshot(){  }

        public static Bitmap GetHtmlImage(Uri uri, int Width)
        {
            WebBrowser webBrowser = new WebBrowser();
            //webBrowser.Size = new Size(Width, 10);
            webBrowser.ScrollBarsEnabled = false;
            webBrowser.Url = uri;
            while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
            {
                Application.DoEvents();
            }

            webBrowser.Width = webBrowser.Document.Body.ClientRectangle.Width;
            webBrowser.Height = webBrowser.Document.Body.ClientRectangle.Height;
            webBrowser.Url = uri;
            Bitmap bitmap = TakeSnapshot(webBrowser.ActiveXInstance, new Rectangle(0, 0, webBrowser.Width, webBrowser.Height));

            webBrowser.Dispose();

            return bitmap;

        }

        /// <summary>
        /// 取快照
        /// </summary>
        /// <param name="pUnknown">Com 对象</param>
        /// <param name="bmpRect">图象大小</param>
        /// <returns></returns>
        public static Bitmap TakeSnapshot(object pUnknown, Rectangle bmpRect)
        {
            if (pUnknown == null)
                return null;
            //必须为com对象
            if (!Marshal.IsComObject(pUnknown))
                return null;

            //IViewObject 接口
            UnsafeNativeMethods.IViewObject ViewObject = null;
            IntPtr pViewObject = IntPtr.Zero;
            //内存图
            Bitmap pPicture = new Bitmap(bmpRect.Width, bmpRect.Height);
            Graphics hDrawDC = Graphics.FromImage(pPicture);
            //获取接口
            object hret = Marshal.QueryInterface(Marshal.GetIUnknownForObject(pUnknown),
                ref UnsafeNativeMethods.IID_IViewObject, out pViewObject);
            try
            {
                ViewObject = Marshal.GetTypedObjectForIUnknown(pViewObject, typeof(UnsafeNativeMethods.IViewObject)) as UnsafeNativeMethods.IViewObject;
                //调用Draw方法
                ViewObject.Draw((int)System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT,
                    -1,
                    IntPtr.Zero,
                    null,
                    IntPtr.Zero,
                    hDrawDC.GetHdc(),
                    new NativeMethods.COMRECT(bmpRect),
                    null,
                    IntPtr.Zero,
                    0);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw ex;
            }
            //释放
            hDrawDC.Dispose();
            return pPicture;
        }
    }
}

  

  到此既完成了对Com对象的图象抓取.那么现在给它提供一个浏览器的实例,让它实现对 web page 的快照。
  .net 2.0提供了webbrowser对象,它是对activex对象的包装,它的使用很简单,这里就不详细说明。
  WebBrowser 对象的实例的属性ActiveXInstance就是它的原生COM对象,获取它的IVewObject接口,即可调用它实现的Draw方法把网页绘制到指定的DC上.

  这里提供一个测试用的代码:

前台页面设置:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" AspCompat="true" %>

后台调用代码:

using System.Drawing;
using System.IO;
using SnapUtility;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Bitmap MyImage = WebSnapshot.GetHtmlImage(new Uri(@"http://www.hao123.com/"), 1024);
        string folder = Server.MapPath("Html/");

        //如果目录不存在则创建目录
        if (!Directory.Exists(folder))
            Directory.CreateDirectory(folder);

        MyImage.Save(folder + DateTime.Now.Ticks.ToString() + ".jpeg");
        MyImage.Dispose();
    }

  当然,这样做可能太复杂了,因为.net 为我们简化了所有的工作,简单到任意的 contrl 对象都支持 DrawToBitmap 方法。不过想要了解机制的朋友们,可以研究一下.

时间: 2024-08-03 14:35:23

Asp.Net 之 网页快照的相关文章

大家好

http://www.yugaopian.com/people/259723 http://www.yugaopian.com/people/259744 http://www.yugaopian.com/people/259783 http://www.yugaopian.com/people/259824 http://www.yugaopian.com/people/259839 http://www.yugaopian.com/people/259933 http://www.yugao

阿哥吗卡怪每次哦阿哥看啦过啦嘎开吃麻辣个啊蓝光

http://www.xx186.com/web/web_kpic.asp?id=156613http://www.xx186.com/web/web_kpic.asp?id=156608http://www.xx186.com/web/web_kpic.asp?id=156605http://www.xx186.com/web/web_kpic.asp?id=156602http://www.xx186.com/web/web_kpic.asp?id=156600http://www.xx18

风格更家霍建华

http://www.9ku.com/fuyin/daogaoo.asp?dgid=119864http://www.9ku.com/fuyin/daogaoo.asp?dgid=119867http://www.9ku.com/fuyin/daogaoo.asp?dgid=119876http://www.9ku.com/fuyin/daogaoo.asp?dgid=119879http://www.9ku.com/fuyin/daogaoo.asp?dgid=119883http://www

,了可美军以本合同个v分

http://shike.gaotie.cn/zhan.asp?zhan=%A1%FE%CE%F7%B0%B2%B8%B4%B7%BD%B5%D8%B7%D2%C5%B5%F5%A5%C6%AC%C4%C4%C0%EF%C2%F2Q%A3%BA%A3%B1%A3%B1%A3%B2%A3%B7%A3%B4%A3%B0%A3%B1%A3%B1%A3%B7%A3%B5%A1%F4 http://shike.gaotie.cn/zhan.asp?zhan=%A8%7D%CD%AD%B4%A8%B8%B4

ASP.NET MVC 使用Remote特性实现远程属性验证

RemoteAttribute是asp.net mvc 的一个验证特性,它位于System.Web.Mvc命名空间 下面通过例子来说明 很多系统中都有会员这个功能,会员在前台注册时,用户名不能与现有的用户名重复,还要求输入手机号码去注册,同时手机号码也需要验证是否重复,下面是实体类 /// <summary> /// 会员 /// </summary> public class Member { public int Id { get; set; } [Required(Error

asp.net264简单汽车小程序

转载于我帮你毕业设计 有需要的可以加Q 97095639 文章在www.hongtaibysj.com 上看到,想查看详细的可以自己去查阅 一.技术实现: 开发语言: asp.net, 框架: mvc ,模式:B/S 数据库 : sqlserver , 开发工具: vs sqlserver . 论文字数:1万左右. 二.功能实现: 三.系统截图

搭建连接MySql的三层架构的ASP.NetCore2.0的WebApi

里我们用三层架构搭建一个连接MySql的ASP.netCore模板的WebApi项目 首先添加WebApi项目(ASP.NetCore版本) 右键解决方案>新建项目> 选择Web>ASP.NET Core Web应用程序(.NET Core) 选择Web API 此时的目录结构: 添加实体层Entity 右键添加>新建项目>.Net Core类库 添加后的目录结构 BaseEntity: using System; using System.Collections.Gener

微微信.NET:开源的ASP.NET微信公众号应用平台

题记: 平时喜欢使用 C# 编程,近半年玩微信公众平台,看到一些微信的应用系统大多是PHP.Python的,于是就有想法做一套开放的 C# ASP.NET的微信应用系统. 微微信.NET  基于ASP.NET的开源微信应用平台  --(面向客户完全开源 永久免费代码更新 游戏和点餐菜单正在完善中 很多功能继续优化) 微微信.NET  系统设计架构         本系统基于文件系统和内存数据库,标准板运行于.NET4,完全版需要单独的服务器或者VPS支持          本系统是一个开源系统,

95后实习生的远程办公体验(asp.net mvc\C#技术栈)

这个月我们做了一件别人看起来很疯狂的事情,就是让一批95后的实习生实行远程办公,效果还不错,于是写此文总结一下. 其实认真算算,我自己的远程工作经验有十年了吧,在北京工作的时候天气不好就申请在家办公,在硅谷的时候每周有三天在家办公,两天去办公室办公.所以我也算得上是远程办公的老司机了吧. 不过,我之前都是对有多年工作经验的老司机才实行远程办公,还从来没有对还未毕业的实习生实行过.老实说,不敢啊,也不放心,况且我在cnblogs博客园呆了十年,还真没见过对还未毕业的实习生实行过远程办公的. 那为什