private
void
btn_Find_Click(
object
sender, EventArgs e)
{
//find all the input controls on the page
if (this.wb_Show.Document == null)
{
MessageBox.Show("请先打开一个要分析的网页");
return;
}
string type = this.cbx_type.Text.ToLower().Trim();
if (type == "")
{
MessageBox.Show("请选择一个分析对象的类型");
return;
}
this.lb_InputList.Items.Clear();
HtmlDocument htm = this.wb_Show.Document;
HtmlElementCollection all = htm.All;
for (int i = 0; i < all.Count; i++)
{
HtmlElement elem = all[i];
if (elem.TagName.ToLower() == type)
{
this.lb_InputList.Items.Add(elem.Name);
}
}
MessageBox.Show("总共找到" + this.lb_InputList.Items.Count.ToString() + "个符合条件的结果");
}
{
//find all the input controls on the page
if (this.wb_Show.Document == null)
{
MessageBox.Show("请先打开一个要分析的网页");
return;
}
string type = this.cbx_type.Text.ToLower().Trim();
if (type == "")
{
MessageBox.Show("请选择一个分析对象的类型");
return;
}
this.lb_InputList.Items.Clear();
HtmlDocument htm = this.wb_Show.Document;
HtmlElementCollection all = htm.All;
for (int i = 0; i < all.Count; i++)
{
HtmlElement elem = all[i];
if (elem.TagName.ToLower() == type)
{
this.lb_InputList.Items.Add(elem.Name);
}
}
MessageBox.Show("总共找到" + this.lb_InputList.Items.Count.ToString() + "个符合条件的结果");
}
这一段代码是用来触发按钮事件的,注意使用的是click参数
if
(elem.Name.ToLower()
==
login.Attributes[
"
name
"
].Value)
{
//elem.InvokeMember("click");
elem.InvokeMember("click");
}
{
//elem.InvokeMember("click");
elem.InvokeMember("click");
}
对于form的提交,得使用submit
else
{
XmlNode form = FindNode("form");
htm.Forms[form.Attributes["name"].Value].InvokeMember("submit");
}
获取值,和赋值类似
XmlNode userName
=
FindNode(
"
userName
"
);
if (elem.TagName.ToLower() == " input " && elem.Name.ToLower() == userName.Attributes[ " name " ].Value)
{
elem.InnerText = userName.InnerText;
}
if (elem.TagName.ToLower() == " input " && elem.Name.ToLower() == userName.Attributes[ " name " ].Value)
{
elem.InnerText = userName.InnerText;
}
可以使用这些简单的应用做一个网页自动登录之类的系统
2. 实现WebBrowser控件的HTML源代码读写
思路其实很简单,直接通过document.documentElement.outerHTML
或者使用IPersistStreamInit接口直接对流进行处理
前者我就不废话了,后者实现方法如下
首先是写入HTML到已初始化的WebBrowser控件
初始化可以通过Navigate("about:blank")完成
必须确保WebBrowser.Document != null
否则应该推迟到DocumentComplete事件再读写
UCOMIStream stream = null
;
CreateStreamOnHGlobal(Marshal.StringToHGlobalUni(value), true, out stream);
if(stream != null )
{
IPersistStreamInit persistentStreamInit =
(IPersistStreamInit)WebBrowser.Document;
persistentStreamInit.InitNew();
persistentStreamInit.Load(stream);
persistentStreamInit = null;
}
CreateStreamOnHGlobal(Marshal.StringToHGlobalUni(value), true, out stream);
if(stream != null )
{
IPersistStreamInit persistentStreamInit =
(IPersistStreamInit)WebBrowser.Document;
persistentStreamInit.InitNew();
persistentStreamInit.Load(stream);
persistentStreamInit = null;
}
UCOMIStream是COM中IStream的CLR版本
CreateStreamOnHGlobal函数从一个字符串的地址
创建一个IStream供使用
[DllImport("ole32.dll", PreserveSig=false
)]
static extern void CreateStreamOnHGlobal(IntPtr hGlobal,
Boolean fDeleteOnRelease, [Out] out UCOMIStream pStream);
static extern void CreateStreamOnHGlobal(IntPtr hGlobal,
Boolean fDeleteOnRelease, [Out] out UCOMIStream pStream);
然后就是通过IPersistStreamInit接口初始化并载入HTML源码,
IPersistStreamInit接口CLR缺省没有导入,定义如下
[ComVisible(true), ComImport(), Guid("7FD52380-4E07-101B-AE2D-08002B2EC713"
),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistStreamInit
{
void GetClassID([In, Out] ref Guid pClassID);
[return: MarshalAs(UnmanagedType.I4)] [PreserveSig]
int IsDirty();
void Load([In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pstm);
void Save([In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pstm,
[In, MarshalAs(UnmanagedType.I4)] int fClearDirty);
void GetSizeMax([Out, MarshalAs(UnmanagedType.LPArray)] long pcbSize);
void InitNew();
}
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistStreamInit
{
void GetClassID([In, Out] ref Guid pClassID);
[return: MarshalAs(UnmanagedType.I4)] [PreserveSig]
int IsDirty();
void Load([In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pstm);
void Save([In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pstm,
[In, MarshalAs(UnmanagedType.I4)] int fClearDirty);
void GetSizeMax([Out, MarshalAs(UnmanagedType.LPArray)] long pcbSize);
void InitNew();
}
读取HTML也是类似思路,将HTML源码写到一个IStream中
然后转换成字符串供C#代码使用,不过实现方式比较麻烦
比较简单的方法还是使用ole32.dll提供的函数
重建流,但这需要预先设定流的长度,如
UCOMIStream stream = null
;
CreateStreamOnHGlobal(Marshal.AllocHGlobal(4096), true, out stream);
IPersistStreamInit persistentStreamInit =
(IPersistStreamInit)WebBrowser.Document;
persistentStreamInit.Save(stream, 0 );
persistentStreamInit = null ;
IntPtr pStr;
GetHGlobalFromStream(stream, out pStr);
return Marshal.PtrToStringAnsi(pStr);
CreateStreamOnHGlobal(Marshal.AllocHGlobal(4096), true, out stream);
IPersistStreamInit persistentStreamInit =
(IPersistStreamInit)WebBrowser.Document;
persistentStreamInit.Save(stream, 0 );
persistentStreamInit = null ;
IntPtr pStr;
GetHGlobalFromStream(stream, out pStr);
return Marshal.PtrToStringAnsi(pStr);
然后使用GetHGlobalFromStream函数和
Marshal.PtrToStringAnsi将流转换为字符串
另外一种方法是自行实现一个支持IStream接口的类
通过流的方式灵活完成读取操作,我比较喜欢这种
using(MemoryStream stream = new
MemoryStream())
{
ComStreamAdapter adapter = new ComStreamAdapter(stream);
IPersistStreamInit persistentStreamInit =
(IPersistStreamInit)WebBrowser.Document;
persistentStreamInit.Save(adapter, 0);
stream.Seek(0, SeekOrigin.Begin);
using(StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
{
ComStreamAdapter adapter = new ComStreamAdapter(stream);
IPersistStreamInit persistentStreamInit =
(IPersistStreamInit)WebBrowser.Document;
persistentStreamInit.Save(adapter, 0);
stream.Seek(0, SeekOrigin.Begin);
using(StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
这里的ComStreamAdapter是一个使用了adapter模式的类
将普通的System.IO.Stream转换为IStream支持的类
public class
ComStreamAdapter : UCOMIStream
{
private Stream _stream;
public ComStreamAdapter(Stream stream)
{
_stream = stream;
}
UCOMIStream Members#region UCOMIStream Members
public void Commit(int grfCommitFlags)
{
}
public void Clone(out UCOMIStream ppstm)
{
ppstm = null;
}
public void CopyTo(UCOMIStream pstm, long cb, System.IntPtr pcbRead, Syste
m.IntPtr pcbWritten)
{
}
public void Revert()
{
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
}
public void Seek(long dlibMove, int dwOrigin, System.IntPtr plibNewPositio
n)
{
_stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
if(plibNewPosition != IntPtr.Zero)
{
Marshal.WriteInt32(plibNewPosition, (int)_stream.Position);
}
}
public void Read(byte[] pv, int cb, System.IntPtr pcbRead)
{
int size = _stream.Read(pv, (int)_stream.Position, cb);
if(pcbRead != IntPtr.Zero)
{
Marshal.WriteInt32(pcbRead, size);
}
}
public void Write(byte[] pv, int cb, System.IntPtr pcbWritten)
{
_stream.Write(pv, 0, cb);
if(pcbWritten != IntPtr.Zero)
{
Marshal.WriteInt32(pcbWritten, cb);
}
}
public void SetSize(long libNewSize)
_stream.SetLength(libNewSize);
}
public void Stat(out STATSTG pstatstg, int grfStatFlag)
{
pstatstg = new STATSTG ();
}
#endregion
}
{
private Stream _stream;
public ComStreamAdapter(Stream stream)
{
_stream = stream;
}
UCOMIStream Members#region UCOMIStream Members
public void Commit(int grfCommitFlags)
{
}
public void Clone(out UCOMIStream ppstm)
{
ppstm = null;
}
public void CopyTo(UCOMIStream pstm, long cb, System.IntPtr pcbRead, Syste
m.IntPtr pcbWritten)
{
}
public void Revert()
{
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
}
public void Seek(long dlibMove, int dwOrigin, System.IntPtr plibNewPositio
n)
{
_stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
if(plibNewPosition != IntPtr.Zero)
{
Marshal.WriteInt32(plibNewPosition, (int)_stream.Position);
}
}
public void Read(byte[] pv, int cb, System.IntPtr pcbRead)
{
int size = _stream.Read(pv, (int)_stream.Position, cb);
if(pcbRead != IntPtr.Zero)
{
Marshal.WriteInt32(pcbRead, size);
}
}
public void Write(byte[] pv, int cb, System.IntPtr pcbWritten)
{
_stream.Write(pv, 0, cb);
if(pcbWritten != IntPtr.Zero)
{
Marshal.WriteInt32(pcbWritten, cb);
}
}
public void SetSize(long libNewSize)
_stream.SetLength(libNewSize);
}
public void Stat(out STATSTG pstatstg, int grfStatFlag)
{
pstatstg = new STATSTG ();
}
#endregion
}
3.一些应用
1:如何处理弹出新页面的事件(总是在我的浏览器里面现实新页面)
2:如何处理window.close事件,让我的浏览器页关闭
3:让html页面的js调用我的browse的函数
4:如何让我的browse调用html的js函数。
使用场景:一个web程序,让用户使用自定义浏览器来浏览,该web程序会调用浏览者机器上一些接口。
我的这个浏览器叫做AppBrowser。
关于ObjectForScripting 的介绍http://msdn2.microsoft.com/en-us/library/system.windows.forms.webbrowser.objectforscripting.aspx
首先,第一个问题。
如果只是放置一个browse在那里,在html中打开新页面的时候,他默认使用IE或者其他浏览器来打开网页。如果想要让我的browse也同时能处理所有的新开页面,就要增加一个对_NewWindow事件的处理。
private void wb_Container_NewWindow(object sender, CancelEventArgs e)
{
e.Cancel = true;
AppBrowser newAB = new AppBrowser(wb_Container.Url.ToString());
newAB.Show();
}
在这里要注意的是
1:e.Cancel = true;是为了取消这个事件,不然又打开一个IE
2:wb_Container.Url.接受到的是新页面的参数
关于关闭浏览器
通常,如果设置了这样的js:window.close,那么,IE会自动关闭。但是我的browse却不会,至少默认的是如此的。
为了关闭我的浏览器,我需要接收这个函数。但是,很可惜,找了半天都没找到这个事件在那里处理,于是结合下一个问题,一下子解决了。(其实是半个解决,只有自己写的web程序才能处理)。
关于web调用我的浏览器的函数。
这就成了web和win的交互了,这个win就是在客户端的。以前交互的方式是写一个ActiveX控件,让web调用他,进而访问客户机器上的一些资源。现在的这种方式则是通过自己提供一个符合COM接口的自定义browse来实现。
1:我的browse必须是符合COM接口的[System.Runtime.InteropServices.ComVisibleAttribute(true)]
2:设置一个属性 this.wb_Container.ObjectForScripting = this
这样,Web中就可以这样调用了javascript:window.external.xxx('xx')。比方说上边的那个关闭窗口的调用就可以这样写:
οnclick="javascript:window.external.close();"
这个调用,其实是调用的我的browse的Close函数。这个函数是我的winForm上默认的那一个函数。调用其他函数亦然,只要是公开方法就可以。
关于如何browse调用web页面中的函数。
第一个,可以通过直接调用页面中元素的方式来实现,在我上一篇里面有所介绍。
第二个,就是可以直接访问.Document.InvokeScript函数来实现。
比如:
public object InvokeHtmlJsScript(string scriptName,object[] objects)
{
return this.wb_Container.Document.InvokeScript(scriptName, objects);
}
简单的类
1 //set this class as a COM
2 [System.Runtime.InteropServices.ComVisibleAttribute(true)]
3 public partial class AppBrowser : Form
4 {
5 public AppBrowser()
6 {
7 InitializeComponent();
8 }
9 /**//// <summary>
10 /// which the url will be go
11 /// </summary>
12 /// <param name="url"></param>
13 public AppBrowser(string url)
14 {
15 InitializeComponent();
16 _url = url;
17 this.wb_Container.Navigate(_url);
18 this.wb_Container.ObjectForScripting = this;//set this to be the COM handler
19
20 }
21 private string _url;
22 public string Url
23 {
24 get { return this._url; }
25 }
26 /**//// <summary>
27 /// open new page in the window which is also in my brower but not in IE
28 /// </summary>
29 /// <param name="sender"></param>
30 /// <param name="e"></param>
31 private void wb_Container_NewWindow(object sender, CancelEventArgs e)
32 {
33 e.Cancel = true;
34 AppBrowser newAB = new AppBrowser(wb_Container.Url.ToString());
35 newAB.Show();
36 }
37 /**//// <summary>
38 /// this function can be invoked by js in html
39 /// like this 'javascript:window.external.ShowMessage('this is invoke from web');'
40 /// </summary>
41 /// <param name="msg"></param>
42 public void ShowMessage(string msg)
43 {
44 MessageBox.Show(msg);
45 }
46 /**//// <summary>
47 /// can invoke script in the html showing in the webbrowser
48 /// </summary>
49 /// <param name="scriptName"></param>
50 /// <param name="objects"></param>
51 /// <returns></returns>
52 public object InvokeHtmlJsScript(string scriptName,object[] objects)
53 {
54 return this.wb_Container.Document.InvokeScript(scriptName, objects);
55 }
56 }
1 //set this class as a COM
2 [System.Runtime.InteropServices.ComVisibleAttribute(true)]
3 public partial class AppBrowser : Form
4 {
5 public AppBrowser()
6 {
7 InitializeComponent();
8 }
9 /**//// <summary>
10 /// which the url will be go
11 /// </summary>
12 /// <param name="url"></param>
13 public AppBrowser(string url)
14 {
15 InitializeComponent();
16 _url = url;
17 this.wb_Container.Navigate(_url);
18 this.wb_Container.ObjectForScripting = this;//set this to be the COM handler
19
20 }
21 private string _url;
22 public string Url
23 {
24 get { return this._url; }
25 }
26 /**//// <summary>
27 /// open new page in the window which is also in my brower but not in IE
28 /// </summary>
29 /// <param name="sender"></param>
30 /// <param name="e"></param>
31 private void wb_Container_NewWindow(object sender, CancelEventArgs e)
32 {
33 e.Cancel = true;
34 AppBrowser newAB = new AppBrowser(wb_Container.Url.ToString());
35 newAB.Show();
36 }
37 /**//// <summary>
38 /// this function can be invoked by js in html
39 /// like this 'javascript:window.external.ShowMessage('this is invoke from web');'
40 /// </summary>
41 /// <param name="msg"></param>
42 public void ShowMessage(string msg)
43 {
44 MessageBox.Show(msg);
45 }
46 /**//// <summary>
47 /// can invoke script in the html showing in the webbrowser
48 /// </summary>
49 /// <param name="scriptName"></param>
50 /// <param name="objects"></param>
51 /// <returns></returns>
52 public object InvokeHtmlJsScript(string scriptName,object[] objects)
53 {
54 return this.wb_Container.Document.InvokeScript(scriptName, objects);
55 }
56 }
后记:经过测试,终于找到了一种方法可以解决window.close的问题了(第二个问题)
1
private
void
wb_Container_DocumentCompleted(
object
sender, WebBrowserDocumentCompletedEventArgs e)
2 {
3 wb_Container.Document.Window.Unload += new HtmlElementEventHandler(Window_Unload);
4 }
5
6 void Window_Unload( object sender, HtmlElementEventArgs e)
7 {
8 if (this.wb_Container.Document == null)
9 this.Close();
10 }
2 {
3 wb_Container.Document.Window.Unload += new HtmlElementEventHandler(Window_Unload);
4 }
5
6 void Window_Unload( object sender, HtmlElementEventArgs e)
7 {
8 if (this.wb_Container.Document == null)
9 this.Close();
10 }
原理:
1:代理window的unload事件。这个事件在页面卸载的时候触发。
2:在这个事件之后检查webbrowser的值。如果是window.close,那么属性为空。
可能这个方法还是不够好,但是现下可用了。