以前書いた記事のように、Microsoft EdgeにはInternet Explorer_Serverクラスのウィンドウがあり、そこからIHTMLDocument2経由でDOM操作することができます。
上記記事ではVBAでコードを書いたわけですが、今回はC#で書き直してみようと思います。
/* Microsoft Edge Automation without WebDriver. add references: UIAutomationClient UIAutomationTypes Microsoft.mshtml for reference: https://support.microsoft.com/en-us/kb/249232 https://msdn.microsoft.com/en-us/library/hh706902.aspx http://blogs.msdn.com/b/windowsappdev/archive/2012/09/04/automating-the-testing-of-windows-8-apps.aspx http://stackoverflow.com/questions/12925748/iapplicationactivationmanageractivateapplication-in-c http://www.pinvoke.net/default.aspx/user32.EnumChildWindows http://www.pinvoke.net/default.aspx/user32.FindWindowEx http://www.pinvoke.net/default.aspx/user32.GetClassName http://www.pinvoke.net/default.aspx/oleacc.ObjectFromLresult http://www.pinvoke.net/default.aspx/user32.RegisterWindowMessage http://www.pinvoke.net/default.aspx/user32.SendMessageTimeout http://www.pinvoke.net/default.aspx/Enums.SendMessageTimeoutFlags */ using System; using System.Text; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Windows.Automation; using mshtml; namespace EdgeAutomation { class Program { public enum ACTIVATEOPTIONS : uint { AO_NONE = 0x00000000, AO_DESIGNMODE = 0x00000001, AO_NOERRORUI = 0x00000002, AO_NOSPLASHSCREEN = 0x00000004 } [Flags] public enum SendMessageTimeoutFlags : uint { SMTO_NORMAL = 0x0000, SMTO_BLOCK = 0x0001, SMTO_ABORTIFHUNG = 0x0002, SMTO_NOTIMEOUTIFNOTHUNG = 0x0008, SMTO_ERRORONEXIT = 0x0020 } public delegate bool Win32Callback(IntPtr hWnd, IntPtr lParam); [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IApplicationActivationManager { IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ACTIVATEOPTIONS options, [Out] out UInt32 processId); IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); } [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] class ApplicationActivationManager : IApplicationActivationManager { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ACTIVATEOPTIONS options, [Out] out UInt32 processId); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("oleacc.dll", PreserveSig=false)] [return: MarshalAs(UnmanagedType.Interface)] public static extern object ObjectFromLresult(UIntPtr lResult, [MarshalAs(UnmanagedType.LPStruct)] Guid refiid, IntPtr wParam); [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)] public static extern uint RegisterWindowMessage(string lpString); [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)] public static extern IntPtr SendMessageTimeout(IntPtr windowHandle, uint Msg, IntPtr wParam, IntPtr lParam, SendMessageTimeoutFlags flags, uint timeout, out UIntPtr result); public static void Main(string[] args) { uint pid; const string edgeUserModelId = "Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge"; const string url = "https://www.bing.com/"; //navigate to www.bing.com ApplicationActivationManager aam = new ApplicationActivationManager(); aam.ActivateApplication(edgeUserModelId, url, ACTIVATEOPTIONS.AO_NONE, out pid); System.Threading.Thread.Sleep(1000); IntPtr hEdge = IntPtr.Zero; hEdge = FindWindowEx((IntPtr)0, (IntPtr)0, "Windows.UI.Core.CoreWindow", "Microsoft Edge"); //get the minimized Edge window if (hEdge == IntPtr.Zero) { //Edge is not minimized AutomationElement root = AutomationElement.RootElement; foreach (AutomationElement child in root.FindAll(TreeScope.Children, PropertyCondition.TrueCondition)) { AutomationElement edge = child.FindFirst(TreeScope.Children, new AndCondition( new PropertyCondition(AutomationElement.ClassNameProperty, "Windows.UI.Core.CoreWindow"), new PropertyCondition(AutomationElement.NameProperty, "Microsoft Edge") ) ); if (edge != null) { hEdge = (IntPtr)edge.Current.NativeWindowHandle; break; } } } if (hEdge != IntPtr.Zero) { Win32Callback childProc = new Win32Callback(EnumWindow); if (EnumChildWindows(hEdge, childProc, (IntPtr)0) == false) { if (pid != 0) { System.Diagnostics.Process pr = System.Diagnostics.Process.GetProcessById((int)pid); if (pr != null) { pr.Kill(); } } } } } //get active HTMLDocument public static bool EnumWindow(IntPtr handle, IntPtr pointer) { StringBuilder buf = new StringBuilder(255); IntPtr ret = GetClassName(handle, buf, buf.Capacity); if (ret != IntPtr.Zero) { string className = buf.ToString(); if (className == "Internet Explorer_Server") { IHTMLDocument2 idoc2 = GetHTMLDocumentFromWindow(handle); if (idoc2 != null) { try { //find the search box and query for 'Microsoft Edge' HTMLDocument doc = (HTMLDocument)idoc2; HTMLInputElement searchBox = (HTMLInputElement)doc.getElementById("sb_form_q"); HTMLInputElement searchSubmit = (HTMLInputElement)doc.getElementById("sb_form_go"); searchBox.value = "Microsoft Edge"; searchSubmit.click(); //wait for search result while (doc.readyState != "complete") { System.Threading.Thread.Sleep(100); } Console.WriteLine(doc.title + ", " + doc.url); //get the document title & url } catch (Exception e) { Console.WriteLine(e); throw; } } return false; } } return true; } public static IHTMLDocument2 GetHTMLDocumentFromWindow(IntPtr hWnd) { UIntPtr lRes; IHTMLDocument2 doc = null; Guid IID_IHTMLDocument = new Guid("626FC520-A41E-11CF-A731-00A0C9082637"); uint nMsg = RegisterWindowMessage("WM_HTML_GETOBJECT"); if (nMsg != 0) { SendMessageTimeout(hWnd, nMsg, (IntPtr)0, (IntPtr)0, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 1000, out lRes); if (lRes != UIntPtr.Zero) { doc = (IHTMLDocument2)ObjectFromLresult(lRes, IID_IHTMLDocument, (IntPtr)0); } } return doc; } } }
書き直すついでに、Edgeの起動部分を「Microsoft Edgeを起動するC#コード」で紹介している、IApplicationActivationManager::ActivateApplicationメソッドを使う方法にしてみました。
(コードが長くなってしまったので、もうちょっとスッキリさせようと思ったのですが、途中で面倒くさくなったので止めました。
こんなコードですが、どなたかの叩き台にでもなれば幸いです。)
この記事へのコメントはありません。