11

I am building a WPF application in C# and I want to display thumbnails of open IE tabs in a listbox. I'm essentially trying to duplicate the DWM functionality in Windows 7.

Windows 7 showing open IE tabs

I have figured out how to enumerate a list of open tabs using Interop.ShDocVW, but in order to use the DWM API calls, I have to pass in an hwnd, and the tabs all share the same handle as Internet Explorer.

So I've been messing with EnumWindows and EnumChildWindows but I can't get anything to work.

Any suggestions on how to best approach this?

Robert S.
  • 25,266
  • 14
  • 84
  • 116
  • Have you looked at this link it may you a hand as in what direction to go with your idea http://msdn.microsoft.com/en-us/library/bb776426%28VS.85%29.aspx – MethodMan Feb 10 '12 at 22:33
  • This link may help as well if you were using Windows Explorer that is http://www.codeproject.com/Articles/14570/A-Windows-Explorer-in-a-user-control – MethodMan Feb 10 '12 at 22:35
  • Thanks for the links, but I'm looking for something a bit more specific. – Robert S. Feb 10 '12 at 22:37

3 Answers3

6

This code enumerates window handles that correspond to IE thumbnails and can be used as the hwndSource parameter of the DwmRegisterThumbnail function

public static IEnumerable<IntPtr> EnumerateIEDwmThumbnails()
{
    List<IntPtr> ptrs = new List<IntPtr>();
    StringBuilder cls = new StringBuilder(100);
    EnumWindows((hwnd, lparam) =>
    {
        GetClassName(hwnd, cls, cls.Capacity);
        if (cls.ToString() == "TabThumbnailWindow")
        {
            ptrs.Add(hwnd);
        }
        return true;
    }, IntPtr.Zero);
    return ptrs;
}

[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsCallback lpEnumFunc, IntPtr lParam);
private delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam);

[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • +1, `TabThumbnailWindow` was the key part I was missing in my attempts. – Robert S. Feb 14 '12 at 03:30
  • 4
    Note that this class name is undocumented. There is no guarantee that future versions of IE will behave the same way. Make sure your customers understand that you are intentionally taking a dependency on undocumented behavior. – Raymond Chen Feb 15 '12 at 00:28
  • @RaymondChen - Is there any documented way? Unfortunately there is no TaskBar interface capable of listing what has been registered with ITaskList3:RegisterTab, if I understand things correctly. – Simon Mourier Feb 15 '12 at 08:35
  • I am not aware of a documented interface for this. – Raymond Chen Feb 15 '12 at 14:16
  • This solution is elegant, but I am concerned about compatibility going forward. – Robert S. Feb 16 '12 at 22:59
  • @RobertS. - well, it looks like you have no choice :-) If you want the feature, there is no documented way of doing it. – Simon Mourier Feb 17 '12 at 06:14
  • @SimonMourier, never say never! I found that it's possible to use EnumWindows and then find a particular window via its caption. – Robert S. Feb 17 '12 at 18:32
  • 3
    @RobertS. Oh yes, of course, but the fact that these TabThumbnailWindow windows exist, or any windows with a caption that suits is, in itself, not documented. If Raymond Chen said there was no documented way, then he's right - he's always right about Windows :-) – Simon Mourier Feb 17 '12 at 22:06
5

Update

While specified in the question indeed, I hadn't actually looked into the DWM Thumbnail API and the requirements of the DwmRegisterThumbnail function specifically:

hwndSource

The handle to the window to use as the thumbnail source. Setting the source window handle to anything other than a top-level window type will result in a return value of E_INVALIDARG. [emphasis mine]

The emphasized requirement renders my approach with child windows retrieved via FindWindowEx() outlined below invalid, i.e. only FindWindow() might be used to retrieve a handle to a top-level window instead (thanks Simon for pointing this out) - Simon's answer provides an appropriate solution based on the class name of the top-level IE window apparently rendered specifically for this purpose.


[...] in order to use the DWM API calls, I have to pass in an hwnd, and the tabs all share the same handle as Internet Explorer.

How have you inspected the window hierarchy? If I inspect an IE 9 window with e.g. Spy++, it exposes the following hierarchy of Window Classes (abbreviated):

  • IEFrame
    • [...]
    • Frame Tab
      • [...]
    • Frame Tab
      • [...]
      • TabWindowClass
        • Shell DocObject View
          • Internet Explorer_Server

The child windows have separate handles, so (from the top of my head) you should be able to retrieve the desired ones via appropriate calls to the FindWindowEx function, e.g.:

HWND hwndIeTab = ::FindWindowEx(hwndIeFrame, NULL, "Internet Explorer_Server", NULL);

In order to retrieve all desired tabs, you need to iterate over the results by means of the 2nd parameter hwndChildAfter of FindWindowEx():

A handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.

So you'd need to iterate via class "Frame Tab" first and retrieve each "Internet Explorer_Server" child window with a second call to FindWindowEx() in turn (though you might want to experiment, whether passing a child higher up via the 3rd parameter lpszClass produces identical or better results).

Good luck!

Community
  • 1
  • 1
Steffen Opel
  • 63,899
  • 11
  • 192
  • 211
  • 1
    @Steffen Opel - Although it seems a good idea, this does not work. The hierarchy is there but it has no suitable window for thumbnail creation. All windows beneath IEFrame report an error when you try to call DwmRegisterThumbnail on it. – Simon Mourier Feb 15 '12 at 08:38
  • 1
    @SimonMourier: Thanks for actually testing this, I had indeed just answered based on an apparently sloppy understanding of the use case and resulting problem at hand ;) Accordingly, I've updated my answer to explain the resulting error and promote your solution instead (+1). – Steffen Opel Feb 15 '12 at 11:40
1

The solution I went with was using EnumWindows and GetWindowText from the Win32 API. I enumerate through Internet Explorer windows using shdocvw.dll and pass the tab's caption to a method that parses the results of GetWindowText to find the hwnd of the window with that caption.

This works for all IE windows, not just tabs.

Robert S.
  • 25,266
  • 14
  • 84
  • 116