{
//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參數
{
//elem.InvokeMember("click");
elem.InvokeMember("click");
}
對於form的提交,得使用submit
else
{
XmlNode form = FindNode("form");
htm.Forms[form.Attributes["name"].Value].InvokeMember("submit");
}
獲取值,和賦值類似
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事件再讀寫
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供使用
static extern void CreateStreamOnHGlobal(IntPtr hGlobal,
Boolean fDeleteOnRelease, [Out] out UCOMIStream pStream);
然後就是通過IPersistStreamInit接口初始化並載入HTML源碼,
IPersistStreamInit接口CLR缺省沒有導入,定義如下
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提供的函數
重建流,但這需要預先設定流的長度,如
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接口的類
通過流的方式靈活完成讀取操作,我比較喜歡這種
{
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支持的類
{
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的問題了(第二個問題)
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,那麼屬性爲空。
可能這個方法還是不夠好,但是現下可用了。