Microsoft Edge: Get Window URL and Title

后端 未结 7 1087
滥情空心
滥情空心 2020-12-06 15:33

Previously I was using ShellWindows() API for IE to get the window Title and URL for my application Now with new development, Microsoft Edge is new and has many features und

相关标签:
7条回答
  • 2020-12-06 15:51

    I'm not familiar with what's possible through Shell APIs here, but I just tried a test with UIA. I wrote the code below to access the title, url and window handle, and it seemed to work ok. I ran the code while Edge had a few tabs showing, and the page presented was Bing.com. This is the data found by the test...

    The C# code uses a UIA interop dll that I generated through the tlbimp tool.

    The test code makes a few assumptions which might need tightening up, but overall it looks like it can get the data you need. If you find this code doesn't work for you, if you let me know exactly which elements are involved, I can look into it.

    Thanks,

    Guy

    IUIAutomationElement rootElement = uiAutomation.GetRootElement();
    
    int propertyName = 30005; // UIA_NamePropertyId
    int propertyAutomationId = 30011; // UIA_AutomationIdPropertyId
    int propertyClassName = 30012; // UIA_ClassNamePropertyId
    int propertyNativeWindowHandle = 30020; // UIA_NativeWindowHandlePropertyId
    
    // Get the main Edge element, which is a direct child of the UIA root element.
    // For this test, assume that the Edge element is the only element with an
    // AutomationId of "TitleBar".
    string edgeAutomationId = "TitleBar";
    
    IUIAutomationCondition condition = 
        uiAutomation.CreatePropertyCondition(
            propertyAutomationId, edgeAutomationId);
    
    // Have the window handle cached when we find the main Edge element.
    IUIAutomationCacheRequest cacheRequestNativeWindowHandle = uiAutomation.CreateCacheRequest();
    cacheRequestNativeWindowHandle.AddProperty(propertyNativeWindowHandle);
    
    IUIAutomationElement edgeElement = 
        rootElement.FindFirstBuildCache(
            TreeScope.TreeScope_Children, 
            condition,
            cacheRequestNativeWindowHandle);
    
    if (edgeElement != null)
    {
        IntPtr edgeWindowHandle = edgeElement.CachedNativeWindowHandle;
    
        // Next find the element whose name is the url of the loaded page. And have
        // the name of the element related to the url cached when we find the element.
        IUIAutomationCacheRequest cacheRequest =
            uiAutomation.CreateCacheRequest();
        cacheRequest.AddProperty(propertyName);
    
        // For this test, assume that the element with the url is the first descendant element
        // with a ClassName of "Internet Explorer_Server".
        string urlElementClassName = "Internet Explorer_Server";
    
        IUIAutomationCondition conditionUrl =
            uiAutomation.CreatePropertyCondition(
                propertyClassName,
                urlElementClassName);
    
        IUIAutomationElement urlElement =
            edgeElement.FindFirstBuildCache(
                TreeScope.TreeScope_Descendants,
                conditionUrl,
                cacheRequest);
    
        string url = urlElement.CachedName;
    
        // Next find the title of the loaded page. First find the list of 
        // tabs shown at the top of Edge.
        string tabsListAutomationId = "TabsList";
    
        IUIAutomationCondition conditionTabsList =
            uiAutomation.CreatePropertyCondition(
                propertyAutomationId, tabsListAutomationId);
    
        IUIAutomationElement tabsListElement =
            edgeElement.FindFirst(
                TreeScope.TreeScope_Descendants,
                conditionTabsList);
    
        // Find which of those tabs is selected. (It should be possible to 
        // cache the Selection pattern with the above call, and that would
        // avoid one cross-process call here.)
        int selectionPatternId = 10001; // UIA_SelectionPatternId
        IUIAutomationSelectionPattern selectionPattern = 
            tabsListElement.GetCurrentPattern(selectionPatternId);
    
        // For this test, assume there's always one selected item in the list.
        IUIAutomationElementArray elementArray = selectionPattern.GetCurrentSelection();
        string title = elementArray.GetElement(0).CurrentName;
    
        // Now show the title, url and window handle.
        MessageBox.Show(
            "Page title: " + title +
            "\r\nURL: " + url + 
            "\r\nhwnd: " + edgeWindowHandle);
    }
    
    0 讨论(0)
  • 2020-12-06 15:54

    I've just pointed the Inspect SDK tool to Edge, when Edge showed 3 tabs. (Inspect uses the UIA Client API to access all the UI shown in apps, and if you have the SDK installed, can be found in places like "C:\Program Files (x86)\Windows Kits\10\bin\x64".) The image below shows that the UIA element for the main Edge window has a child element which is a list. Inspect also shows me that the AutomationId of that child element is "TabsList", (but that's not shown in the image below). The set of list items that are direct children of the TabsList, have names which are the titles of the pages loaded in Edge. So it looks like it's relatively straightforward to access the titles of the pages loaded in Edge.

    However, it looks like it's not so clean to access the URL associated with a page. When I looked through what Inspect was showing me, I only found the URL in the UIA tree for the page that's currently being shown. The URL can be found from the name of an element with a control type of Pane, which is beneath a chain of Pane elements. I don't know what the most robust way of reaching the element with the URL is. For example, if it's the only element with a UIA ClassName of "InternetExplorer_Server", or its parent is the only element with a ClassName of "TabWindowClass", then maybe a single call to FindFirstBuildCache() could get you close to the element you're interested in. (It looks like neither element has an AutomationId to search for.)

    Thanks,

    Guy

    0 讨论(0)
  • 2020-12-06 15:56

    In response to the comments from Yves above...

    I only use the Windows UIA API these days. While the .NET UIA API is perfectly fine to use for some things, I believe the Windows UIA API has had more investment in recent years. For example, the Windows UIA has been updated to be more resilient when it encounters unresponsive UIA providers, (eg suspended processes). Also, some Windows UIA interfaces now have '2' versions, and I doubt all those interfaces exist in the .NET UIA API. So for me, I use tlbimp.exe to generate a wrapper around the Windows UIA API, and always use that.

    Regarding getting the content of the addressEdit Box, that should be accessible through the UIA Value pattern. Typically edit controls have a UIA Name property that describes the purpose of the edit control, and the content of the control is exposed through the Value pattern.

    The screenshot below shows the Inspect SDK tool reporting the properties exposed through the Value pattern. (The screenshot was taken on a machine with no net connection, hence the "You're not connected" tab names.) Inspect shows me that the Value pattern's Value property is "microsoft.com" and the IsReadOnly property is false.

    I don't know how well the .NET UIA API behaves with Edge UI, but I expect a client of the Windows UIA API will be able to access the edit control text fine.

    Thanks,

    Guy

    0 讨论(0)
  • 2020-12-06 16:00

    This example project monitors title and url for Microsoft Edge active window and active tab. Tested on Windows 10:

    • 10240 Pro
    • 10586 Home
    • 14939 Pro

    C#

    class Program
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();
    
        static bool TryGetMSEdgeUrlAndTitle(IntPtr edgeWindow, out string url, out string title)
        {
            const int UIA_NamePropertyId = 30005;
            const int UIA_ClassNamePropertyId = 30012;
            const int UIA_NativeWindowHandlePropertyId = 30020;
    
            url = "";
            title = "";
    
            IUIAutomation uiA = new CUIAutomation();
            IUIAutomationElement rootElement = uiA.GetRootElement();
    
            IUIAutomationCacheRequest cacheRequest = uiA.CreateCacheRequest();
            cacheRequest.AddProperty(UIA_NamePropertyId);
    
            IUIAutomationCondition windowCondition = uiA.CreatePropertyCondition(UIA_NativeWindowHandlePropertyId, GetForegroundWindow());
            IUIAutomationElement windowElement = rootElement.FindFirstBuildCache(TreeScope.TreeScope_Descendants, windowCondition, cacheRequest);
            if (windowElement == null)
                return false;
    
            IUIAutomationCondition edgeCondition = uiA.CreatePropertyCondition(UIA_NamePropertyId, "Microsoft Edge");
            IUIAutomationElement edgeElement = windowElement.FindFirstBuildCache(TreeScope.TreeScope_Subtree, edgeCondition, cacheRequest);
            if (edgeElement == null)
                return false;
    
            IUIAutomationCondition tabCondition = uiA.CreatePropertyCondition(UIA_ClassNamePropertyId, "TabWindowClass");
            IUIAutomationElement tabElement = edgeElement.FindFirstBuildCache(TreeScope.TreeScope_Descendants, tabCondition, cacheRequest);
            if (tabElement == null)
                return false;
    
            IUIAutomationCondition ieCondition = uiA.CreatePropertyCondition(UIA_ClassNamePropertyId, "Internet Explorer_Server");
            IUIAutomationElement ieElement = tabElement.FindFirstBuildCache(TreeScope.TreeScope_Descendants, ieCondition, cacheRequest);
            if (ieElement == null)
                return false;
    
            url = ieElement.CachedName;
            title = tabElement.CachedName;
    
            return true;
        }
    
        static void Main(string[] args)
        {
            string oldUrl = "";
            string oldTitle = "";
    
            while (true)
            {
                string url = "";
                string title = "";
    
                if (TryGetMSEdgeUrlAndTitle(GetForegroundWindow(), out url, out title))
                {
                    if ((url != oldUrl) || (title != oldTitle))
                    {
                        Console.WriteLine(String.Format("Page title: {0} \r\nURL: {1}", title, url));
    
                        oldUrl = url;
                        oldTitle = title;
                    }
                }
    
                Thread.Sleep(250);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-06 16:00

    I have tried Guy Barker above code in Windows 10 Professional machine, its working great.

    If I have tried the same code in Windows 10 Home Edition(upgraded from windows 8.1) machine, its not working and the"urlElement" return null for me. The code not find the Internet Explorer_Server class. But the Internet Explorer_Server class has found while navigate using inspect.exe.

    IUIAutomationElement urlElement =
            edgeElement.FindFirstBuildCache(
                TreeScope.TreeScope_Descendants,
                conditionUrl,
                cacheRequest);
    
    if(urlElement == null)//true
    

    I have explore further, the code not capture pane(Spartan XAML-To-Trident Input Routing Window) node in windows 10 Home edition machine. So i could not reach "Internet Explorer_Server" class to find URL.

    Is any difference between home and professional edition OS? How to solve it?

    Thanks

    Satheesh

    0 讨论(0)
  • 2020-12-06 16:03

    I use VB.Net version, Windows 10 Home OS. Works for me. I get the page title and page URL. Code is part of one of my modules. Please copy and edit it as needed.

    '-----------------------------------------------------------------------------
    'Allow code to get Microsoft Edge URL & Title
    '   Add .Net references for UIAutomationClient & UIAutomationTypes
    Imports System.Windows.Automation
    '-----------------------------------------------------------------------------
    
    Public Function ActiveMicrosoftEdgeTitleAndURL(ByRef HadError As Boolean,
                                                   ByVal InhibitMsgBox As Boolean) As String()
    
        Dim i1 As Integer
        Dim tmp1 As String = "", tmp2() As String, METitle As String, MEURL As String
        Dim strME As String = "Microsoft Edge"
    
        'ActiveMicrosoftEdgeTitleAndURL(Index) = Page Title or "No Title" + Chr(255) + Page URL
    
        'If no Page URL then any Page Title is ignored.
        '   If the form is minimized to the taskbar the url is typically not available.
    
        HadError = False : ReDim tmp2(-1) : i1 = -1
    
        Try
            Dim conditions As Condition = Condition.TrueCondition
            Dim BaseElement As AutomationElement = AutomationElement.RootElement
            Dim elementCollection As AutomationElementCollection = BaseElement.FindAll(TreeScope.Children, conditions)
            Dim AE As AutomationElement
            For Each AE In elementCollection
                If AE IsNot Nothing Then
                    tmp1 = AE.GetCurrentPropertyValue(AutomationElement.NameProperty).ToString
                    If StrComp(Strings.Right(tmp1, strME.Length), strME, vbTextCompare) = 0 Then
                        MEURL = "" : METitle = ""
                        '-----------------------------------------------------------------------------------------------------------
                        Dim AE1 As AutomationElement = _
                            AE.FindFirst(TreeScope.Subtree, New PropertyCondition(AutomationElement.AutomationIdProperty, "TitleBar"))
                        METitle = AutomationElementText(AE1)
                        METitle = Trim(METitle)
                        '-----------------------------------------------------------------------------------------------------------
                        AE1 = AE.FindFirst(TreeScope.Subtree, New PropertyCondition(AutomationElement.AutomationIdProperty, "addressEditBox"))
                        MEURL = AutomationElementText(AE1)
                        MEURL = Trim(MEURL)
                        '-----------------------------------------------------------------------------------------------------------
                        If MEURL <> "" Then
                            If METitle = "" Then METitle = "No Title"
                            i1 = i1 + 1 : Array.Resize(tmp2, i1 + 1)
                            tmp2(i1) = METitle + Chr(255) + MEURL
                        End If
                    End If
                End If
            Next
        Catch ex As Exception
            HadError = True
            MsgBox("Function AutomationElementData system error." + vbCr + vbCr + ex.ToString, vbExclamation)
        End Try
    
        Return tmp2
    
    End Function
    
    Private Function AutomationElementText(ByRef AE As AutomationElement) As String
    
        Dim MyPattern As AutomationPattern = ValuePattern.Pattern
        Dim MyPattern1 As AutomationPattern = TextPattern.Pattern
        Dim objPattern As Object = Nothing
        Dim txt As String = ""
    
        'Any error just return a null string. !r
    
        If AE.TryGetCurrentPattern(MyPattern, objPattern) Then
            Dim AEValuePattern As ValuePattern = AE.GetCurrentPattern(MyPattern)
            txt = AEValuePattern.Current.Value
        Else
            If AE.TryGetCurrentPattern(MyPattern1, objPattern) Then
                Dim AETextPattern As TextPattern = AE.GetCurrentPattern(MyPattern1)
                txt = AETextPattern.DocumentRange.GetText(-1)
            End If
        End If
    
        Return txt
    
    End Function
    
    0 讨论(0)
提交回复
热议问题