开发者社区> 老朱教授> 正文

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

简介:
+关注继续查看

通过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(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();
}



}

工程原始代码下载:
/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;
}

红色加粗位置.



本文转自suifei博客园博客,原文链接:http://www.cnblogs.com/Chinasf/archive/2006/12/25/603294.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
使用SAP Analytics Path Framework通过图表和表格方式展示CDS view数据
使用SAP Analytics Path Framework通过图表和表格方式展示CDS view数据
17 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
18352 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
24938 0
通过Newtonsoft.Json把类放在ViewState里再解析
接收 string queryClause = ViewState["serQueryClause"].ToString(); msAstQuery = (MsQuery)JavaScriptConvert.
527 0
使用S/4HANA里的Smart Business消费通过CDS view暴露的OData服务
In my previous blog Build Chart and Table representation via Analytics Path Framework
22 0
背水一战 Windows 10 (65) - 控件(WebView): 对 WebView 中的内容截图, 通过 Share Contract 分享 WebView 中的被选中的内容
原文:背水一战 Windows 10 (65) - 控件(WebView): 对 WebView 中的内容截图, 通过 Share Contract 分享 WebView 中的被选中的内容 [源码下载] 背水一战 Windows 10 (65) - 控件(WebView): 对 WebView 中...
1019 0
使用SAP Analytics Path Framework通过图表和表格方式展示CDS view数据
使用SAP Analytics Path Framework通过图表和表格方式展示CDS view数据
15 0
+关注
3546
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载