11

I have been designing a program using Visual C# and have came across an issue with making my program interact with web browsers. Basically what I need is to retrieve the URL address from a web browser (Internet Explorer, Firefox, Chrome etc...).

I figured this wouldn't be too difficult of a task, but after days and days of research and tests, it seems almost impossible! Thus far, I have come across this...

Get Firefox URL?

Which has the code below:

using NDde.Client;
Class Test
{
    public static string GetFirefoxURL()
    {
        DdeClient dde = new DdeClient("Firefox", "WWW_GetWindowInfo");
        dde.Connect();
        string url = dde.Request("URL", int.MaxValue);
        dde.Disconnect();
        return url;
    }
}

Which is perfect for Firefox, but for some reason I cannot get it to work with anything else. I have changed the portion of the code that says "Firefox" to "Iexplore" like I found all over the internet, along with trying other forms of expressing Internet Explorer, and I get the following error:

"Client failed to connect to "IExplorer|WWW_GetWindowInfo", Make sure the server application is running and that it supports the specified service name and topic name pair"

Any help on the issue would be much appreciated as it has become quite a task to figure out.

Community
  • 1
  • 1
StoriKnow
  • 5,738
  • 6
  • 37
  • 46
  • 2
    You're not going to get too far expanding the same [DDE](http://en.wikipedia.org/wiki/Dynamic_Data_Exchange) across different applications. And, as I recall, you'll probably need to use COM for IE (and not sure about chrome). – Brad Christie Mar 15 '11 at 20:31
  • 2
    what makes you believe there is a unique way to achieve this for any browser? I would not even expect there is a non-unique way for all popular browsers. – Doc Brown Mar 15 '11 at 20:47
  • 1
    Helpful link I found: http://stackoverflow.com/questions/3579649/get-url-from-browser-to-c-application (Though I'm 99% sure this can be simplified) – Brad Christie Mar 15 '11 at 21:16
  • As I said in my answer, all the major browsers (apart from Chrome) support WWW_GetWindowInfo, so you should be able to come up with a method that has commonality, at least on those that support WWW_GetWindowInfo. – David Heffernan Mar 15 '11 at 22:42
  • Thank you everyone for your help and comments. Simon's method works perfect for what I need and opened my eyes to an entirely different part of Microsoft programming! – StoriKnow Mar 16 '11 at 20:50

6 Answers6

24

Here is a code based on Microsoft UI Automation:

public static string GetChromeUrl(Process process)
{
    if (process == null)
        throw new ArgumentNullException("process");

    if (process.MainWindowHandle == IntPtr.Zero)
        return null;

    AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
    if (element == null)
        return null;

    AutomationElement edit = element.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
    return ((ValuePattern)edit.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
}

public static string GetInternetExplorerUrl(Process process)
{
    if (process == null)
        throw new ArgumentNullException("process");

    if (process.MainWindowHandle == IntPtr.Zero)
        return null;

    AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
    if (element == null)
        return null;

    AutomationElement rebar = element.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "ReBarWindow32"));
    if (rebar == null)
        return null;

    AutomationElement edit = rebar.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));

    return ((ValuePattern)edit.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
}

public static string GetFirefoxUrl(Process process)
{
    if (process == null)
        throw new ArgumentNullException("process");

    if (process.MainWindowHandle == IntPtr.Zero)
        return null;

    AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
    if (element == null)
        return null;

    AutomationElement doc = element.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document));
    if (doc == null)
        return null;

    return ((ValuePattern)doc.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
}

You can use the UI Spy tool to understand the visual hierarchy for all 3 browsers. You may need to adapt things to make sure it really work in your specific cases, but you should get the general idea with these samples.

And an example that dumps all urls for all the 3 types of process (IE, FF, CH) currently running in the system:

static void Main(string[] args)
{
    foreach (Process process in Process.GetProcessesByName("firefox"))
    {
        string url = GetFirefoxUrl(process);
        if (url == null)
            continue;

        Console.WriteLine("FF Url for '" + process.MainWindowTitle + "' is " + url);
    }

    foreach (Process process in Process.GetProcessesByName("iexplore"))
    {
        string url = GetInternetExplorerUrl(process);
        if (url == null)
            continue;

        Console.WriteLine("IE Url for '" + process.MainWindowTitle + "' is " + url);
    }

    foreach (Process process in Process.GetProcessesByName("chrome"))
    {
        string url = GetChromeUrl(process);
        if (url == null)
            continue;

        Console.WriteLine("CH Url for '" + process.MainWindowTitle + "' is " + url);
    }
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • 1
    Hello all, thank you so much for the responses! They have all been very helpful. @simon mourier, I really like your approach, it opens up much possibility later in what I need. However, when I went to implement it (after adding reference to UIAutomation) I received an error on "TreeScope.Children" stating "cannot resolve symbol 'TreeScope'. Any ideas? Also, I get an error stating that the automation property is defined in an assembly that is not referenced. Any ideas?? Thanks again for your huge help. – StoriKnow Mar 16 '11 at 18:07
  • 2
    Oops, disregard that last comment. I am leaving it anyways just in case someone else comes across the post and makes the same silly mistake I just did. I added only the UIAutomation client reference, when I needed to add all of the references (which I didn't see). I am going to mess around with the code now and see how things go! I will post back shortly. Thanks again for all your help everyone. – StoriKnow Mar 16 '11 at 18:21
  • Code works perfect Simon! Thank you so much, this is exactly what I needed. Now, if you have a second I wouldn't mind a quick lesson on how this works so I could better understand it and potentially implement it for other browsers such as Safari. If you don't have the time I understand, and appreciate what you've done already. Thanks! – StoriKnow Mar 16 '11 at 18:42
  • @Sam - humm... I have taken a look at Safari, and results don't look good... It does support UI Automation somehow, but UI Spy shows that we can't get to the url control pane, unlike the other browsers. And I don't think Safari supports the WWW_GetWindowInfo DDE topic either. Safari looks like a bad ride to me. – Simon Mourier Mar 16 '11 at 21:03
  • Bummer! Well, at least I have these three to go off of, that's a huge help in itself. I can at least continue my coding now with what you have helped me learn, and hopefully later down the road I can find another approach to Safari. Thanks again Simon! – StoriKnow Mar 17 '11 at 19:04
  • I tried this solution with **IE11** and it works great! – majestzim Mar 22 '16 at 17:04
8

Mourier, thank you for your solution Microsoft UI Automation. Even so it didn't worked for Firefox 41.0, I analysed the Firefox window structure with the little tool "Automation Spy". Then I've changed the search conditions slightly, and it worked perfectly!

 public static string GetFirefoxUrl(Process process)
        {
            if (process == null)
                throw new ArgumentNullException("process");

            if (process.MainWindowHandle == IntPtr.Zero)
                return null;

            AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
            if (element == null)
                return null;


            element = element.FindFirst(TreeScope.Subtree, 
                  new AndCondition(
                      new PropertyCondition(AutomationElement.NameProperty, "search or enter address", PropertyConditionFlags.IgnoreCase),
                      new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)));


            if (element == null)
                return null;

            return ((ValuePattern)element.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
        }

And here is the solution for Chromium 48:

 public static string GetChromeUrl(Process process)
    {
        if (process == null)
            throw new ArgumentNullException("process");

        if (process.MainWindowHandle == IntPtr.Zero)
            return null;

        AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
        if (element == null)
            return null;

        AutomationElement edit = element.FindFirst(TreeScope.Subtree,
             new AndCondition(
                  new PropertyCondition(AutomationElement.NameProperty, "address and search bar", PropertyConditionFlags.IgnoreCase),
                  new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)));

        return ((ValuePattern)edit.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
    }

Automation Spy shows Firefox window controls structure. The control of type 'edit' with the name 'Search or enter address' holds the url: Automation Spy

Note: In your .NET project you need 2 references:

  • UIAutomationClient.dll
  • UIAutomationTypes.dll
6

Use parameter "1" instead of "URL" in oDde.Request("URL", int.MaxValue) for IE.

    public static void ProcessIEURLs()
    {

        string sURL;
        try
        {
            DdeClient oDde = new DdeClient("IExplore", "WWW_GetWindowInfo");

            try
            {
                oDde.Connect();

                sURL = oDde.Request("1", int.MaxValue);

                oDde.Disconnect();

                bool bVisited = false;
                if ( oVisitedURLList != null && oVisitedURLList.Count > 0 )
                {
                    bVisited = FindURL(sURL, oVisitedURLList);
                }

                if ( !bVisited )
                {
                    oVisitedURLList.Add(sURL);
                }
            }
            catch ( Exception ex )
            {
                throw ex;
            }

        }
        catch ( Exception ex )
        {
            throw ex;
        }
    }
4

Here's what I have so far (though Chrome I'm not finding any helpful articles on, other than using FindWindowEx (I don't particularly like that method, personally).

public class BrowserLocation
{
    /// <summary>
    /// Structure to hold the details regarding a browed location
    /// </summary>
    public struct URLDetails
    {
        /// <summary>
        /// URL (location)
        /// </summary>
        public String URL;

        /// <summary>
        /// Document title
        /// </summary>
        public String Title;
    }

    #region Internet Explorer

    // requires the following DLL added as a reference:
    // C:\Windows\System32\shdocvw.dll

    /// <summary>
    /// Retrieve the current open URLs in Internet Explorer
    /// </summary>
    /// <returns></returns>
    public static URLDetails[] InternetExplorer()
    {
        System.Collections.Generic.List<URLDetails> URLs = new System.Collections.Generic.List<URLDetails>();
        var shellWindows = new SHDocVw.ShellWindows();
        foreach (SHDocVw.InternetExplorer ie in shellWindows)
            URLs.Add(new URLDetails() { URL = ie.LocationURL, Title = ie.LocationName });
        return URLs.ToArray();
    }

    #endregion

    #region Firefox

    // This requires NDde
    // http://ndde.codeplex.com/

    public static URLDetails[] Firefox()
    {
        NDde.Client.DdeClient dde = new NDde.Client.DdeClient("Firefox", "WWW_GetWindowInfo");
        try
        {
            dde.Connect();
            String url = dde.Request("URL", Int32.MaxValue);
            dde.Disconnect();

            Int32 stop = url.IndexOf('"', 1);
            return new URLDetails[]{
                new URLDetails()
                {
                    URL = url.Substring(1, stop - 1),
                    Title = url.Substring(stop + 3, url.Length - stop - 8)
                }
            };
        }
        catch (Exception)
        {
            return null;
        }
    }

    #endregion
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Internet Explorer: ");
        (new List<BrowserLocation.URLDetails>(BrowserLocation.InternetExplorer())).ForEach(u =>
        {
            Console.WriteLine("[{0}]\r\n{1}\r\n", u.Title, u.URL);
        });
        Console.WriteLine();

        Console.WriteLine("Firefox:");
        (new List<BrowserLocation.URLDetails>(BrowserLocation.Firefox())).ForEach(u =>
        {
            Console.WriteLine("[{0}]\r\n{1}\r\n", u.Title, u.URL);
        });
        Console.WriteLine();
    }
}
Brad Christie
  • 100,477
  • 16
  • 156
  • 200
0

the bese choice is to use selenium webdriver. best and power full api with full premision

0

WWW_GetWindowInfo is supported in IE and has been since version 3.02 back in the 16 bit days! Works for Firefox and Opera

I believe that Chrome is in fact the odd one out.

I've got no knowledge of how things are beyond those four.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490