12

这是我不明白的一个。

给定这个类模块(精简到重现崩溃所需的最低限度):

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "TestCrashClass"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Public Function Init() As TestCrashClass
Attribute Init.VB_UserMemId = 0
    Dim tcc As New TestCrashClass
    Set Init = tcc
End Function

Public Property Get Data() As String
    Data = "test data"
End Property

谁能告诉我为什么当我输入这段代码时 Excel 会完全崩溃:

Sub MakeExcelCrash()
    With TestCrashClass(

在这一点上,我这个可爱的消息:

Excel 崩溃 - 哎呀!

即使我输入了一个没有冒犯括号的完整过程,然后尝试稍后添加它们,我也会遇到同样的崩溃。

我能让 Excel 不崩溃的唯一方法是将一组()从其他地方复制/粘贴到这行代码。

 Sub MakeExcelCrash()
     With TestCrashClass()
         Debug.Print .Data
     End With
 End Sub

如果该Init()方法有一个参数——甚至是一个可选参数——它在输入左括号时不会崩溃。

我更好奇为什么会发生这种情况,而不是解决方法。它实际上并没有经常出现在我的代码中,当它出现时,我可以通过改变方法来修复它,但我真的很沮丧,因为我不知道是什么导致了这些崩溃。那么也许对VBA内部工作有更多了解的人可以向我解释一下?

4

3 回答 3

12

你甚至不需要这个With块。 任何在类名之后键入的尝试(都会导致 Excel 失效。

问题是您将VB_PredeclaredId设置设置为 true 并且默认成员正在尝试返回自身。当您将调试器附加到垂死的 Excel 实例时,您可以看到根本问题是堆栈溢出:

EXCEL.EXE 中 0x0F06EC84 (VBE7.DLL) 处的未处理异常:0xC00000FD:堆栈溢出(参数:0x00000001、0x00212FFC)。

当您键入时,VBA 开始在默认属性上With TestCrashClass(寻找索引器Init(),因为没有任何属性。例如,考虑一个Collection. 您可以像这样使用默认属性的 ( Item) 索引器:

Dim x As Collection
Set x = New Collection
x.Add 42
Debug.Print x(1)   '<--indexed access via default member.

这完全等同于Debug.Print x.Items(1)。这是您开始遇到问题的地方。 Init()没有参数,因此 VBA 开始向下钻取默认成员以查找第一个具有索引器的成员,以便 IntelliSense 可以显示参数列表。它开始这样做:

x.[default].[default].[default].[default].[default]...

在您的情况下,它正在创建一个无限循环,因为[default] 返回 x。同样的事情发生在上面的Collection代码中(除了它找到一个):

开放括号上的 IntelliSense

加上你有一个默认实例的事实,最终结果是这样的:

Private Sub Class_Initialize()
    Class_Initialize
End Sub
于 2017-02-06T20:52:29.660 回答
9

正如@TimWilliams 指出的那样,拥有一个返回同一类实例的默认成员(或类循环ParentClass.ChildClass.ParentClass.ChildClass...,例如 ParentClass 和 ChildClass 都具有默认成员),并且在某些语法情况下使用时,例如With块,将导致VBE 尝试解析默认成员。

第一个括号使 VBE 假定必须有一个方法、索引get或数组索引将接受一个参数,因此它开始解析最终的目标成员。

所以不完整的行,光标位于括号后:

With TestCrashClass(

实际上与以下内容相同:

With TestCrashClass.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init.Init '....You're inquisitive scrolling this far over, but you get the point.

在某些时候,您的系统或 VBE 会耗尽资源并以热核小组拥抱的优雅和镇定退出。

+1 即兴使用括号对的副本/意大利面。

于 2017-02-06T20:50:33.597 回答
-4

听起来像是某种腐败。我以前曾让 Excel 表现得如此不合理,通常是在大型项目中,解决它的唯一方法是将所有类等拖到一个新项目中。

我怀疑这是因为 Excel 并没有真正删除已删除的类、模块、工作表等。您可以根据文件大小来判断这一点。

据我所知,在 Access 中没有压缩和修复功能

于 2017-02-06T19:52:04.243 回答