Minimize And Maximize Userform Across 32-bit and 64-bit Environments

If you scour the internet you’ll find plenty of examples of how to give your VBA userform minimize and maximize buttons. The exact code will vary depending on the trinkets the programmer wants, but a typical snippet will look something like this:

Option Explicit

Private Declare Function GetWindowLong _
    Lib "user32.dll" Alias "GetWindowLongA" ( _
    ByVal hwnd As Long, _
    ByVal nIndex As Long) As Long
    
Private Declare Function SetWindowLong _
    Lib "user32.dll" Alias "SetWindowLongA" ( _
    ByVal hwnd As Long, _
    ByVal nIndex As Long, _
    ByVal dwNewLong As Long) As Long

Private Declare Function FindWindowA _
    Lib "user32.dll" ( _
    ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long
        
Private Declare Function DrawMenuBar _
    Lib "user32.dll" ( _
    ByVal hwnd As Long) As Long

Private Sub UserForm_Initialize()
    CreateMenu
End Sub

Private Sub CreateMenu()
    
    Const GWL_STYLE As Long = -16
    Const WS_SYSMENU As Long = &H80000
    Const WS_MINIMIZEBOX As Long = &H20000
    Const WS_MAXIMIZEBOX As Long = &H10000
    
    Dim lngFrmWndHdl As Long
    Dim lngStyle As Long
    
    lngFrmWndHdl = FindWindowA(vbNullString, Me.Caption)
    
    lngStyle = GetWindowLong(lngFrmWndHdl, GWL_STYLE)
    lngStyle = lngStyle Or WS_SYSMENU       'Add SystemMenu
    lngStyle = lngStyle Or WS_MINIMIZEBOX   'Add MinimizeBox
    lngStyle = lngStyle Or WS_MAXIMIZEBOX   'Add MaximizeBox

    SetWindowLong lngFrmWndHdl, GWL_STYLE, lngStyle
    
    DrawMenuBar lngFrmWndHdl

End Sub

The code is fine but it needs to be updated to run in 64-bit environments. In short, the way do to this is to include the PtrSafe keyword after each Declare keyword and to look up each function on MSDN and then use LongPtr instead of Long for each parameter and output if it represents a handle or a pointer. If you want the functions to work across both 32-bit and 64-bit Office then you may have to declare them multiple times inside a conditional compilation block. There are a couple of useful articles on MSDN which go into this in more depth:

The MSDN documentation for GetWindowLong and SetWindowLong states that they’ve been superseded by GetWindowLongPtr and SetWindowLongPtr, so it sounds like a good idea to use the new ones instead:

Note  To write code that is compatible with both 32-bit and 64-bit versions of Windows, use GetWindowLongPtr. When compiling for 32-bit Windows, GetWindowLongPtr is defined as a call to the GetWindowLong function.

To declare these new functions correctly in VBA, consideration needs to be given to the possibility that you’re using 32-bit Office – even if it is on 64-bit Windows. On 64-bit Windows, 32-bit Excel will try to use the 32-bit User32.dll in the C:\Windows\SYSWOW64 directory. This makes perfect sense but if you don’t believe me you can see it by using Process Explorer:

procmonex

The 32-bit User32.dll doesn’t contain the GetWindowLongPtr and SetWindowLongPtr functions. You can see this by using Dependency Walker:

DepWalkerUser32

In other words, if you try to call them from 32-bit Excel you will get the run-time error 453: Can’t Find DLL entry point [method name] in User32.dll. This means that the Win64 compilation constant needs to be used rather than VBA7 (I will talk more about these constants in a later blog post), so the API declarations should be written as follows:

Option Explicit

#If Win64 Then
    Private Declare PtrSafe Function GetWindowLongPtr _
        Lib "user32.dll" Alias "GetWindowLongPtrA" ( _
        ByVal hwnd As LongPtr, _
        ByVal nIndex As Long) As LongPtr

    Private Declare PtrSafe Function SetWindowLongPtr _
        Lib "user32.dll" Alias "SetWindowLongPtrA" ( _
        ByVal hwnd As LongPtr, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As LongPtr) As LongPtr

    Private Declare PtrSafe Function FindWindowA _
        Lib "user32.dll" ( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As LongPtr
        
    Private Declare PtrSafe Function DrawMenuBar _
        Lib "user32.dll" ( _
        ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Function GetWindowLongPtr _
        Lib "user32.dll" Alias "GetWindowLongA" ( _
        ByVal hwnd As Long, _
        ByVal nIndex As Long) As Long
    
    Private Declare Function SetWindowLongPtr _
        Lib "user32.dll" Alias "SetWindowLongA" ( _
        ByVal hwnd As Long, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As Long) As Long

    Private Declare Function FindWindowA _
        Lib "user32.dll" ( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long
        
    Private Declare Function DrawMenuBar _
        Lib "user32.dll" ( _
        ByVal hwnd As Long) As Long
#End If

Private Sub UserForm_Initialize()
    CreateMenu
End Sub

Private Sub CreateMenu()
    
    Const GWL_STYLE As Long = -16
    Const WS_SYSMENU As Long = &H80000
    Const WS_MINIMIZEBOX As Long = &H20000
    Const WS_MAXIMIZEBOX As Long = &H10000
    
    #If Win64 Then
        Dim lngFrmWndHdl As LongPtr
        Dim lngStyle As LongPtr
    #Else
        Dim lngFrmWndHdl As Long
        Dim lngStyle As Long
    #End If
    
    lngFrmWndHdl = FindWindowA(vbNullString, Me.Caption)
    
    lngStyle = GetWindowLongPtr(lngFrmWndHdl, GWL_STYLE)
    lngStyle = lngStyle Or WS_SYSMENU       'Add SystemMenu
    lngStyle = lngStyle Or WS_MINIMIZEBOX   'Add MinimizeBox
    lngStyle = lngStyle Or WS_MAXIMIZEBOX   'Add MaximizeBox

    SetWindowLongPtr lngFrmWndHdl, GWL_STYLE, lngStyle
    
    DrawMenuBar lngFrmWndHdl

End Sub
Advertisements

About Colin Legg

RAD Developer Microsoft MVP - Excel 2009 - 2014
This entry was posted in Microsoft Excel and tagged , , , , , , , , , , , , , , . Bookmark the permalink.

One Response to Minimize And Maximize Userform Across 32-bit and 64-bit Environments

  1. Pingback: Getting A Handle On Userforms [VBA] | RAD Excel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s