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でクラス名を調べていくという、これはこれで面倒くさいものですが、一応下図の通り目的達成できました。





















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