2010-05-28 62 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); 
     } 
    }