2010-05-28 66 views
2

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

显然最小化或最大化是造成行为,但如何?我不确定在哪里寻找以防止这种行为。

我是否应该防止最大化和最小化行为,或者在P /调用调用的方式中是否存在特别错误?一旦加载了应用程序(主窗体),我就可以通过点击按钮来调用静态方法。

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

我不会禁用的关闭按钮,在我所有的(宽松)C#转换,但为什么关闭目前无法代替显示一条消息。 – CodesInChaos 2011-01-04 09:44:07

回答

5

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

如果这不能给你你想要的,我不会为了可用性而禁用最小化/最大化,但是你可以监听最小化/最大化事件并重新运行代码来禁用关闭按钮。最后,有可能处理关闭事件,而不是关闭。那么你知道你的窗口一定不会被关闭,即使关闭按钮确实无意中被启用。

+0

谢谢,这会做得很好。尽管这是一个学习练习,但我更喜欢将其删除而不是将其禁用。我主要感到困惑的是,最小化/最大化会重新启用它。再次感谢! – Brainsick 2010-05-28 23:56:35

+0

这两个链接都被破坏了 – 2014-08-15 06:54:51

3

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

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

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

仅供参考,请注意原始问题中显示的完全相同的代码在原生Win32应用程序中正常工作。重新启用Close菜单项似乎仅限于WinForms应用程序。

学习的源代码,System.Windows.Forms.Form类揭示了一个有趣的实现细节:.NET框架设计者显然决定每个窗体的WindowState改变时间调整窗体的系统菜单,其中包括最大化和最小化由发送的事件系统。

具体来说,有两种名称为AdjustSystemMenu的方法负责修改系统菜单以响应这些事件(并且搞乱了您自己可能完成的任何自定义)。如果您有兴趣检查代码(为避免Mono等项目带来的好处,我放弃在这里发布的代码),请免费获得.NET Reflector

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

0

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

工程就像一个魅力。它仍然是一个黑客,但它的工作原理。

这里的VB原

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