C# HwndSource from Process.MainWindowHandle - c#

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.

Related

Can't select text in RichEditComponent control

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!

Trying to get a "button" from a child window

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

WPF window should be modal to native owner window, but isn't

I have the following C# code in a WPF project:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsWindow = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsWindow);
helper.Owner = new IntPtr(ownerHandle);
}
settingsWindow.ShowDialog();
}
The SettingsWindow isn't properly modal to the owner window (i.e. I can focus on, interact with, and even close the owner window while the SettingsWindow is still open). What am I doing wrong?
For context, this code is part of a screen saver program, and the owner window is the Control Panel screen saver selection window (which passes in the handle to use as owner via command line parameter). I know the IF statement is evaluating true and correctly parsing the handle.
I have also tried using the SetWindowLongPtr method from user32.dll (compiling for x64, hence not using SetWindowLong) which is briefly described here and shown in use here. This method works in WinForms, but doesn't seem to work here in WPF. Help me Obi-Wan Kenobi, you're my only hope.
It turns out that using WindowInteropHelper to set the native window as owner of the WPF Window does work, it just doesn't do the whole job. When set this way, the WPF Window will remain visible on top of the native window, even if the native window has focus. However, that is the only effect obtained. The WPF Window does not prevent interaction with the native Window, and the native window can even be closed, without the WPF Window closing or being affected.
In order to get the rest of the desired behaviour, we need to use the EnableWindow function in user32.dll to disable the native Window before calling ShowDialog on the WPF Window, and again to re-enable it once the WPF Window closes.
The modified code looks like this:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsForm = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsForm);
helper.Owner = new IntPtr(ownerHandle);
NativeMethods.EnableWindow(helper.Owner, false);
settingsForm.ShowDialog();
NativeMethods.EnableWindow(helper.Owner, true);
}
else
{
settingsForm.ShowDialog();
}
}
(Note: The above code is correct in general, but incomplete in the case of screen savers, which is what this code is actually being used for. In the case that this code is being used for the config window of a screen saver, the string passed in for the owner handle is not the handle of the Control Panel window to be used as owner, but rather a handle for a control that is a child of the Control Panel window. The extra step in this case is to get the handle of the parent of that control. We can do this by calling GetParent, also in user32.dll, on the passed-in handle. This will return the real handle we want to use for the owner and EnableWindow calls.)
If anyone from Microsoft ever finds this, maybe consider modifying WindowInteropHelper to properly set all of this up when Owner is assigned and ShowDialog used, since this is the proper complete behavior for modal windows.

WPF OpenFileDialog with Win32 parent window; window is not regaining focus when closed

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

From HWND to control

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.

Categories

Resources