如何处理 VBA boolean/LongLong 数组中的 "type mismatch"?
How to handle "type mismatch" in VBA boolean/LongLong array?
尝试执行以下代码时,出现 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
当然这段代码是没有结果的,只是为了测试这段代码。
tldr;
你不能 "handle" 它 - LongLong
与你的 ReDim
声明不兼容。 (尽管 999999999
在技术上适合 Long
,但编译器不允许在那里进行缩小转换。
VBA 中任何数组的最大大小由内部支持的 OLE 自动化协议规范的 SAFEARRAY
structure (defined in section 2.2.30.10 决定。 C++中结构体的定义是这样的:
typedef struct tagSAFEARRAY {
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[1];
}
请注意,cbElements
数组项的大小(以字节为单位)。这有效地将每个项目限制为 ~4GB。
您 运行 遇到的问题是 SAFEARRAYBOUND
structures 存储有关数组维度的信息:
typedef struct tagSAFEARRAYBOUND {
ULONG cElements;
LONG lLbound;
} SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
这意味着无论编程语言如何,您可以塞入任何维度的最大项目总数是ULONG_MAX
的值(4,294,967,295)。因此下面的编译(虽然它 运行s 在我的机器上分配内存不足):
Dim foo(-2147483646 To 2147483647) As Byte
请注意,在上面的示例中,下限是负数,因为 VBA 也不支持无符号类型,这为 VBA 调整数组大小的代码带来了另一个障碍。从技术上讲,您可以通过 oleaut32.dll 导出的 SafeArrayCreate 函数请求一个边界为 0 To 4294967294
的数组,但我怀疑您会 运行 遇到类似的问题对其进行索引。
进一步剥离这些层,您开始遇到一些更有趣的限制。例如,回顾上面的 SAFEARRAYBOUND
结构,您会注意到虽然可以有 ULONG_MAX
元素 ,但数组的下限受限于a 已签名 LONG
。这个限制被转移到大多数处理 SAFEARRAY
s 的其他 OLE 自动化中,包括 SafeArrayGetLBound
and others (interestingly, SafeArrayGetUBound
是 also 签名,这让我想知道你是否可能会溢出它......)。
那么为什么 MS 在添加 64 位支持时不更新它呢?好吧,它几乎会破坏 一切 。除此之外,真的没有任何紧迫的需要 - 一旦你超过 ULONG
个元素,你就会开始 运行 进入非常实际的内存问题,因为数据区域的内存 必须在创建结构时分配 - 否则无法通过 COM 使用它,因为该结构的根是一个指针,并且合同规定调用代码(无论客户端如何)必须能够使用数据区内的任何地址,包括 VBA。
尝试执行以下代码时,出现 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
当然这段代码是没有结果的,只是为了测试这段代码。
tldr;
你不能 "handle" 它 - LongLong
与你的 ReDim
声明不兼容。 (尽管 999999999
在技术上适合 Long
,但编译器不允许在那里进行缩小转换。
VBA 中任何数组的最大大小由内部支持的 OLE 自动化协议规范的 SAFEARRAY
structure (defined in section 2.2.30.10 决定。 C++中结构体的定义是这样的:
typedef struct tagSAFEARRAY { USHORT cDims; USHORT fFeatures; ULONG cbElements; ULONG cLocks; PVOID pvData; SAFEARRAYBOUND rgsabound[1]; }
请注意,cbElements
数组项的大小(以字节为单位)。这有效地将每个项目限制为 ~4GB。
您 运行 遇到的问题是 SAFEARRAYBOUND
structures 存储有关数组维度的信息:
typedef struct tagSAFEARRAYBOUND { ULONG cElements; LONG lLbound; } SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
这意味着无论编程语言如何,您可以塞入任何维度的最大项目总数是ULONG_MAX
的值(4,294,967,295)。因此下面的编译(虽然它 运行s 在我的机器上分配内存不足):
Dim foo(-2147483646 To 2147483647) As Byte
请注意,在上面的示例中,下限是负数,因为 VBA 也不支持无符号类型,这为 VBA 调整数组大小的代码带来了另一个障碍。从技术上讲,您可以通过 oleaut32.dll 导出的 SafeArrayCreate 函数请求一个边界为 0 To 4294967294
的数组,但我怀疑您会 运行 遇到类似的问题对其进行索引。
进一步剥离这些层,您开始遇到一些更有趣的限制。例如,回顾上面的 SAFEARRAYBOUND
结构,您会注意到虽然可以有 ULONG_MAX
元素 ,但数组的下限受限于a 已签名 LONG
。这个限制被转移到大多数处理 SAFEARRAY
s 的其他 OLE 自动化中,包括 SafeArrayGetLBound
and others (interestingly, SafeArrayGetUBound
是 also 签名,这让我想知道你是否可能会溢出它......)。
那么为什么 MS 在添加 64 位支持时不更新它呢?好吧,它几乎会破坏 一切 。除此之外,真的没有任何紧迫的需要 - 一旦你超过 ULONG
个元素,你就会开始 运行 进入非常实际的内存问题,因为数据区域的内存 必须在创建结构时分配 - 否则无法通过 COM 使用它,因为该结构的根是一个指针,并且合同规定调用代码(无论客户端如何)必须能够使用数据区内的任何地址,包括 VBA。