以前書いた記事のように、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;
}
}
}
書き直すついでに、Edgeの起動部分を「Microsoft Edgeを起動するC#コード」で紹介している、IApplicationActivationManager::ActivateApplicationメソッドを使う方法にしてみました。
(コードが長くなってしまったので、もうちょっとスッキリさせようと思ったのですが、途中で面倒くさくなったので止めました。
こんなコードですが、どなたかの叩き台にでもなれば幸いです。)


















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