System.Windows.Forms.WebBrowser あれこれ備忘録
別ドメインの <iframe> の中身を参照する
<iframe>要素にアクセスするには、以下のようにします。複数あれば[1]や[2]になっていきます。(MSDN)
HtmlWinfow iframeWindow = webBrowser1.Document.Window.Frames[0];
ただし、<iframe> の先が別のドメインだと、ここでUnauthorizedAccessExceptionが発生します。セキュリティ上の問題(クロスフレームスクリプティング)から禁じられているようです。
これをすり抜けるのが以下コードです。Micsorosft.mshtmlとSHDocVwへの参照が必要です。前者は アセンブリ -> 拡張 にあります。後者はCOMからMicrosoft Internet Controlsを探して追加します。
参考:http://stackoverflow.com/questions/10645143/webbrowsercontrol-unauthorizedaccessexception-when-accessing-property-of-a-fram
using System; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; using SHDocVw; using mshtml; class Test { public static void GetIFrame(WebBrowser browser) { HtmlWindow iframeWindow = browser.Document.Window.Frames; IHTMLWindow2 iframeWindowCom = (IHTMLWindow2)iframeWindow.DomWindow; // iframeの中身 IHTMLDocument2 doc = CrossFrameIE.GetDocumentFromWindow(iframeWindowCom); } /// <summary> /// http://stackoverflow.com/questions/10645143/webbrowsercontrol-unauthorizedaccessexception-when-accessing-property-of-a-fram /// </summary> static class CrossFrameIE { // Returns null in case of failure. public static IHTMLDocument2 GetDocumentFromWindow(IHTMLWindow2 htmlWindow) { if (htmlWindow == null) { return null; } // First try the usual way to get the document. try { IHTMLDocument2 doc = htmlWindow.document; return doc; } catch (COMException comEx) { // I think COMException won't be ever fired but just to be sure ... if (comEx.ErrorCode != E_ACCESSDENIED) { return null; } } catch (UnauthorizedAccessException) { } catch { // Any other error. return null; } // At this point the error was E_ACCESSDENIED because the frame contains a document from another domain. // IE tries to prevent a cross frame scripting security issue. try { // Convert IHTMLWindow2 to IWebBrowser2 using IServiceProvider. IServiceProvider sp = (IServiceProvider)htmlWindow; // Use IServiceProvider.QueryService to get IWebBrowser2 object. Object brws; sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out brws); // Get the document from IWebBrowser2. IWebBrowser2 browser = (IWebBrowser2)(brws); return (IHTMLDocument2)browser.Document; } catch { } return null; } private const int E_ACCESSDENIED = unchecked((int)0x80070005L); private static Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); private static Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"); } // This is the COM IServiceProvider interface, not System.IServiceProvider .Net interface! [ComImport, ComVisible(true), Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IServiceProvider { [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject); } }
IHTMLDocument2 から HtmlDocument に変換
上記コードで取れるのはIHTMLDocument2です。もちろんこのまま扱っても良いですが、ゆとりなのでラッパーに戻したいところです。
以下コードで System.Windows.Forms.HtmlDocument に戻します。戻すメソッド(コンストラクタ)はpublicではないため、リフレクションで呼び出します。
参考:http://www.antoine.st/2009-05.html
static HtmlDocument ConvertCom2Wrapper(IHTMLDocument2 doc) { Type thd = typeof(HtmlDocument); ConstructorInfo[] ci = thd.GetConstructors( BindingFlags.NonPublic | BindingFlags.Instance); HtmlDocument hdoc = (HtmlDocument)ci[0].Invoke(new object[] { null, doc }); return hdoc; }
WebBrowserのスクリーンショットを取る
参考: http://homepage2.nifty.com/nonnon/SoftSample/CS.NET/SampleWebBitmap.html
static Bitmap Screenshot() { Rectangle scroolRectangle = webBrowser.Document.Body.ScrollRectangle; webBrowser.Width = scroolRectangle.Width; webBrowser.Height = scroolRectangle.Height; // WebBrowserのサイズ(WEBページのサイズ)に合わせてBitmap生成 Bitmap bmp = new Bitmap(webBrowser.Width, webBrowser.Height); using (var g = Graphics.FromImage(bmp)) { IntPtr hdc = g.GetHdc(); // WebBrowser(WEBページ)のオブジェクト取得 IntPtr web = Marshal.GetIUnknownForObject(webBrowser.ActiveXInstance); // WebBrowser(WEBページ)のイメージをBitmapにコピー Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); Win32Api.OleDraw(web, Win32Api.DVASPECT_CONTENT, hdc, ref rect); Marshal.Release(web); g.ReleaseHdc(hdc); } return bmp; }
static class Win32Api { public const int DVASPECT_CONTENT = 1; [DllImport("ole32.dll")] public static extern int OleDraw( IntPtr pUnk, int dwAspect, IntPtr hdcDraw, ref Rectangle lprcBounds); }
WebBrowserのスクロール位置を取得
static Point GetScrollPosition(WebBrowser browser) { IHTMLDocument3 doc3 = (IHTMLDocument3)browser.Document.DomDocument; IHTMLElement2 elm = (IHTMLElement2)doc3.documentElement; return new Point(elm.scrollLeft, elm.scrollTop); }
ある要素(HtmlElement)をクリックする
Focusをあらかじめ当てないとダメらしいです。だいぶはまりました。
static void Click(HtmlDocument doc) { HtmlElement elem = doc.GetElementById("piyo"); elem.Focus(); elem.InvokeMember("click"); }
HtmlElementの絶対座標を取得する
親へ親へと辿りながら相対座標を足していきます。
static Point GetAbsolutePosition(HtmlElement elem) { Point pos = Point.Empty; HtmlElement e = elem; while (e != null) { pos.Offset(e.OffsetRectangle.Location); e = e.OffsetParent; } return pos; }