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

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

首先定义IViewObject的接口声名,如下:
IVewObject接口声明
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方法的完整代码:
Snapshot类
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)) 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对象的图象抓取.那么现在给它提供一个浏览器的实例,让它实现对 web page 的快照吧.
.net 2.0提供了webbrowser对象,它是对activex对象的包装,它的使用很简单,这里就不详细说明.
WebBrowser 对象的实例的属性ActiveXInstance就是它的原生COM对象,获取它的IVewObject接口,即可调用它实现的Draw方法把网页绘制到指定的DC上.

以下是对webbrowser对象的包装类,结合Snapshot 类的类代码:
web 页面快照类
/**//// <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(1024768);
                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(00this.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();
        }



    }

工程原始代码下载:
/Files/Chinasf/SnapLibrary.rar

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

2006年12月26日 8:55:14 修正:请到Snapshot类中增加一句释放引用接口的代码.
Snapshot..
             try
            
{
                
//ViewObject = Marshal.GetObjectForIUnknown(pViewObject) as SnapLibrary.UnsafeNativeMethods.IViewObject;
                ViewObject = Marshal.GetTypedObjectForIUnknown(pViewObject, typeof(SnapLibrary.UnsafeNativeMethods.IViewObject)) 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);
                Marshal.Release(pViewObject);
            }

            
catch  (Exception ex)
            
{
                Console.WriteLine(ex.Message);
                
throw ex;
            }

红色加粗位置.

转载于:https://www.cnblogs.com/zjoch/archive/2010/05/16/1736769.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值