arraysvbalong-long

How to handle "type mismatch" in VBA boolean/LongLong array?


When trying to execute below code, I get the VBA error: type mismatch. Anyone know the cause (and solution? :-))

I changed datatypes from Long to LongLong to be able to handle larger numbers. Before this the code (excerpt) was working fine.

Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hDC As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hDC As LongPtr) As Long

Public Sub TestScreenResolution()
    Debug.Print ScreenResolution
End Sub
Private Function ScreenResolution() As Double
     Dim hDC As Long
     hDC = GetDC(0)
     ScreenResolution = GetDeviceCaps(hDC, 88)
     ReleaseDC 0, hDC
End Function

Public Sub TestMySub()
    Call MySub(999999999)
End Sub
Private Sub MySub(ByVal x As LongLong)

Dim y As LongLong
Dim Max As LongLong
Dim Min As LongLong

Max = x * x
Min = (x - 1) * (x - 1)

Dim Arr() As Boolean   'Default Boolean type is False
ReDim Arr(Min To Max) ''<<< "Type Mismatch" compile error

For y = Max To Min Step -2
    Arr(y) = True
Next y

End Sub

Of course this code is leading to nothing, just for testing this piece of code.


Solution

  • tldr;

    You can't "handle" it - LongLong isn't compatible with your ReDim statement. (Although 999999999 would technically fit into a Long, the compiler doesn't allow a narrowing conversion there).


    The maximum size of any array in VBA is determined by the SAFEARRAY structure (defined in section 2.2.30.10 of the OLE Automation Protocol specification) that it is backed by internally. The definition of the structure in C++ is this:

    typedef struct tagSAFEARRAY {
      USHORT         cDims;
      USHORT         fFeatures;
      ULONG          cbElements;
      ULONG          cLocks;
      PVOID          pvData;
      SAFEARRAYBOUND rgsabound[1];
    }
    

    Note, the cbElements the size in bytes of an array item. This effectively limits each item to ~4GB.


    The problem that you run into is in the SAFEARRAYBOUND structures that store information about the array's dimensions:

    typedef struct tagSAFEARRAYBOUND {
      ULONG cElements;
      LONG  lLbound;
    } SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
    

    That means that the maximum number of total items you can cram into any dimension of a SAFEARRAY regardless of programming language is the value of ULONG_MAX (4,294,967,295). Thus the following compiles (although it runs out of memory with the allocation on my machine):

    Dim foo(-2147483646 To 2147483647) As Byte
    

    Note that in the above example, the lower bound is negative because VBA also doesn't support unsigned types, which presents the other hurdle for VBA code that is sizing arrays. You could technically get an array with bounds of 0 To 4294967294 by requesting one through the SafeArrayCreate function exported by oleaut32.dll, but I'm suspecting that you'd run into similar problems indexing into it.


    Peeling the layers back even further, you start hitting some of the more interesting limits. For example, looking back at the SAFEARRAYBOUND structure above, you'll notice that although you can have ULONG_MAX elements, the lower bound of the array is constrained by a signed LONG. This is limitation is carried over into most of the other OLE automation that deal with handling SAFEARRAYs, including SafeArrayGetLBound and others (interestingly, SafeArrayGetUBound is also signed, which makes me wonder if you could overflow it...).


    So why didn't MS update this when they added 64 bit support? Well, it would have broken pretty much everything. Beyond that, there really wasn't any pressing need to - once you get beyond ULONG elements, you start running into very real problems with memory in that the memory for the data area has to be allocated when the structure is created - otherwise it's not possible to use it via COM, because at the root of that structure is a pointer, and the contract says that the calling code (regardless of client) must be able to use any address that falls within the data area, VBA included.