.NET 2.0 Webbrowser 一些用法

 
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')。比方說上邊的那個關閉窗口的調用就可以這樣寫:
ο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);
        }


簡單的類


後記:經過測試,終於找到了一種方法可以解決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,那麼屬性爲空。
可能這個方法還是不夠好,但是現下可用了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章