从控件中对ParentForm WndProc进行子类化

我正在将自定义的MenuStrip控件集成到另一个.NET应用程序中。 除焦点问题外,MenuStrip工作正常。 如果托管MenuStrip的表单没有聚焦,则用户需要单击两次 – 一次激活表单,另一次选择菜单项。 主机表单是单独的.NET应用程序的一部分,我无法修改。 我只能修改我的控件的源代码。

我在WinForm级别找到了一个覆盖Form WndProc方法的解决方案 ,除了我没有WinForm的源代码访问权限,我也可以重新编译主机应用程序和表单。

有没有办法对Control的ParentForm进行子类化,以便在没有聚焦的情况下自动激活ParentForm?

我能够使用ContainerControl.ParentForm来获取对宿主表单的引用,但是在生产环境中,ParentForm返回null,我仍然需要找到解决方案。 我把NewWndProc处理程序放在try / catch中,以防它抛出任何exception,虽然我不确定它可以抛出什么(如果有的话)。 我可能只需要使用Win32函数而不使用Form.Focused和Form.Activate().NET方法。 无论如何,这是代码:

public class FocusFormWrapper { #region DllImport public const int GWL_WNDPROC = (-4); public const UInt32 WM_CLOSE = 0x0010; public const UInt32 WM_PARENTNOTIFY = 0x0210; public delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll", SetLastError = true)] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr newWndProc); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr FindWindow(string _ClassName, string _WindowName); [DllImport("user32.dll", SetLastError = true)] public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); [DllImport("user32.dll", SetLastError = true)] private static extern bool SetForegroundWindow(IntPtr hWnd); #endregion DllImport private Form myForm = null; private IntPtr hWndTarget = IntPtr.Zero; private IntPtr oldWndProc = IntPtr.Zero; private WndProcDelegate newWndProc; private FocusFormWrapper() { } public FocusFormWrapper(Form sourceForm) { if (sourceForm == null) throw new ArgumentNullException("sourceForm"); if (!IsWindow(sourceForm.Handle)) throw new ArgumentException("sourceForm IsWindow failed"); myForm = sourceForm; hWndTarget = myForm.Handle; AddSubclass(); } ~FocusFormWrapper() { RemoveSubclass(); myForm = null; newWndProc = null; } private int AddSubclass() { int result = -1; if (myForm != null && newWndProc == null) { newWndProc = new WndProcDelegate(NewWndProc); oldWndProc = GetWindowLong(hWndTarget, GWL_WNDPROC); result = SetWindowLong(hWndTarget, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newWndProc)); } return result; } public int RemoveSubclass() { int result = -1; if (myForm != null && newWndProc != null) { result = SetWindowLong(hWndTarget, GWL_WNDPROC, oldWndProc); newWndProc = null; } return result; } public IntPtr NewWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { try { if (msg == WM_PARENTNOTIFY && !myForm.Focused) { // Make this form auto-grab the focus when menu/controls are clicked myForm.Activate(); } if (msg == WM_CLOSE) { RemoveSubclass(); } } catch { } return CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam); } }