Search code examples
excelvba32bit-64bit

How to fix a VBA "type mismatch" error after switching to 64-bit Excel


I was using code that was working fine when I was running the 32-bit version of Excel. After I was switched to the 64-Bit version, the macro broke. I updated the dll calls to use LongPtr everywhere instead of Long.
Is there any way to determine which arguments and return types need to be changed for VBA7, and which don't, for a specific Declare Function?

Here is an example of some of the "Declare Functions" that I have updated (there were actually several more too).

#If VBA7 Then
    Private Declare PtrSafe Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As LongPtr) As LongPtr
    Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As LongPtr, ByVal nWidth As LongPtr, ByVal nHeight As LongPtr) As LongPtr
    Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As LongPtr) As LongPtr
    Private Const LOGPIXELSY As Long = 90
#Else
    Private Declare CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long
    Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
    Private Const LOGPIXELSY As Long = 90
#End If

This code was adapted from an answer to this question: vb macro string width

Relevant snippet is copied here below:

Private Function GetLabelSize(text As String, font As StdFont) As SIZE
    Dim tempDC As Long
    Dim tempBMP As Long
    Dim f As Long
    Dim lf As LOGFONT
    Dim textSize As SIZE

    tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0)
    tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)

I get a runtime error that just says "Compile Error: Type Mismatch." The function call to CreateDC is highlighted, and the debugger breaks on the function GetLabelSize. I have no idea which variable is now causing the error. I'm also assuming that once I fix this first error, I'll have other errors too.

Do I need to pass the value of that last argument (ByVal 0) as an explicitly typed variable? If so how?


Solution

  • I updated the dll calls to use LongPtr everywhere instead of Long.

    You should have not done that.

    By adding PtrSafe to a function declaration, you promise to the compiler that you have put LongPtr in all places where it needs to be, and nowhere else.

    LongPtr is a pointer-sized integer. It must be used for things that have the same size as a pointer.

    To learn which Windows API types should be described as LongPtr, you must look at the original function signature, consult https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types, track the used data types through all the typedefs down to the basic types, and use LongPtr for those that are pointers to things.

    For the functions that you have shown, that would be

    #If VBA7 Then
        Private Declare PtrSafe Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As LongPtr) As LongPtr
        Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As LongPtr, ByVal nWidth As Long, ByVal nHeight As Long) As LongPtr
        Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As LongPtr) As Long
    #Else
        Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Long) As Long
        Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
        Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
    #End If
    

    When you declare variables to hold your LongPtr results, you need to use #If VBA7 too:

    #If VBA7 Then
        Dim tempDC As LongPtr
        Dim tempBMP As LongPtr
    #Else
        Dim tempDC As Long
        Dim tempBMP As Long
    #End If
    

    If you don't have to support Office 2007 and older, you can ditch the #If VBA7s and only use the LongPtr branch.