以下是什么原因导致excel 2016崩溃?
Option Explicit
Option Compare Text
Option Base 1
Sub p1()
Dim t(1), w()
t(1) = w
If IsMissing(w) Then DoEvents
If IsMissing(t(1)) Then DoEvents
End Sub
w
是一个未初始化的数组,但是将w
传递到ismissing(w)
很好,但是当通过t(1)
传递时,excel可再现地终止了... >
这是我使用vba设法生成的最短的代码,它只能杀死excel应用程序(即不使用createobject
或shell
之类的外部代码)。
还有其他这样的例子吗?
欢呼
答案 0 :(得分:17)
您将IsMissing
弄得一团糟,因为它认为未初始化的数组是格式错误的ParamArray
,并在实现中遇到了错误。
根据Windows事件查看器,导致Excel崩溃的根本异常是vbe7.dll中的访问冲突:
我看到的第一个报告(在上面进行注释)是Rubberduck回调失败,因为Excel是敬酒的。
Faulting application name: EXCEL.EXE, version: 15.0.5067.1000, time stamp: 0x5b76360d
Faulting module name: VBE7.DLL, version: 7.1.10.68, time stamp: 0x58def301
Exception code: 0xc0000005
Fault offset: 0x00000000001cd38c
Faulting process id: 0xac4
Faulting application start time: 0x01d45de81ef072ed
Faulting application path: C:\Program Files\Microsoft Office\Office15\EXCEL.EXE
Faulting module path: C:\PROGRA~1\COMMON~1\MICROS~1\VBA\VBA7.1\VBE7.DLL
访问冲突是由于VB运行时试图取消引用空指针而引起的。这是调试器在死亡时降落的地方:
00007FFF9CDDD383 mov rax,qword ptr [rsp+30h]
00007FFF9CDDD388 mov rax,qword ptr [rax+8]
00007FFF9CDDD38C movzx eax,word ptr [rax] <--fails, rax is 0.
00007FFF9CDDD38F cmp eax,1
00007FFF9CDDD392 jne 00007FFF9CD7B8A4
因此,让我们看一下IsMissing
function正在检查什么。 VBA本质上是COM的野兽,被调用的函数以Variant
的形式接收未传递的可选参数,该参数根据RPC约定具有VT_ERROR的类型。它不会作为Null
,Empty
或任何其他VB构造传递。以下代码演示:
Sub Foo()
Bar
End Sub
Sub Bar(Optional x)
Debug.Print VarPtr(x) 'This has a valid pointer.
Debug.Print IsError(x) 'True
Debug.Print VarType(x) '10 (vbError)
End Sub
您可以阅读技术性更强的说明on Raymond Chen's blog。
IsMissing
是意向传递的传入参数,因此它还必须注意ParamArray
s(在幕后,ParamArray
是只是Variant()
)。虽然,如果您不给它任何参数,过程仍然会得到一个数组-它的UBound
比LBound
低:
Sub Calling()
Called
End Sub
Sub Called(ParamArray params())
Debug.Print LBound(params) '<-- 0
Debug.Print UBound(params) '<-- -1
End Sub
在这里变得很有趣。同一过程签名中不能同时包含ParamArray
和可选参数-它可以是一个或另一个。当您查看函数签名时,会看到它需要指向Variant
的指针(也就是说,它需要一个ByRef
参数):
[entry(0x60000007), helpcontext(0x000f69b5)]
VARIANT_BOOL _stdcall IsMissing([in] VARIANT* ArgName);
但是这样的代码并不能提供预期的功能:
Sub DieExcelDie()
Dim x(1), y()
x(1) = y
x(1) = IsMissing(x(1))
End Sub
x(1)
必须编组为ByRef Variant
,才能作为参数传递给IsMissing
。因此,它 看到 的是一个Variant
数组,其LBound
和UBound
值均为零({{3} }深入探讨了未初始化的数组在内存中的外观。该参数尚未通过RPC机制进行处理,因此与包含一个传递参数的ParamArray
完全 无关。它将检查变体是否键入为VT_ERROR,看是否它是一个数组,然后尝试以这种方式进行处理。由于它不符合空参数列表的规范,因此它可能通过取消引用数据区域来检查第一个传递的参数是什么。 SAFEARRAY
结构看起来像这样 1 :
返回反汇编,而不是完全解开vbe7.dll的麻烦,上一条指令中的rax+8
最有可能加载了pvData
指针(一个8字节Excel的64位版本),您可以看到它为零。
这只是IsMissing
实现中的错误-应该检查空指针,而不是盲目地取消引用它。我怀疑,尽管无法验证,但它是与VB5一起引入的,并且所做的更改允许非{Variant
可选参数和默认值。