您问题的第 2 部分非常有趣,非常值得扩展答案。
这将涵盖三个关键点:
- 对象和对象变量;
- 解除对象时的陷阱;
- ...在 Excel 2013 中对 Application 对象进行引用计数方面的一项重要变化。
但是,如果您想要一个简短的答案,那就是:
“并非所有对象都是平等的”。
现在继续阅读...
有些对象是在您的 Excel 会话“自己的”内存空间中创建的,它们的内存分配由您的会话控制;一些对象具有在对象变量被解除后存在的持久组件;有些没有:
Set oDict = CreateObject("Scripting.Dictionary")
Set oWShell = CreateObject("Shell.Application")
在这两种情况下,都会分配内存,实例化对象变量(以及它们指向方法和属性的指针的 vTable),在您解除它们之前,它们都是您的命令:
Set oDict = Nothing
Set oWShell = Nothing
而且,在解雇时,他们没有留下任何痕迹。
但是 this 对象是持久的:
Dim oWbk as Excel.Workbook
Set oWbk = Application.Workbooks.Add
...您已经创建了一个新的工作簿对象,如果您使用 Set oWbk = Nothing 关闭对象变量,您将看到新的工作簿对象仍然作为可见的存在于用户界面中。
您实际创建的是一个 Workbook object - 一个带有活动工作表和与之配套的完整用户界面的工作簿窗口 - 以及一个 Workbook object variable - 一个程序员的 COM 接口,工作簿对象的方法和属性表 - 您可以使用命名实体 'oWbk' 在代码中对其进行操作。
取消 oWbk 对象变量会删除该框架,但 Workbook 本身仍然存在:您已经创建了一个 Workbook 对象,它是您的。
对象不仅仅是它的对象变量,解除变量并不会破坏对象:它只是解除了一个接口,一个方法和属性的框架,您可以使用它在代码中操作对象.
关闭工作簿,无论是否保存文件,都应该自动关闭对象变量并清除为该接口分配的属性、方法和属性的内存:
'try this:
oWbk.Close SaveChanges:=False
' or maybe this:
Application.Workbooks(Application.Workbooks.Count).Close SaveChanges:=False
...也就是说,您希望这两个命令都调用 Set oWbk= Nothing - 特别是 oWbk.Close 命令 - 但是如果您尝试其中任何一个而不显式关闭 oWbk,您会发现 oWbk 仍然存在为一个空壳,所有关于它的信息调用和请求 (try>Debug.Print> TypeName(oWbk)) 都将返回 'Automation Error' 消息。
上一个答案中的一些评论提到 UserForm 对象 - 与 Dictionary 和 Shell 对象不同 - 是具有可见用户界面的对象。但这个用户界面不是 Excel 用户界面中的持久新对象,如工作簿或工作表。
幸运的是,您创建的对象归您的 Excel 会话所有,您可以再次实例化一个对象变量,以获得相同的方法和属性框架,并再次控制该对象:
Set oWbk = Application.Workbooks(Application.Workbooks.Count)
...当然,假设您有某种方法可以确保您确定了正确的工作簿对象:但这根本不是您的问题。
这个答案的意义在于:不是在 Excel 会话的“自己”内存中创建的对象。
Set oApp = CreateObject("Excel.Application")
此语句将创建一个 Excel 对象,该对象与新的 Workbook 一样,具有用户界面(尽管您需要将 .Visible 属性设置为 True 才能看到它)和内存中的持久存在:再一次,该对象是超过它的对象变量,并且关闭变量不会破坏对象。
与新的 Workbook 不同,它完全由你来指挥:它本身就是一个 Excel 会话,它分配自己的内存 - oApp 在当前会话内存中的“足迹”只是指针和名称:接口(vTable、iDispatch 和所有具有指向在 VBA 中操作 Excel 会话的神秘行为的结构的命名方法)存在于这个新 Excel 会话分配的内存块中。
以下是 Office 2010 和旧版 Excel 中发生的情况:
使用Set oApp = Nothing 取消对象变量会使该会话启动并运行,我强烈建议您使会话可见,以便您可以手动关闭它!
手动关闭该 Excel 会话,而不显式关闭 oApp 对象变量,肯定会使 oApp 处于“空壳”状态,一个冷酷无头的幽灵在哀号 '自动化对象已与客户断开连接!' 在代码库的阴暗角落。
但是,在 Office 2013 及更高版本中,Set oApp = Nothing 完全执行您所期望的引用计数,并且会话关闭。尝试一下:
Private Sub Test()
Dim oApp As Excel.Application
Set oApp = New Excel.Application
'Set oApp = CreateObject("Excel.Application")
oApp.Visible = True
Set oApp = Nothing
End Sub
如果另一个对象变量有引用,它不会在Set oApp = Nothing 上关闭 - 这不是唯一增加引用计数器的实体:GUI 中的用户活动(尝试创建一个新工作簿并对其进行编辑)使会话保持正常和跑步。
为了您自己的乐趣,看看oApp.Quit 是否真的关闭了oApp 并将其设置为Nothing。
当然,oApp.Quit 肯定会关闭会话...
...或者会吗?如果该会话中发生了一些事情 - 长时间计算或“模态”错误消息,您必须在应用程序对象响应任何其他输入之前从用户界面或您的 VBA - 然后 oApp.Quit 不会关闭会话。
我们不要去那里。在所有条件相同的情况下,oApp.Quit 肯定会在 2010 年及更早版本的 Excel 中关闭会话。
但在 Office 2013 中,从最后一个对象变量调用“退出”只会隐藏 UI:对象变量仍会响应您的代码 - 不需要活动工作簿的方法和属性仍可通过 oApp 访问 - 并且Excel.exe 的单独实例在任务管理器的进程选项卡中清晰可见。
同样,通过单击用户界面中的“关闭”按钮退出新会话会关闭会话的窗口,但是,如果您的代码中有一个引用此应用程序对象的对象变量,它仍然存在,在内存中,并且'oApp' 仍然可以获取属性和方法。
因此,引用计数器在当前版本的 Excel 中以两种方式工作:对象存在直到引用计数减少到零,并且最后一个剩余的对象变量将不会通过退出“断开”命令或 UI 操作。
尽管如此,您的会话并不“拥有”那个新的应用程序对象:如果您已经解除了最后一个对象变量并将其设置为 Nothing,并且还有其他东西使 neww 会话保持活动状态 - 用户活动,或者一些内部流程 - 没有什么像 Application.Workbooks() 或 Worksheets() 集合来识别其他 Excel 会话并实例化指向 Excel.Application 对象的特定实例的对象变量。
有一些方法可以使用 API 调用来获取特定会话,但它们并不像您希望的那样可靠。
...所以,总而言之,“第 2 部分”有很多内容。