none
Controlling Minimize, Maximize and Close Menu Buttons

    Question

  • Access 2007
    Windows XP SP3

    There appear to be two approaches to controlling the Minimize, Maximize and Close menu buttons:
    1. Set Windows Styles using SetWindowLong
    2. Manipulate the menu using EnableMenuItem

    I have tried both and have gotten inconsistent results.  The code I am using is as follows:

    Windows Styles:
    Const WS_MAXIMIZEBOX = &H10000
    Const WS_MINIMIZEBOX = &H20000
    Const WS_SYSMENU = &H80000
    Public Function SetWindowStyle(ByVal vgwlStyle As GWLSTYLE, ByVal vbOn As Boolean)
        Const GWL_STYLE = -16&
        Const HWND_TOP = 0&
        Const SWP_NOSIZE = &H1&
        Const SWP_NOMOVE = &H2&
        Const SWP_DRAWFRAME = &H20&
        Dim lHwnd As Long
        Dim lStyle As Long    
        lHwnd = Application.hWndAccessApp
        lStyle = GetWindowLong(lHwnd, GWL_STYLE)
        If (vbOn) Then
            lStyle = lStyle Or vgwlStyle
        Else
            lStyle = lStyle And (Not vgwlStyle)
        End If
        Call SetWindowLong(lHwnd, GWL_STYLE, lStyle)
        Call SetWindowPos(Application.hWndAccessApp, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE Or SWP_DRAWFRAME)
    End Function
    This works for:
     SetWindowStyle WS_MINIMIZEBOX, False
     SetWindowStyle WS_MINIMIZEBOX, True
    But does not appear to do anything for:
     SetWindowStyle WS_SYSMENU, False
     SetWindowStyle WS_SYSMENU, True
     SetWindowStyle WS_MAXIMIZEBOX, False
     SetWindowStyle WS_MAXIMIZEBOX, True

    EnableMenuItem:
    Const SC_MINIMIZE = &HF020&
    Const SC_MAXIMIZE = &HF030&
    Const SC_CLOSE = &HF060&
    Const MF_GRAYED = &H1&
    Const MF_ENABLED = &H0&
    Public Function SetMenuItem(ByVal vEI As ENABLEITEM, ByVal vMF As MENUFLAGS)
        Const HWND_TOP = 0    
        Const SWP_NOSIZE = &H1
        Const SWP_NOMOVE = &H2
        Const SWP_DRAWFRAME = &H20
        Dim lHwnd As Long
        Dim lResult As Long    
        lHwnd = GetSystemMenu(Application.hWndAccessApp, False)
        lResult = EnableMenuItem(lHwnd, vEI, vMF)
        DrawMenuBar Application.hWndAccessApp
    End Function

    This appears to work for:
     SetMenuItem SC_CLOSE, MF_GRAYED
     SetMenuItem SC_CLOSE, MF_ENABLED
    But does not appear to do anything for:
     SetMenuItem SC_MAXIMIZE, MF_GRAYED
     SetMenuItem SC_MAXIMIZE, MF_ENABLED
     SetMenuItem SC_MINIMIZE, MF_GRAYED
     SetMenuItem SC_MINIMIZE, MF_ENABLED

    What do I need to do to get at least one of the approaches to work accross all three menu/button items?

    Saturday, August 29, 2009 6:59 AM

Answers

  • But the actual issue is since the orginal objective is,
    >>The original objective was to disable the close button to allow the application to close gracefully
    based on my current research, I haven't found any API ways, Ribbon customization, or CommandBar object model ways to disable Office Access 2007's Office Menu->Exit Access button. It cannot be disabled through any way. (It is not a ribbon item, not exact the CommandBarControl, not system menu item, no a normal native control we can get the handle).


    Upon further research I found you can remove "Exit Access" from the Office button using the USysRiibbons table.  You can actually remove everything from the Office Button EXCEPT Recent Documents (which is annoying as it looks funny):
    <customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
     <commands>
      <command idMso="FileNewDatabase" enabled="false"/>
      <command idMso="FileCloseDatabase" enabled="false"/>
      <command idMso="ApplicationOptionsDialog" enabled="false"/>
      <command idMso="FileOpenDatabase" enabled="false"/>
      <command idMso="FileExit" enabled="false"/>
    </commands>
     <ribbon>
      <officeMenu>
       <control idMso="FileNewDatabase" visible="false"/>
       <control idMso="SourceControlCreateDatabaseFromProject" visible="false"/>
       <control idMso="FileOpenDatabase" visible="false"/>
       <control idMso="FileSave" visible="false"/>
       <control idMso="ConvertDatabaseFormat" visible="false"/>
       <control idMso="FileSaveAsMenuAccess" visible="false"/>
       <control idMso="FileManageMenu" visible="false"/>
       <control idMso="FileSendAsAttachment" visible="false"/>
       <control idMso="MenuPublish" visible="false"/>
       <control idMso="FileServerMenu" visible="false"/>
       <control idMso="FileCloseDatabase" visible="false"/>
       <control idMso="FilePrintMenu" visible="false"/>
      </officeMenu>
     </ribbon>
    </customUI>

     

    • Marked as answer by saberman Thursday, September 10, 2009 5:13 AM
    Monday, September 07, 2009 7:29 AM

All replies

  • Hello Saberman,

    Nice to see you again. I do some tests regarding to the EnableMenuItem API in my side and can reproduce what you describe.

    I found some explanations from Microsoft KB article,
    http://support.microsoft.com/kb/82876/EN-US

    Application itself will continously reset the System Menu Item status depending on main window status. So once we use EnableMenuItem to set the SC_MAXIMIZE and SC_MINIMIZE menu item to disabled. Application will find these menuitem via their IDs and reset them back to enabled. This is by design behavior. However we have a workaround that is using ModifyMenu to disable the items. ModifyMenu API can change menu item ID. So Application itself cannot revert it back automatically.

    -----------------------------
        Partial Public Class NativeMethods

            '''Return Type: BOOL->int
            '''hMnu: HMENU->HMENU__*
            '''uPosition: UINT->unsigned int
            '''uFlags: UINT->unsigned int
            '''uIDNewItem: UINT_PTR->unsigned int
            '''lpNewItem: LPCSTR->CHAR*
            <System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint:="ModifyMenuA")> _
            Public Shared Function ModifyMenuA(<System.Runtime.InteropServices.InAttribute()> ByVal hMnu As System.IntPtr, ByVal uPosition As UInteger, ByVal uFlags As UInteger, ByVal uIDNewItem As UInteger, <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> ByVal lpNewItem As String) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
        End Class

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            SetMenuItem(NativeConstants.SC_CLOSE, NativeConstants.MF_DISABLED, 65535, "Close")
            SetMenuItem(NativeConstants.SC_MAXIMIZE, NativeConstants.MF_DISABLED, 65534, "Maximize")
            SetMenuItem(NativeConstants.SC_MINIMIZE, NativeConstants.MF_DISABLED, 65533, "Minimize")
        End Sub

        Public Sub SetMenuItem(ByVal vEI As UInteger, ByVal vMF As UInteger, ByVal newItemID As UInteger, ByVal lPNewItem As String)
            Dim lHwnd As Long
            Dim lResult As Long
            lHwnd = NativeMethods.GetSystemMenu(Me.Handle, False)
            lResult = NativeMethods.ModifyMenuA(lHwnd, vEI, vMF, newItemID, lPNewItem)
            NativeMethods.DrawMenuBar(Me.Handle)
        End Sub
    -----------------------------


    By the way, this only affects menu item in the system menu(appear when clicking left top icon or right clicking icon in taskbar). It does not affect the control box at the right top of the window. All Office 2007 application draw their title bars in main window where we cannot customize.

    Best regards,
    Colbert Zhou


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Tuesday, September 01, 2009 8:07 AM
    Moderator
  • There are a couple of problems with your example (besides it being for VB.NET not VBA in Office) and the KB article (which says it applies to VB 1, 2 and 3 -- 16 bit products):

    I ran my tests using Access 2007 VBA (VB 6.5):

    Using EnableMenuItem worked for the Close X on the Access application and for the Close item when you right click the icon in the taskbar.  It does not appear that VBA (VB 6.5) resets this or eats the change request.
    Using EnableMenuItem did not work for the Minimize and Maximize buttons on the system menu nor for the right click menu.  I don't know if the VBA engine is resetting the items or the SetMenuTime API is filtering out requests to change them or just failing to disable them.  If the VBA engine is doing it it would have to be filtering the change menu messages and eating the ones for Minimize and Maximize but not for Close.

    I am also confused as to why changing Access's window's Style only affected the Minimize button.  Changing a window's style is equivalent to removing a menu item.  If you turn the bit off an item it should be gone.  Again, here the VBA engine would have to be eating the Minimize and Close changes and not the Minimize change.

    This article: http://support.microsoft.com/kb/184686 suggests using SetMenuItemInfo to disable the close button but goes on to say

    Additional Notes
    Visual Basic handles enabling and disabling the standard Control menu items, including Restore, Size, Move, Minimize, and Maximize (the Close menu is always enabled). Generally, Visual Basic enables/disables the menu items depending on the window's state (minimized, maximized, or normal) by referencing these menu items by their menu item IDs.

    As a result, if we merely add or remove MFS_GRAYED from a menu's fState, Visual Basic immediately resets it. The workaround is to change the menu item's ID (which also renders the item inoperable).

    Using this workaround, we can effectively disable a menu item that Visual Basic intends to be enabled, but we cannot enable a menu item Visual Basic intends to be disabled (the menu item must have its regular ID to be operable).

    Enabling a menu item that Visual Basic intends to be disabled requires removing the item and replacing it with a menu item of our own (this is not covered in this article).

    When the Close menu item is enabled or disabled, the Close box on the title bar should also be normal or grayed, respectively. Sending the message WM_NCACTIVATE (nonclient activate) notifies the window to refresh its title bar so the Close box is updated to appear normal or grayed.

    Which seems to be in line with the results of my SetMenuItem tests and it applies to VB 6.  That still doesn't explain why changing the window's style (which doesn't just change the state of a menu item but removes it) only affected the Minimize button.

    Tuesday, September 01, 2009 11:20 PM
  • >>Using EnableMenuItem worked for the Close X on the Access application and for the Close item when you right click the icon in the taskbar.  It does not appear that VBA (VB 6.5) resets this or eats the change request.

    Not VBA, but application itself will reset the status. An UI application(including Microsoft Office Access) will reset the SC_MINIMIZE/SC_CLOSE/SC_MAXIMIZE command depending on the window's status. SC_MINIMIZE and SC_MAXIMIZE will be resetted immediately after we call EnableMenuItem to disable them. But the SC_CLOSE is reset after we do some changes to application main window(Maximize and restore window). That is to say, after you call EnableMenuItem to disable SC_CLOSE, you can see the Close command is disabled successfully. But if you maximize and restore the window, Close command is reset to enabled. Using ModifyMenuItem API can workaround this because the ID of menuitem is modified. Access cannot find the expected ID item to reset.

    The thing I mainly concern is what the objective is, we want to achieve in Access. If it is disabling Maximize/Minimize/Close command in system menu from Access Application, I think the ModifyMenuItem already works in my side. But the problem is Office application is not same as normal UI application. Office team does a lot of customization to Office UI. Although we cannot close the Access from the System Menu, but we still can close it from Office button->Exit Access.


    Best regards,
    Colbert Zhou
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Wednesday, September 02, 2009 11:03 AM
    Moderator
  • The original objective was to disable the close button to allow the application to close gracefully. 

    >But if you maximize and restore the window, Close command is reset to enabled.

    I don't see that.  After disabling the close menu item using EnableMenuItem it stayed disabled after maximizing, minimizing and restoring the application. 

    I can uderstand the Office team doing a lot of customization to the Office UI that is outside of the user's control when in the full Office environment.  But to do that with a compiled application using the runtime is unforgivable. 

    Is there a simple way to disable the Office button?  If not does the Office button have its own handle in which case I should be able to find and disable it.  The other possibility is to subclass the application window and eat the WM_CLOSE mesage unless the application requested it.
    Wednesday, September 02, 2009 11:49 PM
  • >>I don't see that.  After disabling the close menu item using EnableMenuItem it stayed disabled after maximizing, minimizing and restoring the application. 

    Yes, I can confirm this. Access 2007 seems a bit special. As to a normal application's Window, the SC_CLOSE will reset if we maximize/minimize and restore the main Window.

    The KB is still right, I think. The SC_MAXIMIZE and SC_MINIMIZE are resetted immediately every time we disable them. Using ModifyMenuItem to change their default menu ID can workaround the issue. I already test the API ModifyMenuItem on Access. It disable all of the three menu items without resetting. My codes is in .NET. But I think calling API should be very similar in VBA. If you need VBA codes, let me know and I will write one to post here.


    But the actual issue is since the orginal objective is,
    >>The original objective was to disable the close button to allow the application to close gracefully
    based on my current research, I haven't found any API ways, Ribbon customization, or CommandBar object model ways to disable Office Access 2007's Office Menu->Exit Access button. It cannot be disabled through any way. (It is not a ribbon item, not exact the CommandBarControl, not system menu item, no a normal native control we can get the handle).

    So the last suggestion I can make is already mentioned in your post, :-) subclassing the application Window to filter WM_CLOSE messages.


    Best regards,
    Colbert Zhou
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Friday, September 04, 2009 9:30 AM
    Moderator
  • But the actual issue is since the orginal objective is,
    >>The original objective was to disable the close button to allow the application to close gracefully
    based on my current research, I haven't found any API ways, Ribbon customization, or CommandBar object model ways to disable Office Access 2007's Office Menu->Exit Access button. It cannot be disabled through any way. (It is not a ribbon item, not exact the CommandBarControl, not system menu item, no a normal native control we can get the handle).


    Upon further research I found you can remove "Exit Access" from the Office button using the USysRiibbons table.  You can actually remove everything from the Office Button EXCEPT Recent Documents (which is annoying as it looks funny):
    <customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
     <commands>
      <command idMso="FileNewDatabase" enabled="false"/>
      <command idMso="FileCloseDatabase" enabled="false"/>
      <command idMso="ApplicationOptionsDialog" enabled="false"/>
      <command idMso="FileOpenDatabase" enabled="false"/>
      <command idMso="FileExit" enabled="false"/>
    </commands>
     <ribbon>
      <officeMenu>
       <control idMso="FileNewDatabase" visible="false"/>
       <control idMso="SourceControlCreateDatabaseFromProject" visible="false"/>
       <control idMso="FileOpenDatabase" visible="false"/>
       <control idMso="FileSave" visible="false"/>
       <control idMso="ConvertDatabaseFormat" visible="false"/>
       <control idMso="FileSaveAsMenuAccess" visible="false"/>
       <control idMso="FileManageMenu" visible="false"/>
       <control idMso="FileSendAsAttachment" visible="false"/>
       <control idMso="MenuPublish" visible="false"/>
       <control idMso="FileServerMenu" visible="false"/>
       <control idMso="FileCloseDatabase" visible="false"/>
       <control idMso="FilePrintMenu" visible="false"/>
      </officeMenu>
     </ribbon>
    </customUI>

     

    • Marked as answer by saberman Thursday, September 10, 2009 5:13 AM
    Monday, September 07, 2009 7:29 AM
  • Thanks for your sharing! Actually I had looked through the Access Ribbon control ID list downloaded from,
    http://www.microsoft.com/downloads/details.aspx?familyid=4329D9E9-4D11-46A5-898D-23E4F331E9AE&displaylang=en

    But it does not show the command. So I miss the FileExit here. Your founds will definitely help more community members who are trying to achieve this objective. Greate man!


    Best regards,
    Ji.Zhou
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Tuesday, September 08, 2009 9:50 AM
    Moderator
  • I'm back -- I now need to get rid of or disable the Maximize/Minimize buttons in the Menu Box in the top right corner of the application window.

    None of the examples in the thread work for the Maximize button.  (The Minimize botton can be removed using Windows Styles.)

    Can you point me to code for handling the Maximize/Minimize buttons in the Menu Box?
    Tuesday, September 29, 2009 5:46 PM