Как обработать "несоответствие типов" в массиве VBA boolean/LongLong?

При попытке выполнить код ниже, я получаю ошибку VBA: несоответствие типов. Кто-нибудь знает причину (и решение?:-))

Я изменил типы данных с Long на LongLong, чтобы иметь возможность обрабатывать большие числа. До этого код (выдержка) работал нормально.

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

Конечно, этот код ни к чему не приводит, просто для тестирования этого куска кода.

1 ответ

Решение

tldr;

Вы не можете "справиться" с этим - LongLong не совместим с вашим ReDim заявление. (Хотя 999999999 технически вписался бы в Long компилятор не допускает сужающего преобразования там).


Максимальный размер любого массива в VBA определяется SAFEARRAY структура (определенная в разделе 2.2.30.10 спецификации протокола автоматизации OLE), которой она поддерживается внутри. Определение структуры в C++ таково:

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

Обратите внимание cbElements размер в байтах элемента массива. Это эффективно ограничивает каждый элемент до ~4 ГБ.


Проблема, с которой вы сталкиваетесь, заключается в SAFEARRAYBOUND структуры, хранящие информацию о размерах массива:

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

Это означает, что максимальное количество предметов, которое вы можете втиснуть в любое измерение SAFEARRAY независимо от языка программирования это значение ULONG_MAX (4294967295). Таким образом, следующие компиляции (хотя у меня не хватает памяти с распределением на моей машине):

Dim foo(-2147483646 To 2147483647) As Byte

Обратите внимание, что в приведенном выше примере нижняя граница является отрицательной, поскольку VBA также не поддерживает неподписанные типы, что представляет собой другое препятствие для кода VBA, который определяет размеры массивов. Технически вы можете получить массив с границами 0 To 4294967294 запросив его через функцию SafeArrayCreate, экспортированную oleaut32.dll, но я подозреваю, что вы столкнетесь с подобными проблемами при индексации.


Сняв слои еще дальше, вы начинаете преодолевать некоторые из более интересных пределов. Например, оглядываясь назад на SAFEARRAYBOUND структура выше, вы заметите, что, хотя вы можете иметь ULONG_MAX элементы, нижняя граница массива ограничена подписью LONG, Это ограничение переносится на большинство других средств автоматизации OLE, которые занимаются обработкой SAFEARRAY с, в том числе SafeArrayGetLBound и другие (интересно, SafeArrayGetUBound также подписан, что заставляет меня задуматься, не могли бы вы переполнить его...).


Так почему же MS не обновили это, когда добавили 64-битную поддержку? Ну, это сломало бы почти все. Помимо этого, действительно не было никакой острой необходимости - как только вы выйдете за рамки ULONG элементы, вы начинаете сталкиваться с очень реальными проблемами с памятью в том, что память для области данных должна быть выделена при создании структуры - в противном случае ее невозможно использовать через COM, потому что в корне этой структуры находится указатель, и в контракте говорится, что вызывающий код (независимо от клиента) должен иметь возможность использовать любой адрес, который попадает в область данных, включая VBA.

Другие вопросы по тегам