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. Office関連

    Chrome DevTools ProtocolでEdgeを操作するVBAマクロ

    Microsoft Edgeの操作を自動化する際はWebDriverを…

  2. Windows 10

    【2017年3月版】Microsoft Edgeでブックマークレットを使う方法

    2015年8月にMicrosoft Edgeでブックマークレットを使う…

  3. Windows 11

    Power Automate for desktop(Power Automate Desktop)…

    前回の記事でも触れていますが、Windows 11ではPower Au…

  4. Windows関連

    Windows 8のDeveloper Previewが公開されました。

    画像はWindows Developer Preview - A …

  5. Windows 10

    [Windows 10]OneDriveを無効にする。

    前回の記事で、Windows 10のナビゲーションウィンドウにあるOn…

コメント

  • コメント (0)

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

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

Time limit is exhausted. Please reload CAPTCHA.

Translate

最近の記事

アーカイブ

PAGE TOP