【问题标题】:Why are Maximize/Minimize events causing the Close Button to be re-enabled after disabling it?为什么最大化/最小化事件会导致关闭按钮在禁用后重新启用?
【发布时间】:2010-05-28 22:35:57
【问题描述】:

我使用 P/Invoke 调用 GetSystemMenu 和 EnableMenuItem (win32api) 来禁用关闭功能。但是,在最小化或最大化我的 Windows 窗体应用程序后,该按钮会重新启用。

显然最小化或最大化会导致该行为,但是如何?我不确定在哪里可以防止这种行为。

我应该阻止最大化和最小化行为,还是我 P/Invoked 调用的方式有什么特别错误?加载应用程序(主窗体)后,我通过单击按钮调用静态方法。

class PInvoke
{
    // P/Invoke signatures
    [DllImport("user32.dll")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    [DllImport("user32.dll")]
    static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);

    // SysCommand (WM_SYSCOMMAND) constant
    internal const UInt32 SC_CLOSE = 0xF060;

    // Constants used with Add/Check/EnableMenuItem
    internal const UInt32 MF_BYCOMMAND = 0x00000000;
    internal const UInt32 MF_ENABLED = 0x00000000;
    internal const UInt32 MF_GRAYED = 0x00000001;
    internal const UInt32 MF_DISABLED = 0x00000002;

    /// <summary>
    /// Sets the state of the Close (X) button and the System Menu close functionality.
    /// </summary>
    /// <param name="window">Window or Form</param>
    /// <param name="bEnabled">Enabled state</param>
    public static void EnableCloseButton(IWin32Window window, bool bEnabled)
    {
        IntPtr hSystemMenu = GetSystemMenu(window.Handle, false);

        EnableMenuItem(hSystemMenu, SC_CLOSE, MF_BYCOMMAND | (bEnabled ? MF_ENABLED : MF_GRAYED));
    }
}

【问题讨论】:

  • 我根本不会禁用关闭按钮,而是显示一条消息,为什么当前无法关闭。

标签: c# .net winapi forms pinvoke


【解决方案1】:

每个窗口都有一个窗口类,它定义了该类的所有窗口的样式。您可以使用CS_NOCLOSE 类样式删除该类窗口的关闭按钮。有关如何设置此类标志的详细信息,请参阅 herehere

如果这不能满足您的需求,出于可用性考虑,我不会禁用最小化/最大化,但您可以侦听最小化/最大化事件并重新运行代码以禁用关闭按钮。最后,可以处理关闭事件,而根本不关闭。那么你就知道你的窗口肯定不会被关闭,即使关闭按钮在不经意间被启用了。

【讨论】:

  • 谢谢,这会做得很好。虽然这是一个学习练习,但我更喜欢删除它而不是禁用它。我主要感到困惑的是,最小化/最大化会重新启用它。再次感谢!
  • 两个链接都坏了
【解决方案2】:

接受的答案确实提出了一种可能的解决方法(我已经多次使用过),但它根本没有回答最初提出的问题:

如何/为什么最大化或最小化表单会导致关闭按钮在使用GetSystemMenuEnableMenuItem API 函数禁用后重新启用?

在发现这种看似无法解释的行为后,我在完全没有结果的 Google 搜索过程中提出了这个问题。没有找到真正解释这种行为的答案,
我不得不自己进行一些挖掘。

作为参考,请注意与原始问题中显示的完全相同的代码在本机 Win32 应用程序中可以正常工作。重新启用关闭菜单项似乎仅限于 WinForms 应用程序。

研究System.Windows.Forms.Form 类的源代码发现了一个有趣的实现细节:.NET Framework 设计人员显然决定在每次窗体的WindowState 更改时调整窗体的系统菜单,这包括系统发送的最大化和最小化事件。

具体来说,有两种名为 AdjustSystemMenu 的方法负责更改系统菜单以响应这些事件(并弄乱您可能自己完成的任何自定义)。如果您有兴趣检查代码(为了方便参与 Mono 等项目的人,我已放弃在此处发布代码),请获取.NET Reflector 的免费副本。

我不完全确定为什么会做出这个决定,但至少我现在有了自己的解释。

【讨论】:

    【解决方案3】:

    我也有同样的要求。在尝试了几种方法来禁用关闭菜单选项,然后删除并尝试重新创建它(正确)后,我发现了来自 Microsoft http://support.microsoft.com/kb/184686 的这个 hack。

    像魅力一样工作。它仍然是一个 hack,但它可以工作。

    这是我对 VB 原件的(松散的)C# 转换

            [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern int GetMenuItemCount(IntPtr hMenu);
    
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern bool DrawMenuBar(IntPtr hWnd);
    
    
        public static void EnableCloseButton(Form frm, bool enabled) {
            IntPtr hMenu;
            int n;
            hMenu = GetSystemMenu(frm.Handle, false);
            if (hMenu != IntPtr.Zero) {
                n = GetMenuItemCount(hMenu);
                if (n > 0) {
                    if (enabled) {
                        EnableClose(frm);
                    }
                    else {
                        DisableClose(frm);
                    }
                    SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);
                    DrawMenuBar(frm.Handle);
                    Application.DoEvents();
                }
            }
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct MENUITEMINFO {
            public uint cbSize;
            public uint fMask;
            public uint fType;
            public uint fState;
            public int wID;
            public int hSubMenu;
            public int hbmpChecked;
            public int hbmpUnchecked;
            public int dwItemData;
            public string dwTypeData;
            public uint cch;
            //    public int hbmpItem;
        }
    
        internal const UInt32 SC_CLOSE = 0xF060;
    
        //SetMenuItemInfo fMask constants.
        const UInt32 MIIM_STATE = 0x1;
        const UInt32 MIIM_ID = 0x2;
    
        //'SetMenuItemInfo fState constants.
        const UInt32 MFS_ENABLED = 0x0;
        const UInt32 MFS_GRAYED = 0x3;
        const UInt32 MFS_CHECKED = 0x8;
    
        internal const int MFS_DEFAULT = 0x1000;
    
        [DllImport("user32.dll")]
        static extern bool SetMenuItemInfo(IntPtr hMenu, int uItem, bool fByPosition, [In] ref MENUITEMINFO lpmii);
    
        [DllImport("user32.dll")]
        static extern bool GetMenuItemInfo(IntPtr hMenu, int uItem, bool fByPosition, ref MENUITEMINFO lpmii);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
        private const UInt32 WM_NCACTIVATE = 0x0086;
    
        private static void DisableClose(Form frm) {
            IntPtr hMenu;
            int n;
            hMenu = GetSystemMenu(frm.Handle, false);
            if (hMenu != IntPtr.Zero) {
                MENUITEMINFO mif = new MENUITEMINFO();
                mif.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO));
                mif.fMask = MIIM_ID | MIIM_STATE;
                mif.fType = 0;
                mif.dwTypeData = null;
                bool a = GetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
                mif.fState = MFS_GRAYED;
                SetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
                SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);
    
                mif.wID = -10;
                mif.fState = MFS_GRAYED;
                SetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
            }
        }
    
        private static void EnableClose(Form frm) {
            IntPtr hMenu;
            int n;
            hMenu = GetSystemMenu(frm.Handle, false);
            if (hMenu != IntPtr.Zero) {
                MENUITEMINFO mif = new MENUITEMINFO();
                mif.cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO));
                mif.fMask = MIIM_ID | MIIM_STATE;
                mif.fType = 0;
                mif.dwTypeData = null;
                bool a = GetMenuItemInfo(hMenu, -10, false, ref mif);
                mif.wID = (int)SC_CLOSE;
                SetMenuItemInfo(hMenu, -10, false, ref mif);
                SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);
    
                mif.fState = MFS_ENABLED;
                SetMenuItemInfo(hMenu, (int)SC_CLOSE, false, ref mif);
                SendMessage(frm.Handle, WM_NCACTIVATE, (IntPtr)1, (IntPtr)0);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      • 2016-03-04
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      • 2016-10-04
      • 1970-01-01
      相关资源
      最近更新 更多