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
Related
I have a program running on my PC that controls another machine via TeamViewer. It all works fine except that sending a mouse click requires TeamViewer to be in the foreground. I have code that sends mouse clicks to programs like Notepad where the edit panel is called "Edit." But the TeamViewer panel is called TV_REMOTEDESKTOP_CLASS and FindWindowEx fails to find its handle.
Here is my code:
IntPtr handle = WinGetHandle("axie_machine");
if (handle != IntPtr.Zero)
{
var panel = FindWindowEx(handle, IntPtr.Zero, "TV_REMOTEDESKTOP_CLASS", null);
PerformRightClick(panel, new Point(200, 200));
}
Here is the image of Spy++ showing the details of the panel
FindWindowEx returns 0x000000.
Can anyone see what I am doing wrong with FindWindowEx and point in the right direction?
Assuming by WinGetHandle("axie_machine"), you're getting the handle of the TeamViewer window using (part of) its title, then, you're actually getting the handle of the top-level window which your target window whose class is "TV_REMOTEDESKTOP_CLASS" is not a child of. It is one of its descendants, but not a direct child. There's one parent window in between as you can see here:
So, change your code to get the parent window of your target "panel" and then use that to get to the target. The code should looks something like the following:
IntPtr tvWindowHandle = WinGetHandle("axie_machine");
if (tvWindowHandle != IntPtr.Zero)
{
var panelParent = FindWindowEx(tvWindowHandle, IntPtr.Zero, "ATL:03B8D350", null);
if (panelParent != IntPtr.Zero)
{
var panel = FindWindowEx(panelParent, IntPtr.Zero, "TV_REMOTEDESKTOP_CLASS", null);
PerformRightClick(panel, new Point(200, 200));
}
}
Note: You might want to double-check the class of the parent window. It was "ATL:03B8D350" in my version of TV but it might be different for you if you're using another version.
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;
}
I will start by saying that my experience with pinvoke is extremely limited. I know very little of C++, too. But here is my situation...
I have a program in C# that uses pinvoke to get functions like SendMessage and FindWindow and the like from C++. It looks for the name of the window, gets the handles, etc. This works fine right now. I will put the code down a bit lower.
Now I have a situation where I want to find a "button" on one of the child windows. I have the IntPtr of the Actual Child Window, at least I think it is. I am very confused about what is a window and what isn't.
I am trying to use EnumChildWindows and FindWindowByCaption and FindWindowEx etc to go through all of the children and find this button. I can get its "caption" with Spy++, but giving that to the FindWindowByCaption does not yield anything.
This is the code that actually finds the parent window. I am sorry if it is confusing, it is being run against very old software.
This is all in C#, using pInvoke. I try to first get the button I want with this method..
FindWindowByCaption(IntPtr.Zero, "{{Caption as shown by Spy++}}");
This doesn't work, even though I give it the right caption. So I try this... In this example, Handle is the IntPtr of the parent window that contains the button I want to find.
// create a small counter so that we do not try to check
// too many windows and crash the program.
int i = 0;
var previous = new Window();
var current = new Window();
do {
current =
new Window(
InteropServices.FindWindowEx(
Handle,
previous.Handle,
null, null
)
);
if (current == IntPtr.Zero)
break;
// add current to list
// continue
previous = current; // never finds the right button, either.
++i;
} while (i < 500);
But even at this, the control I want is never found. Can anyone help me with this? It is getting very frustrating. I can see the Caption in Spy++, I can see that it has a handle. It does not appear to be anything special.
You need to do the following:
Call FindWindow to find the top-level window. Use either class name, or window title, or both, to identify it.
Call FindWindowEx repeatedly to find child windows. Pass the parent window as hwndParent and NULL as hwndChildAfter. Again use either class name, or window title, or both, to identify the child window.
Eventually you will descend the parent/child window hierarchy until you reach your target window.
So, for an example, consider the following hierarchy, using Spy++ notation:
Window 00001000 "My MainForm window title" "MainFormWindowClass"
|
-- Window 00002000 "Panel container" "PanelWindowClass"
|
-- Window 00003000 "Click me!" "ButtonWindowClass"
You would find this with the following sequence of calls:
HWND main = FindWindow("MainFormWindowClass", NULL); // just use class name
HWND panel = FindWindowEx(main, NULL, "PanelWindowClass", "Panel container");
HWND button = FindWindowEx(panel, NULL, "ButtonWindowClass", "Click me!");
Note that I've written this in C++ to avoid confounding things with p/invoke. And I've also omitted all error checking for the sake of a clean example. You obviously would check for errors.
I suggest that you keep Spy++ at hand and try to write a simple C++ console application that locates your window. Once you know the sequence of calls to FindWindow and FindWindowEx that do the job, translate them to 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.
Im using the FindWindow method from user32.dll to find a window and get the handle, but is it possible to get the form control from the handle? and the use it like an ordinary form? Example:
int myhwnd = FindWindow(null, "MyWindow");
form myform = SomeMagic.GetFormFromHandle(myhwnd);
myform.Visible = false;
or do I have to continue to use the methods in user32.dll to send a message to the window?
If it's a managed window (you've created it with System.Windows.Forms, and it has inherited from System.Windows.Forms.Control) you can get a reference to the Control object with
Control.FromHandle(myIntPtr);
Then you just get the parent of the control until you get the form.
If not, you can't get a Control object, what you can do though is to is to create a NativeWindow, and assign the IntPtr handle to the object with the AssignHandle. That will at least give you some access to the WndProc, and similar, but that's it.
Have you tried Control.FromHandle()? Forms are (inherit from) Controls. If you hit a nested control, you'll have to search up through its parents until you hit your Form.
This assumes there actually is a Form somewhere, and you've just used the user32 methods to locate its HWND.
It's very difficult to wrap a Form class around Win32 window handle. There is no full fledged implementation provided by Microsoft. So, you have to use Native functions only to communicate with a given handle.
If the window belongs to your application you can use Control.FromHandle Method. Otherwise you will have to continue using win api. For example to hide the window you need to call ShowWindow Function.