2017/8/18 追記:
当記事のコードは現在動作しなくなっているため、新しくコードを書き直しました。
前回の記事で、UI Automationを使って起動中のMicrosoft EdgeからタイトルとURLを取得するC#コードを紹介しましたが、動作としてはEdgeのアドレス欄をフォーカスしてからURLを取得するという面倒なことをやっています。
それに対して「Microsoft Edge: Get Window URL and Title」では、ClassNameプロパティが「Internet Explorer_Server」のPaneコントロールからCachedNameプロパティを取得する形がとられています。
コードを見る限り、たしかにコチラの方が手間が無くて良いですね。
(実は私も「起動中のMicrosoft EdgeからタイトルとURLを取得するVBAマクロ(UI Automation編)」を書いたときに同じような方法を試したのですが、そのときは何故かURLが途中で切れてしまうページがあったので、結局アドレス欄から取得する方法にしました。)
ただ、一つ気になるのが下記の一文。
“The C# code uses a UIA interop dll that I generated through the tlbimp tool.”
えー!?Tlbimp.exeでわざわざDLL吐き出さないといけないの??
Visual Studio入れてない人はどうすれば良いの??
・・・というわけで、何とか別の方向から行けないか、調べてみることにしました。
まず、Inspectで先のPaneコントロールを調べたところ、NativeWindowHandleが0x1005CAになっていました。
このウィンドウが何なのかをSpy++で調べたところ、「Windows.UI.Core.CoreComponentInputSource」クラスのウィンドウであることが分かりました。
ということは、Edgeの子ウィンドウからWindows.UI.Core.CoreComponentInputSourceクラスのウィンドウを列挙して、そこからAutomationElementを捕まえれば良いのでは?
・・・ということで、さっそくやってみました。
※ UIAutomationClient, UIAutomationTypes 要参照
using System; using System.Text; using System.Runtime.InteropServices; using System.Windows.Automation; namespace UIAutomationEdge { class Program { public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam); [DllImport("user32.Dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] static public extern IntPtr GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)] public static extern IntPtr GetParent(IntPtr hWnd); public static void Main(string[] args) { AutomationElement root = AutomationElement.RootElement; AutomationElement edge = root.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TitleBar")); if (edge != null) { edge = edge.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "Windows.UI.Core.CoreWindow")); if (edge != null) { Win32Callback childProc = new Win32Callback(EnumWindow); EnumChildWindows((IntPtr)edge.Current.NativeWindowHandle, childProc, (IntPtr)0); Console.ReadKey(true); } } } private 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 == "Windows.UI.Core.CoreComponentInputSource") { IntPtr hTitle = GetParent(handle); AutomationElement title = AutomationElement.FromHandle(hTitle); AutomationElement url = AutomationElement.FromHandle(handle); Console.WriteLine(title.Current.Name + ", " + url.Current.Name); } } return true; } } }
EnumChildWindowsでウィンドウを列挙してGetClassNameでクラス名を調べていくという、これはこれで面倒くさいものですが、一応下図の通り目的達成できました。
この記事へのコメントはありません。