通过IViewObject接口,取浏览器的图象,实现SNAP

今天又见到snap实现的文章,看来对此感兴趣的人挺多的.实现这个功能确实很'眩',我也来做一个把玩一下.
我的做法不是Control.DrawToBitmap,而是直接QueryInterface浏览器Com对象的IViewObject接口,用它实现的Draw方法,画到图象上.

首先定义IViewObject的接口声名,如下:
复制 保存

using System;using System.Collections.Generic;using System.Text;using System.Security;using System.Runtime.InteropServices;using System.Runtime.InteropServices.ComTypes;namespace SnapLibrary{ /// <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)] IAdviseSink pAdvSink); void GetAdvise([In, Out, MarshalAs(UnmanagedType.LPArray)] int[] paspects, [In, Out, MarshalAs(UnmanagedType.LPArray)] int[] advf, [In, Out, MarshalAs(UnmanagedType.LPArray)] IAdviseSink[] pAdvSink); } }}


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

using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices;using System.Drawing;namespace SnapLibrary{ /// <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接口取出:
复制 保存

//获取接口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方法的完整代码:
复制 保存

as SnapLibrary.UnsafeNativeMethods.IViewObject; //调用Draw方法 ViewObject.Draw((int) 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对象的图象抓取.那么现在给它提供一个浏览器的实例,让它实现对webpage的快照吧.
.net2.0提供了webbrowser对象,它是对activex对象的包装,它的使用很简单,这里就不详细说明.
WebBrowser对象的实例的属性ActiveXInstance就是它的原生COM对象,获取它的IVewObject接口,即可调用它实现的Draw方法把网页绘制到指定的DC上.

以下是对webbrowser对象的包装类,结合Snapshot类的类代码:
复制 保存
/// <summary>/// web 页面快照类/// </summary>public class WebPageSnapshot : IDisposable{    string url = "about:blank";    /// <summary>    /// 简单构造一个 WebBrowser 对象    /// 更灵活的应该是直接引用浏览器的com对象实现稳定控制    /// </summary>    WebBrowser wb = new WebBrowser();    /// <summary>    /// URL 地址    /// http://www.cnblogs.com    /// </summary>    public string Url    {        get { return url; }        set { url = value; }    }    int width = 1024;    /**/    /// <summary>    /// 图象宽度    /// </summary>    public int Width    {        get { return width; }        set { width = value; }    }    int height = 768;    /// <summary>    /// 图象高度    /// </summary>    public int Height    {        get { return height; }        set { height = value; }    }    /// <summary>    /// 初始化    /// </summary>    protected void InitComobject()    {        try        {            wb.ScriptErrorsSuppressed = false;            wb.ScrollBarsEnabled = false;            wb.Size = new Size(1024, 768);            wb.Navigate(this.url);            //因为没有窗体,所以必须如此            while (wb.ReadyState != WebBrowserReadyState.Complete)                System.Windows.Forms.Application.DoEvents();            wb.Stop();            if (wb.ActiveXInstance == null)                throw new Exception("实例不能为空");        }        catch (Exception ex)        {            Console.WriteLine(ex.Message);            throw ex;        }    }    /// <summary>    /// 获取快照    /// </summary>    /// <returns>Bitmap</returns>    public Bitmap TakeSnapshot()    {        try        {            InitComobject();            //构造snapshot类,抓取浏览器ActiveX的图象            SnapLibrary.Snapshot snap = new SnapLibrary.Snapshot();            return snap.TakeSnapshot(wb.ActiveXInstance, new Rectangle(0, 0, this.width, this.height));        }        catch (Exception ex)        {            Console.WriteLine(ex.Message);            throw ex;        }    }    public void Dispose()    {        wb.Dispose();    }}

这里提供一个测试用的代码:
复制 保存
class Program{    /// <summary>    /// 测试    /// </summary>    /// <param name="args"></param>    [STAThread]    static void Main(string[] args)    {        //web 页面快照        WebPageSnapshot wps = new WebPageSnapshot();        if (args != null && args.Length > 1)            wps.Url = args[0];        else            wps.Url = "http://www.cnblogs.com";        try        {            //保存到文件            wps.TakeSnapshot().Save("1.bmp");        }        catch (Exception ex)        {            Console.WriteLine(ex.Message);            Console.ReadLine();        }        wps.Dispose();    }}

工程原始代码下载:
http://files.cnblogs.com/chinasf/snaplibrary.rar

当然,这样做可能太复杂了,因为.net为我们简化了所有的工作,简单到任意的contrl对象都支持DrawToBitmap方法.不过想要了解机制的朋友们,可以研究一下.
using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices;using System.Runtime.InteropServices.ComTypes;using System.Drawing;using System.Windows.Forms;namespace SnapLibrary{ /// <summary> /// ActiveX 组件快照类 /// AcitveX 必须实现 IViewObject 接口 /// /// 作者:随飞 /// http://chinasf.cnblogs.com /// chinasf@hotmail.com /// </summary> public class Snapshot { /// <summary> /// 取快照 /// </summary> /// <param name="pUnknown">Com 对象</param> /// <param name="bmpRect">图象大小</param> /// <returns></returns> public Bitmap TakeSnapshot(object pUnknown, Rectangle bmpRect) { if (pUnknown == null) return null; //必须为com对象 if (!Marshal.IsComObject(pUnknown)) return null; //IViewObject 接口 SnapLibrary.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(SnapLibrary.UnsafeNativeMethods.IViewObject)) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值