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#.
Related
I want to keep my app's window in front of another programs's window.
My app is created with WPF, I set owner with another window's hwnd like this:
// this: my wpf window
WindowInteropHelper helper = new WindowInteropHelper(this);
//The hwnd is handle of window that other program I want to follow
helper.Owner = new IntPtr(hwnd);
Everything is perfect, but I can't select text with mouse within RichEditComponent of the window (the hwnd window).
Any idea to fix this?
Don't know other program write with which language,maybe c++. Handle of other program's window obtained with windows api "FindWindowEx".
If your other program is a Winforms program, you need to add a reference to
System.Windows.Forms.Integration.dll
and add a call to ElementHost.EnableModelessKeyboardInterop(Window window) like shown below:
WindowInteropHelper helper = new WindowInteropHelper(this);
helper.Owner = new IntPtr(hwnd);
ElementHost.EnableModelessKeyboardInterop(this);
because apparently Winforms and WPF have different ways of handling text input (and therefore also affecting text selection - more specific, copy & paste of selected text - as well).
Other than that, the issue might be the HWND pointer - how do you obtain it?
e.g. this is how you can obtain the main window handle by specifying a process name:
Process process = Process.GetProcessesByName("...")[0];
IntPtr hwnd = process.MainWindowHandle;
I had resolved the question by detect Mouse drag event use global hook. When drag start,cancel set owner,and then,when drag finished,set owner with the follow window again.
Use MouseKeyHook to detect global mouse drag event.
https://www.nuget.org/packages/MouseKeyHook
Thanks #Thomas Flinkow again,for your help!
Previously, I asked this question about returning the user to the previous window they were using, but I have found the solution, but it has created another problem. The function that determines the previous window will sometimes work (if a certain set of events happen that I cannot recreate at will, but happens randomly). At some point it goes 2 windows back, sometimes 1 window back. It does this because the taskbar is in the way first, and then there is the window.
So, in order to fix this, I have determined that I should keep going through the previous windows until I get to a window that is not the taskbar, however, I do not know how to determine the IntPtr of the taskbar.
I have tried:
IntPtr taskBarWnd = FindWindow("Shell_TrayWnd", null);
But it doesn't seem to work. If I call MessageBox.Show(taskBarWnd.ToInt64().ToString()), I get 131258, but when I do:
IntPtr thisWnd = GetForegroundWindow();
IntPtr lastWnd = GetWindow(thisWnd, 2);
It is referencing the taskbar, but when I call the same MessageBox function above, it returns 131260 (65774 if the icon is within the hidden icons area).
Does anyone know if there is anything else I can try to determine if the lastWnd I have calculated is the taskbar? I would prefer not to have something that checks if the IntPtr's are close to each other instead of being equal.
I can't check right now since this is my iPad, but I'm pretty sure the taskbar windows all use custom window classes so you should be able to call GetClassName and figure out from the result what your handle refers to.
I am trying to "hook" in to the messages of a window to detect a minimize/maximize. I've looked around, and think that the only/best solution to do this, is to hook into the messages of a window, and check for the WM_WINDOWPOSCHANGED message, and then check it's status.
I've run into a problem.
System.Windows.Interop.HwndSource source = System.Windows.Interop.HwndSource.FromHwnd(System.Diagnostics.Process.GetProcessesByName("notepad")[0].MainWindowHandle);
System.Windows.Interop.HwndSourceHook hook = new System.Windows.Interop.HwndSourceHook(WndProc);
source.AddHook(hook);
It will give me a "Object refrence not set to the instance of an object." error on "source.AddHook...". When breakpointing, it also becomes clear that the source variable is null. In other words: It fails to get the HwndSource on the first line.
I know that it's possible by using an "WindowInteropHelper", but that is when you have the actual window as a Windows.Window available, but in my situation I do not.
Any workarounds/solutions would be very much appreciated,
René Sackers
P.S. I am 100% sure that Notepad is running when the code is executed, and it manages to find it, and it's main window handle.
HwndSource and HwndSourceHook don't do what you are thinking. They only exist for interop between WPF and standard Win32 windows - in the same process. They can't be used for hooking the window procedure of a window in a different process.
HwndSource.FromHwnd() doesn't create a new HwndSource object it "Returns the HwndSource object of the specified window." If the hWnd doesn't have one it associated, FromHwnd() will return null. It would be like calling System.Windows.Forms.Control.FromHandle on the hWnd from notepad - which would return null as well since the notepad window isn't a WinForms control.
The way to hook another process's window procedure is to use SetWindowsHookEx. And for to hook another process, the code has to be written in C or C++.
You are misusing WindowInteropHelper. The documentation for the constructor states:
Initializes a new instance of the WindowInteropHelper class for a specified Windows Presentation Foundation (WPF) window.
The notepad window is not a WPF window which is why FromHwnd returns null.
In fact, I don't believe it could ever work for a window in a separate process, even if the other window was a WPF window.
I want to know if it is possible to acquire the desktop's Form. I have tried to get the hWnd from the desktop and use Form.FromHandle to get the form. But it always returns null. So I assume this is not possible; if it is possible can someone show me an example code.
Here is the code that did not work below:
hWnd = GetDesktopWindow();
desktop = Form.FromHandle(hWnd) as Form;
System.Diagnostics.Debugger.Break();
P.S. Can someone also explain what I did wrong here.
You can't do this because the desktop window isn't a Form. FromHandle() tries to find the managed Control (in this case a Form) that corresponds to the given window handle. Since no such Control exists, it returns null.
I have a c++ windows app calling into a c++/cli library, which is calling into a managed library. The managed library is calling OpenFileDialog.Show with a WPF window parent which is owned by the Win32 window. I haven't found a better way to do this, and all the resources I've read here and searching google and social.msdn recommend doing what I'm doing.
The dialog opens just fine, but when I hit the cancel button, for instance, the app loses focus completely. I'm not sure why it's happening, but I can't seem to make it stop. I've tried a number of different things to no avail.
If I just launch the OpenFileDialog without creating a WPF Window, I don't see the problem.
If I don't set the owner of the WPF Window, I don't see the problem. If I call OpenFileDialog.Show and don't pass the parent, but still create the WPF Window and set its owner, I still see the problem.
I am able to hack it to set the parent app window to foreground after it loses focus, but I would like to not have to.
I have uploaded a small example solution for my scenario that illustrates the problem:
http://dl.dropbox.com/u/26054523/MixedExample.zip
Any help would be appreciated.
Have you tried inverting the hosting scenario? Right now it sounds like you're going unmanaged->bridge->managed->WPF->Winforms. Maybe you could go ...managed->WinForms->WPF using ElementHost http://msdn.microsoft.com/en-us/library/ms742215.aspx
In that way, the WPF window would just be a child control of the WinForms app and that might work out better for focus switches. WinForms controls are not really meant to work directly with WPF apps so well, two different UI threading setups are being used as you've noted.
I know that this is an old post but I think that this is a common problem and I have a good answer. If you have a Win32 window parent window called ParentWindow and a WPF child window called WPFChild you can do this:
using System.Windows.Interop;
void OpenWindow()
{
WPFChildWindow WPFChild = new WPFChildWindow();
WindowInteropHelper helper = new WindowInteropHelper(WPFChild)
{
Owner = new NativeWindowWrapper(ParentWindow.Hwnd).Handle
};
bool? ret = _stepsForm.ShowDialog();
}
This will cause the child window to remain on top of the parent and function as a dialog. Keep in mind that the WPF window does not return a DialogResult but rather a nullable bool.
NativeWindow wrapper is a simple class that takes casts an int as an IntPtr. It's actually from a .net Excel ref edit project located here: How to code a .NET RefEdit ControlT