Why are the Maximize / Minimize events that trigger the reactivation of the Close button after it is disabled?

I used P / Invoke to call GetSystemMenu and EnableMenuItem (win32api) to disable the close function. However, after minimizing or maximizing my Windows Forms application, the button is turned on again.

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

Should I prevent maximization and minimization of behavior or something especially wrong with how I P / Called Challenges? As soon as the application (main form) has loaded, I call the static method with the click of a button.

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)); } } 
+2
source share
3 answers

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

If this does not give you what you want, I would not turn off minimization / maximization for the sake of ease of use, but you could listen to minimize / maximize events and re-run the code to disable the close button. Finally, you can handle the close event and simply not close it. Then you know that your window will certainly not be closed, even if the close button accidentally turns on.

+5
source

The accepted answer offers a possible workaround for the problem (and one that I have used many times), but it just does not answer the question that was originally asked:

How / why does maximizing or minimizing the form cause the close button to turn on again after it is disabled using the GetSystemMenu and EnableMenuItem API functions?

I came to this question during a completely barren Google search after discovering this seemingly inexplicable behavior for myself. Not finding the answer that actually explained the behavior, I was forced to resort to some digging of my own.

For reference, please note that the same code as in the original question works fine in the native Win32 application. Re-enabling the Close menu item appears to be limited by WinForms applications.

Examining the source code for the System.Windows.Forms.Form class reveals an interesting implementation detail: The .NET Framework developers apparently decided to set up the form's system menu every time the WindowState form changes , which includes maximizing and minimizing events sent by the system .

In particular, there are two methods called AdjustSystemMenu , which are responsible for changing the system menu in response to these events (and ruined any setting that you may have made yourself). If you are interested in learning the code (which I refrained from posting here for the benefit of those involved in projects such as Mono), grab a free copy of .NET Reflector .

I'm not quite sure why this decision was made, but at least I have an explanation.

+3
source

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

It works like a charm. It is still a hack, but it works.

Here is my (free) VB original C # conversion

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

Source: https://habr.com/ru/post/1200388/


All Articles