Answered Interacting w/tabs in IE7

  • Wednesday, November 15, 2006 1:15 AM
     
     

    Is there a recommended way of (a) enumerating open tabs in an existing IE7 window, (b) figuring out which tab is active, and/or (c) setting a specific tab to be active?  I am working with a .NET forms applications, not an IE7 extension.

    The only way I've found to enumerate tabs is to step through the HWND hierarchy and look for windows of class TabWindowClass, but this seems awkward and doesn't let me do (b) or (c).  If this is the best way to interact with tabs, does someone have a nice clean example of how to do this in a reasonably robust way, how to get IWebBrowser2 interfaces to individual tabs, activate tabs, etc.?

    Thanks for any tips you have!

    -Dan

    http://research.microsoft.com/~dan

     

    (Interestingly, MSDN forums _insists_ that my URL is ~dmorris, which you will no doubt see below this message.  It's not.  My profile doesn't contain ~dmorris, my Passport information doesn't contain ~dmorris.  It's a fascinating MSDN forums bug).

All Replies

  • Wednesday, November 15, 2006 2:06 AM
     
     Answered

    No, unfortunately IE does not yet expose a tabbed browsing API set. Enumerating windows is also unsupported.

    The closest thing we have to (b) is DWebBrowserEvents2::OnWindowStateChanged (http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/webbrowser/reference/ifaces/dwebbrowserevents2/windowstatechanged.asp) which enables extensions and IE automation clients to track the visibility state of a given browser window within a tab.

    - Tony Schreiner

  • Wednesday, November 15, 2006 7:20 PM
     
     
    Sure, but it seems like lots of folks are asking about tabs, so maybe someone has a "nice" wrapper around the not-super-nice process of using COM casts, Window classes, and EnumWindowsProc's that one seems to be able to use to enumerate tabs, close tabs, activate tabs, and find the active tab - all of which are things that various places on the web indicate can be done in a reasonably robust way (generally without much detail).

    Thanks!

    -Dan


    http://research.microsoft.com/~dan
  • Thursday, December 21, 2006 5:28 PM
     
     Answered
    FYI, if anyone is interested, I ended up using the following approach to solving my problem.  As a reminder, my problem was basically to – from my C# Forms application – open url’s in the current tab of an existing IE7 window (launching a url with process.start creates a new tab), and also enumerate all the open URL’s to avoid opening things that were already open.  More generally I wanted to do useful stuff with IE from an external application.

    1) This is especially difficult in C#, so I created a managed C++ DLL to do the actual work and I talk to it from C#.

    2) I use EnumWindows to find all windows of the class IEFrame.  Those are just HWND’s and cannot be cast to IWebBrowser objects; you’ll see the point of getting HWND’s at all in a bit.

    3) I use the IShellWindows interface – obtained using CoCreateInstance(CLSID_ShellWindows) – to enumerate all shell windows on the system.  I then cast each one to an IWebBrowser2.  This actually works for both IE windows and explorer windows, so I need to get rid of the explorer windows.  So I call IWebBrowser2->get_hwnd() on each window.  For IE tabs, this doesn’t give you the HWND for the tab, it gives you the HWND for the top-level IE window.  So I check the get_hwnd() value against my IEFrame HWND list from step (1), and anything that doesn’t match isn’t an IE tab.

    4) Now I have IWebBrowser2 objects for each tab, and if I care, I know which IE windows they belong to.  If I want to enumerate open URL’s, this is enough; I can call get_LocationURL() to get the URL from each tab.

    5) If I want to open a link in the active tab, I can’t just call IWebBrowser2->navigate() or IWebBrowser2->navigate2(), because this opens a new tab.  And in fact I can’t even ask which tab is active through this interface.  So I cast each one to an IServiceProvider, then to an IOleWindow, on which I call GetWindow, which gives me the HWND for each tab.  Then I can call ::IsWindowEnabled(hwnd); the active tab is always enabled and the background tabs are not.  Now I know which of my IWebBrowser2’s is the active tab.

    6) I get an IHTMLDocument2 from that IWebBrowser2 (via get_document()) and call set_url() on the document, which opens a specified document in that tab (note that using IWebBrowser2->navigate() still opens a new tab, so I use the IHTMLDocument2 approach instead...)

    This was something of a mess, but it seems to be reliable and was a good tour through the external interfaces to IE.

    -Dan

    ------------------------------------------
    Dan Morris
    http://research.microsoft.com/~dan
    ------------------------------------------

  • Tuesday, January 09, 2007 10:20 AM
     
     

    Hi Dan Morris!

    I hope that you can help me out! I have basicly the same issue:(

    But I actually just want to get a hold of the last IE window (and the tab inside of this) url.

    or even better the html content of it.

    It seems like your skills a greater then mine since you fixed it using C++ dll.

     

    but maby you have some code that can do what I asked?

    I really hope so.. there is nothing on this when searching google:( 

  • Tuesday, January 09, 2007 10:40 AM
     
     
    The last tab/"site" opened is the one that calls IObjectWithSite::SetSite last.
  • Tuesday, January 09, 2007 10:48 AM
     
     

    I really don't understand much of this..

    What really sucks is that in Computer Science in my country we don't learn stuff like these..

    Do you have an example?

     

  • Wednesday, January 10, 2007 2:31 PM
     
     

    sure that it doesn't just give you the last opened tab? not the last tab you where active in (or is active in)

    http://msdn.microsoft.com/library/default.asp?url=/workshop/components/com/reference/ifaces/iobjectwithsite/setsite.asp

  • Wednesday, January 31, 2007 7:52 AM
     
     
     Dan Morris wrote:
    FYI, if anyone is interested, I ended up using the following approach to solving my problem. As a reminder, my problem was basically to – from my C# Forms application – open url’s in the current tab of an existing IE7 window (launching a url with process.start creates a new tab), and also enumerate all the open URL’s to avoid opening things that were already open. More generally I wanted to do useful stuff with IE from an external application.

    [...]

    3) I use the IShellWindows interface – obtained using CoCreateInstance(CLSID_ShellWindows) – to enumerate all shell windows on the system. I then cast each one to an IWebBrowser2. This actually works for both IE windows and explorer windows, so I need to get rid of the explorer windows. So I call IWebBrowser2->get_hwnd() on each window. For IE tabs, this doesn’t give you the HWND for the tab, it gives you the HWND for the top-level IE window. So I check the get_hwnd() value against my IEFrame HWND list from step (1), and anything that doesn’t match isn’t an IE tab.

    [...]



    Hi Dan,

    I used a similar approach in a program of mine. Unfortunately, this doesn't seem to work under Windows Vista anymore... When using IShellWindows to enumerate the open shell windows, only the real shell windows are returned, the Internet Explorer windows are ignored. It works with IE7 under XP, but under Vista it doesn't. This is probably some kind of a new security feature... Does anybody here know of a way to make this work again?

    There is also another observation I made that may be connected to this: If you enter a URL in the address field of a shell window in Vista, the shell window doesn't turn into an IE window as it did before; instead, the URL is opened in a new Internet Explorer window (or a new tab if IE is already open).

    Thanks in advance,
    Robert
  • Wednesday, February 21, 2007 2:54 AM
     
     
    Robert,
    I'm having the same problem, did you find a solution to this?
    -Aaron
  • Friday, March 16, 2007 6:17 AM
     
     

    Hi,

    I am facing a somewhat different problem. I have a web application that goes to the server every 10 seconds to refresh some data.

    I'd like to able to know when my application is not the current browser in order to stop the calls to the server to reduce load

    can you assist?

    Thanks!!

    Omri

  • Friday, March 16, 2007 4:55 PM
     
     
    Can you just call ::GetForegroundWindow and see if it matches your WebBrower's HWND?  That seems to work for me.  Other than being the foreground window, there's no real notion of "current browser" that I know of.  You could also enumerate all browsers, get their HWND's, and sort by z-order to find the foremost window; this would help in the case where the user is looking at content in your browser but is focused in another application.

    -Dan
  • Thursday, April 19, 2007 2:00 AM
     
     

    Robert:

    Sorry to be so late with this, but I think you'll find that these behaviours are caused by IE7's "protected mode" when running under Vista.

  • Thursday, October 18, 2007 10:52 PM
     
     
    This is a bit messy, but it's only proof of concept. This is what I use in C# for finding the active tab.

                IEnumerator windows = new SHDocVw.ShellWindowsClass().GetEnumerator();
                while (windows.MoveNext())
                {
                    if ((windows.Current is SHDocVw.IWebBrowser2) && ((windows.Current as SHDocVw.IWebBrowser2).HWND == Explorer.HWND))
                    {
                        IntPtr hwnd; ((windows.Current as SHDocVw.IWebBrowser2).Document as IOleWindow).GetWindow(out hwnd);

                        if (IsWindowVisible(hwnd))
                        {
                            MessageBox.Show(((windows.Current as SHDocVw.IWebBrowser2).Document as mshtml.IHTMLDocument2).title);
                            break;
                        }
                    }
                }

    So far now I've been able do the following things with IE7 via various tricks: open a tab, close a tab, scrape data from a page, change the tab label, enumerate open tabs, navigate a particular tab to a new page and handle events like NavigateComplete2 with respect to certain tabs.
    The only thing I haven't been able to do is activate a tab; that is, bring it to the front as if the user had selected it. For that, I have found the HWnd using a variation on the code above, and tried the following API calls: SetFocus, SetForegroundWindow, ShowWindow, SwitchToThisWindow, BringWindowToTop, SetActiveWindow and EnableWindow. I noticed that GetParent(GetParent(GetParent(HWnd))) returns the same HWnd as the main IE7 window, so I tried all these functions on HWnd's parent and HWnd's parent's parent as well.
    Since the code above is different to what you described Dan, I've been wondering if I've done something wrong in finding the HWnd of the active tab. However, simply casting the IWebBrowser2 object to an IServiceProvider and then/or an IOleWindow returns null. Hoping you or someone else has had some success with this.
  • Friday, April 25, 2008 5:13 PM
     
     

    Hi guys, sorry for the so late post, but I'm facing a problem with this code running on IE7/Vista with Protected Mode turned on. I think it's because of the lack of permissions that protected mode implies that doesn't let my BHO obtain the ShellWindows.

    Did you guys have some kind of workaround for this situation?

     

    Any help will be appreciated! Thanks!

  • Friday, April 25, 2008 9:24 PM
     
     

    ShellWindows is a big hack that the IE/Shell team made with internal use in mind.  It's probably only documented because it was legally required.  I encourage you all to avoid using it if at all possible.

     

    If you just want to keep track of what is going on with the tabs in your window, you will get a BHO instantiated once per-tab.  Then you just need to handle the DWebBrowserEvents2::WindowStateChanged event.  When your BHO gets the WSC event with the flags indicating it is active now, you can tell all your other BHO instances this is the case.

     

    One way of doing that is to give each BHO a unique ID using a global LONG and InterlockedIncrement().

     

    Then you just need another global that keeps track of the active BHO's ID and wrap access to that in a critical section.

     

     

  • Saturday, April 26, 2008 7:03 PM
     
     
    That's ok. I'm actually doing that, you advice me to do that in another post. But what I need is to get access to the IWebBrowser2 interface of the active tab document to manipulate DOM.
    When my bho gets instantiated I'm always getting a pointer to the top level IE window, not the tab that instantiated the BHO.
    How can I get IWebBrowser2 interface of the active tab document when my BHO gets intantiated or in WindowsStateChanged event?

    Thanks
  • Friday, May 16, 2008 9:34 AM
     
     

     

    Hi Dixon,
     
    I was looking at ways to iterated through IE tab for my .net Desktop app.. found your post useful, but have few queries. I have been looking for it for the last 3-4 days, and tried many possibilities, but couldn't. Will be grateful to you, if you can help, as I need it badly :-)
     
    I got your email from MSDN forum, and was impressed with one of your postings.
    Below is the link of that post
     
    this is what you have posted..
    ========
      IEnumerator windows = new SHDocVw.ShellWindowsClass().GetEnumerator();
                while (windows.MoveNext())
                {
                    if ((windows.Current is SHDocVw.IWebBrowser2) && ((windows.Current as SHDocVw.IWebBrowser2).HWND == Explorer.HWND))
                    {
                        IntPtr hwnd; ((windows.Current as SHDocVw.IWebBrowser2).Document as IOleWindow).GetWindow(out hwnd);

                        if (IsWindowVisible(hwnd))
                        {
                            MessageBox.Show(((windows.Current as SHDocVw.IWebBrowser2).Document as mshtml.IHTMLDocument2).title);
                            break;
                        }
                    }
                }
    ============
     
    I have one query
    1) What is Explorer.HWND ? What is EXPLORER here ? There isn't any declaration for it.. Can you provide that declaraion ?
    2 )Is there any way to get URL of active tab in Mozilla, Opera, Safari too ??
     
    I just need to get the URL of active tab..
     
    Can you please help me out ...

    Thanks and Regards,

  • Friday, May 16, 2008 6:11 PM
     
     

    Explorer = The Instance of Internet Explorer (Handle) you are getting the tabs for.

     

    The IsWindowVisible call, in this case...determines the active tab.

     

  • Monday, September 01, 2008 5:16 PM
     
     
    I don't know if this post is active... But can you please post a working example of how to get the active tab in IE7?

     

  • Friday, January 02, 2009 5:42 PM
     
     

     Hi guys,

    well, i have not found THE solution but if someone has a quick need, i have a little tool-help.
    it activate and show the IE tab but the IE TabBar at the top is not refreshed, and it can be a problem (i mean the Tab Bar stays as it was when the old IE tab was active)


    1/  here is just a batch Dos file  (.bat) which plays with the activation of windows:
    this example active the tab called "Windows Live Hotmail - Internet Explorer".
    you need the free tool :
      Cmdow v1.4.3 for Windows NT4/2000/XP   Copyright (C) 2001-2004 Ritchie Lawrence
      http://www.commandline.co.uk
      thanks to Ritchie !!

    (.bat)
    it works like that :
    if the tab researched is already active (Niv = 1) (... already active amongst the IE(s) Tabs), we active the IE on screen.
    if not, we search the active tab (hwnd2) and disactive it (/dis + /hid),
    then active the wanted tab (/ena /vis).

    -------
    @echo on
    set /a HxHandle=0
    FOR /F "tokens=1-9 delims= " %%A IN ('C:\..your directory...\cmdow /f ^| find "iexplore" ^| find "Windows Live Hotmail - Internet Explorer"') DO set HxHandle=%%A & set Niv=%%B & set NoP=%%C & call :etq1
    exit

    :etq1
    :: echo %HxHandle% - %Niv%  -  %NoP%
    if %Niv%  EQU 1 (
      C:\..your directory...\cmdow %HxHandle% /act
      :: pause
      exit )
    if %Niv%  EQU 2 ( call :etq3 )
    goto :eof

    :etq3
    FOR /F "tokens=1-9 delims= " %%A IN ('C:\..your directory...\cmdow /f ^| find "iexplore" ^| find " %NoP%"  ^| find " 2 " ^| find " Vis " ^| find " - Internet Explorer" ') DO set HxHandle2=%%A & set Niv2=%%B & set NoP2=%%C & call :etq2
    goto :eof

    :etq2
    ::   echo %HxHandle2% - %Niv2%  -  %NoP2%
    :: pause
        C:\..your directory...\cmdow %HxHandle2% /dis
        C:\..your directory...\cmdow %HxHandle2% /hid

    :: echo %HxHandle% - %Niv%  -  %NoP%
    :: pause
      C:\..your directory...\cmdow %HxHandle% /ena
      C:\..your directory...\cmdow %HxHandle% /vis
    :: pause
    goto :eof

    :eof
    ----


    2/ you can walk thru the IE tab windows as an IE tab is in fact a window)
    VBS :
    ---
    Set oSA=CreateObject("Shell.Application")
    For L=0 to oSA.windows.count-1
      typeDoc = ""
      on error resume next
      typeDoc = LCase(TypeName(oSA.windows.item(L).Document))
      If uType = "htmldocument" then  '''' not to treat the c:\Explorer windows)

    there you are in the object IWebBrowser with all properties and methods...
    and in the collection of IE + Shell Explorer windows

    you can send Tab as you would thru keybord to change the current tab.... (it s just a tool ~) like :
      WshShell.AppActivate "Google - Internet Explorer" 
      WshShell.SendKeys ...
      "^{TAB}"  or "^+{TAB}"
    according of the nuber of tabs to reach the tab you want.
    ---

    3/ the value in the area of the Adress Bar (active url) can be read thru :
    FindWindowA "IEFrame"  -->  "WorkerW"  -->   "ReBarWindow32"  /  "Address Band Root"  /  ComboBoxEx32"  /  "ComboBox"  /  "Edit" (hwnd)
    then get the value : SendMessageA(hWnd,WM_GETTEXTLENGTH,0,0) + SendMessageA(hWnd,WM_GETTEXT

    ---

    (((God bless this mad World, sin is Ego-mania)))

    :)

  • Tuesday, January 27, 2009 9:34 PM
     
     
    I also do not know if anyone is looking at this post, but I was curious how you were able to change the tab labels.  I have been able to work through the COM casting required to obtain the handle to the window, and I can get that window to come to the foreground within the browser.  My problem is that the tab control in IE7 and the web page don't seem to be linked.  If you click the tab you get that page, if you programatically set that page as the foreground, the tab control doesn't itself respond.  So, I was wondering how you were able to change the tab labels hoping that I could query that control to obtain the labels so that when I bring the window to the foreground I can also "click" the right tab.

     

    In short, my problem is I know both the IWebBrowser2 and the HWND of the tab window, but can't make IE7 behave programatically as it does when you click its tab (activate that tab AND bring its associated window forward).  Any advice would be appreciated on how to do this.

  • Tuesday, January 27, 2009 10:53 PM
     
     
  • Tuesday, February 03, 2009 6:02 PM
     
     
    I was able to get my code working based on that Accessibility example. Thanks for the insight.