How do I display open IE tabs as DWM thumbnails? - c#

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.
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?

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

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!

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.

Related

WPF web browser UI element refresh

In C# WPF, I made a custom UI element that looks like a window so that I can have a mini-desktop environment in my WPF application. This "WindowControl" class has maximize, minimize, close, scaling, translation, etc...
This window UI contains a canvas, which is where other embedded UI elements goes. For example, I can embed a TreeView in the canvas of my WindowControl and move it around the screen just like as if you opened Windows Explorer in your OS.
Everything is working EXCEPT for the web browser. When I put the built-in web browser control into the canvas of my WindowControl class, it will NOT refresh. I DON'T mean refresh the url of the browser. I mean refresh the UI element itself. As I move my WindowControl class (with embedded web browser) around the screen, the web browser is leaving screen artifacts all over the place.
THE ACTUAL QUESTION IS: How do you force the built-in web browser UI element in C# WPF to re-draw itself so that it does not leave artifacts when being resized/translated?
I would include my code... but the WindowControl class alone is nearly 1000 lines, and that would not be pleasant on this forum.
So far I have tried the following (none worked):
webBrowser.Measure();
webBrowser.Arrange();
webBrowser.Dispatcher.Invoke(DispatcherPriority.Render, new Action(() => { }));
webBrowser.Width = XX;
webBrowser.Height = XX;
webBrowser.ActualWidth = XX;
webBrowser.ActualHeight = XX;
// As sugguested by Noseratio
IntPtr hwnd;
((IOleWindow)webBrowser.Document).GetWindow(out hwnd);
UpdateWindow(hwnd);
I'd try forcing the update with UpdateWindow (untested):
Get HWND of WebBrowser.Document via IOleWindow
IntPtr hwnd;
((IOleWindow)_webBrowser.Document).GetWindow(out hwnd);
Call UpdateWindow via p/invoke:
UpdateWindow(hwnd);
For WinForms:
Error can't convert ... 'System.Windows.Forms.HtmlDocument' in 'PInvoke.NativeMethods.IOleWindow'
Solution
public IntPtr GetHandle()
{
HtmlDocument doc = Document;
if (doc == null || doc.DomDocument == null) return IntPtr.Zero;
IntPtr hwnd;
((NativeMethods.IOleWindow)(doc.DomDocument)).GetWindow(out hwnd);
return hwnd;
}

Why doesn't getting the handle of the foreground window work in my scenario?

I have the wonderful job to create a .exe that is called with a shortcut that is called with the windows native shortcut calling way for elements that are in the start menu folder.
It is supposed to do something with one selected file of the topmost Explorer window.
I have found ways to find all selected files across all explorer windows, I know how to get the window handle of the topmost window and I knew how to get all files selected in the topmost window if getting the topmost window handle would work, which it doesn't in my case:
Calling the native method GetForegroundWindow() doesn't give me the handle of the top window, but another one, maybe the one of the program I wrote, which doesn't even use a console window or anything though visible, so I can't even check if it's a handle from my program without changing it in a way to display the console.I want to get the handle of the foreground window by using a keyboard combination that was set in the options of a shortcut.How do I get the window that was in the foreground before I called my program?Or how do I keep my program from stealing focus?Or how do I give the focus back to the last window that had it? / Take it away from the current one.Or how do I get the topmost window of a specific process? (explorer)
"Or how do I keep my program from stealing focus?"
Try this out...
Add this code to your Form:
private const int WS_EX_NOACTIVATE = 0x8000000;
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | WS_EX_NOACTIVATE;
return cp;
}
}
I found my solution.
It feels kind of hacky, but it works reliably.
To explain what it does in short:
1) It collects all current handles of explorer windows and the SHDocVw.IE object behind it (I wonder if there is a better way for this)
2) I am using the absolutely unreliable and weird GetNextWindow() function until I get the first one that was previously collected as explorer handle. This is always the top window. There are usually ~20 handles between the current window and the last selected explorer window.
3) Then I give back the path to the first file selected in that folder window.
This really feels like something that shouldn't be so hard.
I am going over the top with long cat but who cares.
The method should have error handling or checks for undesired states, for example SelectedItems() can be empty or null.
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static string returnSelectedFile()
{
IntPtr windowPtr = GetForegroundWindow();
Dictionary<string, SHDocVw.InternetExplorer> windows = new System.Collections.Generic.Dictionary<string, SHDocVw.InternetExplorer>();
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows())
if (Path.GetFileNameWithoutExtension(window.FullName).ToLower().Equals("explorer"))
windows.Add(window.HWND.ToString(), window);
long protectionCounter = 0;
while (true)
{
if (windows.ContainsKey(windowPtr.ToString()) || protectionCounter++ > 9999999)
break;
windowPtr = GetWindow(windowPtr, 2);
}
return ((Shell32.IShellFolderViewDual2)windows[windowPtr.ToString()].Document).SelectedItems().Item(0).Path;
}

C# program to paste text from clipbord to any point where mouse clicks

I want to paste text from my text box or rich text box in C# windows form application to out side of the form for example:
//on a button click event
textbox1.text=Clipboard.SetText(); // this will set text to clipboard
now I want when I click in address bar of Firefox or Google chrome to get the same text as I typed in my windows form application, as I can do it by CTRL+V but I want a C# program to do so for me and get text from clipboard when ever I click in address bar or rename a folder.
You could just turn on some windows disability settings, If dragging or pasting is too awkward.
If you really want to implement this, you need a global hook on the mouse so you can recieve mouse events from outside your application. See here or perhaps here.
Then you have a problem, do you want to paste anywhere or just in address bars. First you'll have to define what an address bar window is and work out if that is what has the focus.
Its a lot of effort and the behaviour is not especially desirable. If you really want this, please expand your question and improve its readability so that this post will be useful to future visitors.
This is completely untested, and I've never used these DLL calls in C#, but hopefully it will do the trick or at least come close...
public class Win32
{
public const uint WM_SETTEXT = 0x000c;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
[DllImport("user32.dll")]
static extern IntPtr WindowFromPoint(int xPoint, int yPoint);
}
Elsewhere in the code...
IntPtr theHandle = WindowFromPoint(Cursor.Position.X, Cursor.Position.Y);
if (theHandle != null)
{
long res = Win32.SendMessage(theHandle, WM_SETTEXT, IntPtr.Zero, Clipboard.GetText());
}
Note: I'm not entirely sure that WindowFromPiont will get the handle of the child control (i.e. the actual textbox) of another window, rather than the handle of the window itself. You might have to find the child by the cursor position. Been a long time since I've done something like this, unfortunately.
Also, if you want to get a fancier with the acquisition of the window handle, see this question: getting active window name based on mouse clicks in c#

Setting my C# form as a child to another application

I'm currently developing a form application which works as an overlay to another program (Skype). Right now, I'm using TopMost = true, but that's a pretty bad solution.
I have a handle to the Skype window, as well as a handle to my own window. How do I make my program fulfill the following three statements:
1. It has to disappear if Skype is minimized
2. It has to appear above Skype
3. It has to appear behind any other application which is above Skype
Above and behind relates to z-order.
I'm currently using the SetWindowLong function, but I cant get the desired results.
[DllImport("user32.dll")]
public static extern int SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong);
SetWindowLong(
new HandleRef(child, child.Handle),
-8, // GWL_HWNDPARENT
new HandleRef(owner, owner.Handle));
For #1, my application continually checks if the dimensions of Skype has changed, so I could simply also check if the window is no longer visible. However, I'm completely at loss with #2 and #3.
Thanks in advance.
Kloar

How to kill a window?

When I click on an ESRI COM toolbar item, it spawns a window - it looks like a Winform. Since I didn't spawn the window directly, I can't just do an Object.Close() on it. What technique can I use to delete a window my application spawned, but that I don't have an object reference of?
I think the easiest way is using p/invoke.
The easiest way:
Use FindWindow() function to get and HWND for that window (in C# its IntPtr, you can use NativeWindow class as a wrapper - http://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow.aspx#Y114)
Once you have the HWND you can use CloseWindow() to close the window or send a message to the window useing SendMessage(youHWND, WM_CLOSE, IntPtr.Zero, IntPtr.Zero) (WM_CLOSE = 0x0010).
If your window has a parent (you can use spy++ to find that out) you can find your window in a more precise way using FindWindowEx().
Hope it helps!
Good luck!
p.s.
Just to be REALLY sure you're not by accident killing another application's window unexpectedly (if you use FindWindow or FindWindowEx without a parent) you can use GetWindowThreadProcessId() to make sure that the window belongs to your process!
Assuming you don't have the window handle you could interop to Win32 and do the following:
In some method call:
CallBackPtr callback = WindowEnumeration;
EnumWindows(callback, IntPtr.Zero);
Then it will call the following to find the window and close it, just replace <title> with as descriptive a title for the window as you can to prevent closing of windows that were not intended to be closed.
private bool WindowEnumeration(IntPtr hwnd, IntPtr lParam)
{
_windowName.Clear();
if (GetWindowText(hwnd, _windowName, _windowName.Capacity) != 0)
{
if (_windowName.ToString().Contains("<title>"))
{
PostMessage(window, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
return true;
}
The information for calling Win32 can be found in MSDN or pinvoke.net

Categories

Resources