Windows関連

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メソッドを使う方法にしてみました。

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

コメント

  • コメント (0)

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

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

Time limit is exhausted. Please reload CAPTCHA.

最近の記事

アーカイブ

RapidSSL_SEAL-90x50
PAGE TOP