.NET 2.0 Webbrowser 一些用法

来源:互联网 发布:java web 2016就业 编辑:程序博客网 时间:2024/04/30 01:42
 
1。分析网页上的元素

        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() + "个符合条件的结果");
        }

这一段代码是用来触发按钮事件的,注意使用的是click参数
                     if (elem.Name.ToLower() == login.Attributes["name"].Value)
                     
{
                         
//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;
                }


可以使用这些简单的应用做一个网页自动登录之类的系统

2.实现WebBrowser控件的HTML源代码读写
思路其实很简单,直接通过document.documentElement.outerHTML
 或者使用IPersistStreamInit接口直接对流进行处理
 前者我就不废话了,后者实现方法如下
  
 首先是写入HTML到已初始化的WebBrowser控件
 初始化可以通过Navigate("about:blank")完成
 必须确保WebBrowser.Document != null
 否则应该推迟到DocumentComplete事件再读写
  
 
UCOMIStream stream = null;
  
 CreateStreamOnHGlobal(Marshal.StringToHGlobalUni(value), 
trueout
 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);


  
 然后就是通过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();
 }


  
 读取HTML也是类似思路,将HTML源码写到一个IStream中
 然后转换成字符串供C#代码使用,不过实现方式比较麻烦
  
 比较简单的方法还是使用ole32.dll提供的函数
 重建流,但这需要预先设定流的长度,如
  

UCOMIStream stream = null;
  
 CreateStreamOnHGlobal(Marshal.AllocHGlobal(
4096), trueout
 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模式的类
 将普通的System.IO.Stream转换为IStream支持的类
  
   

  public class ComStreamAdapter : UCOMIStream
     
{
       
private
 Stream _stream;


  
       
public
 ComStreamAdapter(Stream stream)
       
{
         _stream 
=
 stream;
       }

  
       
UCOMIStream Members

  
       
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')。比方说上边的那个关闭窗口的调用就可以这样写:
onclick="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);
        }


简单的类


后记:经过测试,终于找到了一种方法可以解决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        }

原理:
1:代理window的unload事件。这个事件在页面卸载的时候触发。
2:在这个事件之后检查webbrowser的值。如果是window.close,那么属性为空。
可能这个方法还是不够好,但是现下可用了。