Windows 10

WebDriverを使わずMicrosoft Edgeを制御するC#コード

以前書いた記事のように、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;
    }
  }
}

EdgeAutomationWithoutWebDriver_01

書き直すついでに、Edgeの起動部分を「Microsoft Edgeを起動するC#コード」で紹介している、IApplicationActivationManager::ActivateApplicationメソッドを使う方法にしてみました。

(コードが長くなってしまったので、もうちょっとスッキリさせようと思ったのですが、途中で面倒くさくなったので止めました。
こんなコードですが、どなたかの叩き台にでもなれば幸いです。)

2015年8月の人気記事前のページ

テキストボックスの中にある表を操作するWordマクロ次のページ

関連記事

  1. Windows 10

    Selenium WebDriverでChromium版Edgeを操作してみました。

    先日書いた下記記事の通り、Chromium版Microsoft Edg…

  2. Windows関連

    VivoTab RT TF600TにWindows RT 8.1をインストールしました。

    Windows ストアからWindows RT 8.1がダウンロードで…

  3. Windows 10

    SeleniumBasic(Selenium VBA)がMicrosoft Edgeに対応しました。…

    言わずと知れたWebブラウザーの自動制御ツール「Selenium」のV…

  4. Windows 10

    [Windows 10]Microsoft Edgeに検索プロバイダーを追加する方法

    Windows 10のEdgeでは、デフォルトの検索エンジン(検索プロ…

  5. Windows 10

    AppUserModelId(AUMID)を列挙するVBScript

    「「ファイル名を指定して実行」からMicrosoft Edgeを起動す…

コメント

  • コメント (0)

  • トラックバックは利用できません。

  1. この記事へのコメントはありません。

Time limit is exhausted. Please reload CAPTCHA.

最近の記事

アーカイブ

PAGE TOP