Как обработать "несоответствие типов" в массиве 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.