C#中實作WebBrowser控件的HTML源代碼讀寫
<a href="http://www.blogcn.com/user8/flier_lu/index.html?id=1125200&run=.0D9CAA6">http://www.blogcn.com/user8/flier_lu/index.html?id=1125200&run=.0D9CAA6</a>
趁周末想折騰一下嵌入ASP.NET的WinForm程式
需要用到WebBrowser控件的HTML源碼讀寫
就把以前的一些代碼片斷移值到C#下
順便發個文章備忘,呵呵
思路其實很簡單,直接通過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;
}
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提供的函數
重建流,但這需要預先設定流的長度,如
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);
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
}