为什么最大化/最小化事件会导致关闭按钮在禁用后重新启用?

发布于 2024-09-03 05:51:39 字数 1401 浏览 5 评论 0 原文

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

显然,最小化或最大化是导致这种行为的原因,但是如何呢?我不知道在哪里可以防止这种行为。

我是否应该阻止最大化和最小化行为,或者 IP/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));
    }
}

I have used P/Invoke to call GetSystemMenu and EnableMenuItem (win32api) to disable the close functionality. However, after minimizing or maximizing my Windows Forms application the button is re-enabled.

Obviously minimizing or maximizing is causing the behavior, but how? I'm not sure where to look to prevent this behavior.

Should I be preventing the maximize and minimize behavior or is there something particularly wrong with the way in which I P/Invoked the calls? Once the application (main form) has loaded, I call the static method from a button click.

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));
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

东走西顾 2024-09-10 05:51:39

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

如果这不能满足您的需求,为了可用性,我不会禁用最小化/最大化,但您可以监听最小化/最大化事件并重新运行代码以禁用关闭按钮。最后可以处理关闭事件,而干脆不关闭。然后您就知道您的窗口肯定不会关闭,即使关闭按钮无意中启用。

Each window has a window class, which defines styles for all windows of that class. You can use CS_NOCLOSE class style to remove the close button for windows of that class. See here and here for details how to set this class flag.

If this doesn't give you what you want, I wouldn't disable minimize/maximize for sake of usability, but you could listen for minimize/maximimize events and re-run the code to disable the close button. Finally, it is possible to handle the close event, and simply not close. Then you know your window will definitely not be closed, even if the close button does inadvertently become enabled.

那些过往 2024-09-10 05:51:39

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

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

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

作为参考,请注意,与原始问题中所示完全相同的代码在本机 Win32 应用程序中运行良好。重新启用“关闭”菜单项似乎仅限于 WinForms 应用程序。

研究 System.Windows.Forms.Form 类的源代码发现了一个有趣的实现细节:.NET Framework 设计者显然决定在每次表单的 >WindowState 更改,其中包括系统发送的最大化和最小化事件。

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

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

The accepted answer does propose a possible workaround to the problem (and one that I've used many times), but it simply doesn't answer the question that was originally asked:

How/why does maximizing or minimizing the form cause the close button to be re-enabled after it was disabled using the GetSystemMenu and EnableMenuItem API functions?

I arrived at this question during the course of a completely fruitless Google search after discovering this seemingly unexplainable behavior for myself. Not finding an answer that actually explained the behavior,
I was forced to resort to some digging of my own.

For reference, note that the exact same code as shown in the original question works fine in a native Win32 application. The re-enabling of the Close menu item seems limited to WinForms applications.

Studying the source code for the System.Windows.Forms.Form class uncovers an interesting implementation detail: The .NET Framework designers apparently decided to adjust the form's system menu each time that the form's WindowState changes, which includes maximize and minimize events sent by the system.

Specifically, there are two methods by the name AdjustSystemMenu that are responsible for altering the system menu in response to the these events (and messing up any customization that you may have done yourself). If you're interested in examining the code (which I have abstained from posting here for the benefit of those involved with projects such as Mono), grab a free copy of .NET Reflector.

I'm not entirely sure why this decision was made, but at least I have my explanation now.

俏︾媚 2024-09-10 05:51:39

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

就像魅力一样。这仍然是一个 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);
        }
    }

I had the same requirement. After trying several ways to disable the close menu option and then deleting and trying to recreate it (correctly), I found this hack from Microsoft http://support.microsoft.com/kb/184686 .

Works like a charm. It's still a hack, but it works.

Here's my (loose) C# conversion of the VB original

        [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);
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文