tags:

views:

270

answers:

2

How can I get the url from a running instance of Chrome or Opera using C# .NET windows form app? Thanks!

+1  A: 

Unless the browsers are documented to provide that information themselves I don't think there's a reliable way to do so.

That being said, I'm pretty sure at least Chrome stores history information on disk somewhere, so your best bet may be to figure out where, what format it's in, and how to read it.

Davy8
Yes, it is kind of solution but it doesn't work with portable browsers.You can delete folder or use this on the pendrive and there's no mark of visited website.So it's not good solution for me :)
Saint_pl
+3  A: 

Part 2:

Basically I have too much code to separate out the minimum for this example, but I have provided my own algorithm below for you to pour over.

This one also keeps track of tabs in browsers, as and when they reappear (IE only), so you will need to strip out the bids you don't want. I seem to remember I fixed this for IE8, but not sure if that fix went in this code, so don't be surprised if it needs a little tweak :)

// Iterate all browsers and record the details
IntPtr hWnd = IntPtr.Zero;
NativeWIN32.EnumChildWindows(hWnd, new NativeWIN32.Win32Callback(BrowserEnumCallback), hWnd);

    /// <summary>
    /// Called back for each IE browser.
    /// </summary>
    /// <param name="hWnd"></param>
    /// <param name="lParam"></param>
    /// <returns></returns>
    static bool BrowserEnumCallback(IntPtr hWnd, IntPtr lParam)
    {
        // Is this app IE?
        if (NativeWIN32.GetClassName(hWnd) == "IEFrame")
        {
            // If this is a new browser, add it
            if (!BrowserWindows.ContainsKey(hWnd))
            {
                // Record the Browser
                BrowserWindow browser = new BrowserWindow()
                {
                    hWnd = hWnd
                };
                // Store the browser in the temp list and temp member
                TempCurrentBrowser = browser;
                BrowserWindows.Add(hWnd, browser);
            }
            else
            {
                // Store the browser in the temp list and temp member
                TempCurrentBrowser = BrowserWindows[hWnd];
            }
            TempCurrentBrowser.WindowText = NativeWIN32.GetWindowText(hWnd);
            TempCurrentBrowser.Found = true;

            // Now that we know it is a browser, look for tabbed windows and address bar
            NativeWIN32.EnumChildWindows(hWnd, new NativeWIN32.Win32Callback(BrowserEnumChildrenCallback), hWnd);
        }
        return true;
    }

    /// <summary>
    /// Called back for each child window in the browser
    /// </summary>
    /// <param name="hWnd"></param>
    /// <param name="lParam"></param>
    /// <returns></returns>
    static bool BrowserEnumChildrenCallback(IntPtr hWnd, IntPtr lParam)
    {
        string classname = NativeWIN32.GetClassName(hWnd);
        switch (classname)
        {
            // This is the main address bar
            case "Edit":
                {
                    string url = NativeWIN32.GetWindowText(hWnd);
                    if (url.StartsWith(@"http://") || url.StartsWith(@"https://") || url.StartsWith("about:"))
                    {
                        TempCurrentBrowser.Url = url;
                        return true;
                    }
                }
                break;

            case "ComboBoxEx32":
                {
                    string url = NativeWIN32.GetWindowText(hWnd);
                    if (url.StartsWith(@"http://") || url.StartsWith(@"https://") || url.StartsWith("about:"))
                    {
                        TempCurrentBrowser.Url = url;
                        return true;
                    }
                }
                break;

            // Record any sub pages still active, by title to avoid revisiting popup
            // If this name matches the name of the browser, it is the current window
            // If so, record the browser url for reference in that tab window
            case "TabWindowClass":
                {
                    string title = NativeWIN32.GetWindowText(hWnd);
                    BrowserTabWindow tabWindow;
                    if (!TempCurrentBrowser.TabWindows.ContainsKey(hWnd))
                    {
                        // Create a new tabbed window for the current browser
                        tabWindow = new BrowserTabWindow()
                        {
                            hWnd = hWnd
                        };
                        TempCurrentBrowser.TabWindows.Add(hWnd, tabWindow);
                    }
                    else
                    {
                        tabWindow = TempCurrentBrowser.TabWindows[hWnd];
                    }
                    tabWindow.WindowText = title;
                    tabWindow.Found = true;
                }
                break;
        }
        return true;
    }

Part 1:

If Microsoft Spy++ can see the address control, then yes it is possible.

I don't have Opera installed, but the Chrome control hierarchy in Spy++ looks like this:

alt text

I have done similar things with Internet Explorer:

  • To record a diary the sites visited - then a popup asks the user what they were doing
  • To display a widget that pops up when certain known sites were visited

Firefox was the problem child as it renders the address bar internally and not via a windows control but I see you have already found the solution to that one.

In C# you need to make unmanaged calls to Windows API methods. As you can see from the C# API code alone (below) it can get a little involved:

const int WM_GETTEXT = 0xD;

// used for an output LPCTSTR parameter on a method call
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct STRINGBUFFER
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string szText;
}

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent /*HWND*/, 
                                         IntPtr next /*HWND*/, 
                                         string sClassName,  
                                         IntPtr sWindowTitle);

/// <summary>
/// 
/// </summary>
/// <param name="hWnd">handle to destination window</param>
/// <param name="msg">message</param>
/// <param name="wParam">first message parameter</param>
/// <param name="lParam"second message parameter></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
    int msg, int wParam, out STRINGBUFFER ClassName);


[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowText(IntPtr hWnd, out STRINGBUFFER ClassName, int nMaxCount);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd, out STRINGBUFFER ClassName, int nMaxCount);

//[DllImport("user32.dll")]
//[return: MarshalAs(UnmanagedType.Bool)]
//static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

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);

/// <summary>
/// Helper to get window classname
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
static public string GetClassName(IntPtr hWnd)
{
    NativeWIN32.STRINGBUFFER sLimitedLengthWindowTitle;
    NativeWIN32.GetClassName(hWnd, out sLimitedLengthWindowTitle, 256);
    return sLimitedLengthWindowTitle.szText;
}

/// <summary>
/// Helper to get window text
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
static public string GetWindowText(IntPtr hWnd)
{
    NativeWIN32.STRINGBUFFER sLimitedLengthWindowTitle;
    SendMessage(hWnd, WM_GETTEXT, 256, out sLimitedLengthWindowTitle);
    //NativeWIN32.GetWindowText(hWnd, out sLimitedLengthWindowTitle, 256);
    return sLimitedLengthWindowTitle.szText;
}

The algorithm involved basically searches all windows from the Desktop on down until it finds Browser windows (based on specific class or window attributes). It then searches for specific child elements based on the type of browser. When you finally get to the address control you can extract the address from the control. Hopefully my helper class code will speed your development.

Enough already
+1 Very crafty.
ShaderOp
How do you programatically identify which one you want? Just look for "http"? or is there some more reliable way that I'm not aware of?
Davy8
Yes, it's good solution but unfortunately only by IE, because in Chrome every tab hasn't hidden url. I checked this now and to 10 tabs there is only one has url on this tree in spy++. So it's not this way, or I have specific version of browser ;)Check this out and let me know if I was wrong.
Saint_pl
@Davy8 - in IE only one control has url. Control where ClassName is "edit". So if you go from parent to child, from parent to child...you meet this class and then you know there's a url.In example of Chrome by HiTech Magic this class has name Chrome_AutocompleteEditView. But I suppose it's probably in this tab ...only this tab :)
Saint_pl
@Saint... why would you want to know the URL of a tab the user is not looking at? You might need to explain your requirements a bit further? (are they nefarious?) :)
Enough already
Aaaa, everything's ok :)It was because lack of refreshing Spy++ result window. I thought that a Chrome_AutocompleteEditView is still in one of tabs, and it's not make changes :) Now I know that was wrong :) But I still have a little problem, because a way of getting url from IE doesn't work here...and I don't know why.The handle of mainWindow is other than I want. Can you tell me how to get handle another window...that with a child named Chrome_AutocompleteEditView. Code below:
Saint_pl
http://img840.imageshack.us/img840/8938/chromee.pngI should go to the next Window Chrome_WidgetWin_0, but don't what is the best way to do this. Any idea? :)
Saint_pl
@Saint, I have added more of my actual algorithm for you (IE version). It does a lot more than you need so please feel free to strip out the bits you don't want/need. Cheers
Enough already
Ok, thank you very much. It's look nice and helpfull, but I analyse this code tomorrow. Thanks one more time :)
Saint_pl
Thank you HiTech Magic for yours second code. It doesn't help me but btw showed me way to solve my problem :) I'm looking for a main process FindWindow("Chrome_WidgetWin_0", null) and in loop while() until this returned handle != IntPtr.Zero...I'm searching next and next process by GetWindow(...). If one of children contents "Chrome_AutocompleteEditView" I get url :) Now it's so easy :) Maybe this help someone by similar problem :) DllImport("user32.dll") rules ;)
Saint_pl