forward winapi events to another window - c#

What i have now: my app in C# is half-transparent, and does not catch winapi events - every click, drag etc is catch by underlaying window, which is separate app (like webbrower). I use this to overlay information on top of what browser shows. This is my code for this:
int exstyle = GetWindowLong(this.Handle, GWL_EXSTYLE);
exstyle |= WS_EX_TRANSPARENT;
SetWindowLong(this.Handle, GWL_EXSTYLE, exstyle);
IntPtr hwndf = this.Handle;
IntPtr hwndParent = GetDesktopWindow();
SetParent(hwndf, hwndParent);
But now, i would like to send all events to both my app window (which is half-transparent on top) and web browser (under my app). So for example if i click, the click works in both windows as if they were on top. I imagine that only way to do that is to catch all events and then forward them to lower window, but is there any way to do that?
I use winforms as window lib.
What i do now is not that important, because i want to normally consume events, then forward them to underlaying window. So this is something completly different from what i'm doing now with WS_EX_TRANSPARENT. The point of this is to drag content in both windows simultaneously. If there is any better way of doing it, i would be glad to hear it.
As least what i need is to transfer drag events to both windows, and all other events to underlaying window (not under my control). So, perhaps it will be easier to stay with my window as WS_EX_TRANSPARENT (makes events pass-thru to underlaying window) and simply install global hook to receive drag events? What do you think?
BTW i don't have experience with Winapi, so solution might be obvious.

You can capture Windows events sent your own form by overloading WndProc on the form, or alternatively by calling user32!GetMessage
You can send messages to other Windows forms via the user32!PostMessage or user32!SendMessage apis (read PostMessage function on msdn).

You could try forwarding the event after the underlying form has handled it. Something like
protected override void OnDragOver(DragEventArgs drgevent)
{
base.OnDragOver(drgevent);
MyControl.ForwardDragEvent(drgevent);
}
In MyControl:
public void ForwardDragEvent(DragEventArgs drgevent)
{
base.OnDragOver(drgevent);
//Or call your own method to handle the event
}
I have used this to forward scroll-events, in my case however, only one of the controls handled the event..

Related

WPF Window Handle for Message Loop Only

I am writing a WPF application that will put an icon in the system tray and, as an exercise, I want to do this without depending on System.Windows.Forms and using its NotifyIcon or NativeWindow classes.
This is fairly easy - Shell_NotifyIcon isn't hard to call from C# - and, indeed, I have succeeded in my task.
As part of this effort, I have had to create a window handle for the sole purpose of receiving messages from the system-tray. I create the native window as follows:
// Create a 'Native' window
_hwndSource = new HwndSource(0, 0, 0, 0, 0, 0, 0, null, parentHandle);
_hwndSource.AddHook(WndProc);
The message loop is hooked in AddHook() and messages are processed in a function that looks like this:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Handle windows messages in this...
}
And, finally, when it comes time to destroy the thing, I close the window by posting it a WM_CLOSE message and disposing the HwndSource.
if (null != _hwndSource)
{
UnsafeNativeMethods.PostMessage(_hwndSource.Handle, WindowMessage.WM_CLOSE, 0, 0);
_hwndSource.Dispose();
_hwndSource = null;
}
My question is this: the first three parameters to the constructor of HwndSource are the class style, style and extended style of the native Win32 window, respectively. For a non-visible window that will only be used as a target for window-messages, what should they be?
My defaults of zero, zero and ... er.. zero do work but I have used Spy++ to examine what Windows.Forms.NotifyIcon does and it seems that the NativeWindow it creates have the following:
Class Style: <zero>
Styles: WS_CAPTION, WS_CLIPSIBLINGS,
WS_OVERLAPPED
Extended Styles: WS_EX_LEFT, WS_EX_LTRREADING,
WS_EX_RIGHTSCROLLBAR, WS_EX_WINDOWEDGE
Are any of those important for a non-visible window? (I think not.)
Windows style flags date from 1986, back when Windows v1.0 was released. There have been lots of appcompat hacks in the past 29 years and 10 major versions, Windows silently overrides style flags when the app specifies wonky ones. Nothing terribly wonky about this however, note that the value of the WS_OVERLAPPED style flag is 0. Which asks for a plain window, you automatically get the appropriate style flags for such a window.
Your HwndSource window has the exact same style flags, maybe you haven't found the correct one back in Spy++. So you don't have a problem. And no, they don't matter when the window never becomes visible.
Note a bug in your code, the WM_CLOSE message you post is never actually processed since you destroy the window right after calling PostMessage(). Just delete it, there is no point in asking the window nicely, it isn't going to object. You do however have to call Shell_NotifyIcon() with NIM_DELETE to delete the tray icon. Failure to do so leaves a "ghost" icon that only disappears when you move the mouse over it.
And do note that NotifyIcon is not as trivial as you assume it is, it has a non-obvious bug workaround that you are likely to overlook. You'll notice when the context menu refuses to close.

Enumerate all controls inside window

I'm trying to enumerate all controls inside a window using user32.dll, but I don't know exactly what I need to do.
I'm using EnumWindows and EnumChildWindows, but it doesn't retrieve all controls inside a window.
I want to get the text of some labels in a Delphi application.
I tried using Spy++, but it doesn't list too.
IList<IntPtr> childern = new List<IntPtr>();
WNDENUMPROC enumChildProc = delegate(IntPtr hwnd, IntPtr param)
{
childern.Add(hwnd);
return true;
};
EnumChildWindows(Hwnd, enumChildProc, IntPtr.Zero);
return childern;
which Hwnd is the parent window handle
Delphi labels are non-windowed. That means that you cannot ever hope to obtain window handles for them since they are not windows.
If the Delphi VCL properly supported automation then you'd be able to use UI Automation to inspect the text of these controls. But the VCL only supports automation by dint of the underlying Win32 controls doing so. Since labels aren't windowed controls, again you are out of luck.
The only thing that can realistically hope to read this text is Delphi code inside the process. It does not sound as though that will be viable for you.

Alt keys and tab don't work in Windows Forms opened from a WPF application

I have lots of old Windows Forms applications that will eventually be ported to WPF (it is a large application so it can't be done in one sprint), and I have started the process by creating a main menu in WPF. The Windows Forms applications are separate windows opened from this menu.
The Windows Forms applications are opening and working without any problems except the issues I am having with the shortcut and Tab keys. The tab key is not moving focus to the next control, and the Alt key to trigger the &Search button no longer works.
What am I doing wrong?
A partial solution I discovered is to call this from your WPF constructor:
System.Windows.Forms.Integration.WindowsFormsHost.EnableWindowsFormsInterop();
(You need to reference the dll WindowsFormsIntegration.dll)
I say partial because not all key strokes function as expected. Eg, seems to work okay for simple forms.
See this:
http://msdn.microsoft.com/en-us/library/system.windows.forms.integration.windowsformshost.enablewindowsformsinterop(v=vs.100).aspx
I finally managed to fix the issue by hosting the winform inside a WindowsFormsHost control inside a WPF form.
public partial class MyWindow : Window
{
public MyWindow()
{
InitializeComponent();
Form winform = new Form();
// to embed a winform using windowsFormsHost, you need to explicitly
// tell the form it is not the top level control or you will get
// a runtime error.
winform.TopLevel = false;
// hide border because it will already have the WPF window border
winform.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.windowsFormsHost.Child = winform;
}
}
Please note that you may also need to hook up the winform close event if you have a button to close the form.
This is by design. Shortcut keys are handled at the message loop level, detected before the Windows message gets dispatched to the window with the focus. That's the reason those keys can work regardless of the focus.
Problem is, you don't have the Winforms message loop pumping the messages. Application.Run() is implemented by WPF in your program, not Winforms. So any of the code in Winforms that processes keyboard messages to implement shortcut keystrokes won't run.
There's no good solution for this, it is pretty fundamentally the "can't get a little pregnant" problem. This code in Winforms is locked up heavily since it would allow CAS bypass. The only workaround is to display a Form derived class that contain Winforms controls with its ShowDialog() method. That method pumps a modal message loop, the Winforms one, good enough to revive the shortcut keystroke handling code. Restructure your approach by converting the main windows first, dialogs last.
Another solution I found to handle focus on the Tab key is to override OnKeyDown like this:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
{
HandleFocus(this, ActiveControl);
}
else
{
base.OnKeyDown(e);
}
}
internal static void HandleFocus(Control parent, Control current)
{
Keyboard keyboard = new Keyboard();
// Move to the first control that can receive focus, taking into account
// the possibility that the user pressed <Shift>+<Tab>, in which case we
// need to start at the end and work backwards.
System.Windows.Forms.Control ctl = parent.GetNextControl(current, !keyboard.ShiftKeyDown);
while (null != ctl)
{
if (ctl.Enabled && ctl.CanSelect)
{
ctl.Focus();
break;
}
else
{
ctl = parent.GetNextControl(ctl, !keyboard.ShiftKeyDown);
}
}
}
The advantage of this solution is that it doesn't require neither a WindowsFormsHost nor a message pump which can be a hassle to implement. But I don't know if it is possible to handle shortcuts keys like this because I didn't need it.
Check if IsTabStop="True" and TabIndex is assigned. For Alt + Key shortcut, try using the underscore (_) character instead of the ampersand (&).

Disabling redraw in WinForms app

I'm working on a C#.Net application which has a somewhat annoying bug in it. The main window has a number of tabs, each of which has a grid on it. When switching from one tab to another, or selecting a different row in a grid, it does some background processing, and during this the menu flickers as it's redrawn (File, Help, etc menu items as well as window icon and title).
I tried disabling the redraw on the window while switching tabs/rows (WM_SETREDRAW message) at first. In one case, it works perfectly. In the other, it solves the immediate bug (title/menu flicker), but between disabling the redraw and enabling it again, the window is "transparent" to mouse clicks - there's a small window (<1 sec) in which I can click and it will, say, highlight an icon on my desktop, as if the app wasn't there at all. If I have something else running in the background (Firefox, say) it will actually get focus when clicked (and draw part of the browser, say the address bar.)
Here's code I added.
m = new Message();
m.HWnd = System.Windows.Forms.Application.OpenForms[0].Handle; //top level
m.WParam = (IntPtr)0; //disable redraw
m.LParam = (IntPtr)0; //unused
m.Msg = 11; //wm_setredraw
WndProc(ref m);
<snip> - Application ignores clicks while in this section (in one case)
m = new Message();
m.HWnd = System.Windows.Forms.Application.OpenForms[0].Handle; //top level
m.WParam = (IntPtr)1; //enable
m.LParam = (IntPtr)0; //unused
m.Msg = 11; //wm_setredraw
WndProc(ref m);
System.Windows.Forms.Application.OpenForms[0].Refresh();
Does anyone know if a) there's a way to fix the transparent-application problem here, or b) if I'm doing it wrong in the first place and this should be fixed some other way?
There are calls on classes derived from Control for this purpose. They are SuspendLayout and PerformLayout. As they are on Control and Form is derived from Control, your Form has them too.
These calls suffice for most updates but in other circumstances, just hiding the control using Visible = false can be enough. To stop the flicker during this hiding and then reshowing of the control, I usually draw the control to a bitmap which I show in a PictureBox during the update. This is useful when updating trees, tab controls, or lists (as can turning off sorting during the update in that last example).
The behavior you're describing is not normal for a .NET winforms application. The fact that you're using WndProc and sending messages in your example suggests that there is a lot of other unusual stuff going on with this form (I'm guessing there's more than one thread involved). Another possibility that is common in tabbed interfaces is that your form is simply overloaded with controls; sometimes this can cause strange behavior.
I have never witnessed or heard of anything remotely like what you describe.
You can try override the Paint method on your control that you do not want rendered and control it by some global boolean (=ignore all painting while some bool is true.)
If your control is a 3rd party, subclass it and override it there.
Then when you are satisified, set the bool to false and let the control be painted again (might have to force a paint when you turn it on again with .Refresh?)
If this is a custom control, you can try some of the control style flags: I think DoubleBuffered or AllPaintingInWmPaint might help. You can change the style bits using Control.SetStyle (which is protected, which is why you need to do it in your own custom Control class).

Should Form.ShowDialog(IWin32Window) work with any window handle?

When using System.Windows.Forms.ShowDialog(IWin32Window), should I be able to pass in an IWin32Window representing any window handle and have it be modal with respect to that window?
As part of an Internet Explorer 7 extension I'm trying to open a window modal with respect to an Internet Explorer tab. It's not the currently selected tab, but I can get the hwnd of the tab OK. However, when I pass this to ShowDialog my Form is shown, but it's not modal with respect to anything: I can still do things in Internet Explorer, including in the tab that's supposed to be the owner. My form is shown floating above the Internet Explorer windows and it stays on top, so it's not like it's just opened as a normal form, but it's not correctly modal.
Using Spy++, I can find my form and it's owner handle is correctly set.
Does this mean that something has gone wrong, or I'm doing something wrong? How do I make my form correctly modal?
FYI, I'm using this wrapper class to create an IWin32Window from a hwnd (thanks Ryan!):
/// <summary>
/// Wrapper class so that we can return an IWin32Window given a hwnd
/// </summary>
public class WindowWrapper : System.Windows.Forms.IWin32Window
{
public WindowWrapper(IntPtr handle)
{
_hwnd = handle;
}
public IntPtr Handle
{
get { return _hwnd; }
}
private IntPtr _hwnd;
}
UPDATE: Using Internet Explorer 7 & .NET 2.0
UPDATE: Playing around some more with Spy++ and the handles it exposes, I find that if I use a different hwnd then I can make my window modal to the tab:
I was using the tab's hwnd as suggested by the IWebBrowser2.HWND doc, which in Spy++ appears as class TabWindowClass. It has a child of class Shell DocObject View, which has a child of Internet_Explorer_Server. If I use the hwnd of the Internet Explorer_Server then it works correctly, for example, when I click with the mouse on other tabs, Internet Explorer reacts normally. When I click with the mouse on the tab of interest, it plays the windows d'oh sound and doesn't do anything.
I don't yet know how to programatically get the Internet_Explorer_Server hwnd, but it should be possible.
Also, for what it's worth, while playing with other window handles I was generally able to make my form modal to other applications and dialogs. So I guess the answer to my question is 'many but not all handles'... possibly it depends on the application?
UPDATE: Another side-note: The original reason I wanted to make my form modal to the tab instead of the whole window is that when opening a MessageBox from my form, passing the form as owner, the MessageBox would not always open on top of my form. If a new Internet Explorer tab had just been opened but wasn't active then the MessageBox would be hidden and that tab would start flashing. However, since Internet Explorer was disabled with my form opened modal it wasn't possible to switch to that tab, so Internet Explorer would be frozen. I thought that opening my form modal to the tab would solve this, but I've found another solution is to avoid using MessageBox: if I use a second form and ShowDialog(this) from my first form then the second form correctly opens to the front. So it seems that Form.ShowDialog() works better than MessageBox.Show() in some cases. More discussion in Problems with modal dialogs and messageboxes.
ShowDialog() does two important things. It starts pumping a message loop so it acts modally to the calling code. And it disables any other windows in the application with a EnableWindow(false) API call. The latter is what is not happening in your case. Not entirely surprising, considering that the window that needs to be disabled is not a WF window.
You may need to call EnableWindow() yourself. Be sure to re-enable it in before the dialog closes or Windows will go hunting for another app's window to give the focus to.
Your code is correct. The problem you are likely running into though is that IE has a threading model related to its tabs. I don't know the exact details but the short version is that each tab can and likely is running on a different thread than other tabs.
The Modal'ness of a dialog is specific to the thread where the dialog is running. UI on other threads will be unaffected by a model dialog on another thread. It's entirely possible you are able to access tabs which are running on a different thread for this reason.
Here's a more concise version of Ryan/Rory's WindowWrapper code:
internal class WindowWrapper : IWin32Window
{
public IntPtr Handle { get; private set; }
public WindowWrapper(IntPtr hwnd) { Handle = hwnd; }
}
I have never tried this from an IE extension, but I have a hunch that IE may not "respect" a Win32-style modal window the same way it does a modal window raised from Javascript using window.open().
Have you tested this code against something other than IE, just to confirm it works the way it should for other applications?
here is a build in solution in .NET:
public static NativeWindow FromHandle(IntPtr handle)

Categories