您问题的第 2 部分非常有趣,非常值得扩展答案。
这将涵盖三个关键点:
- 对象和对象变量;
- 解雇对象时的陷阱;
- ...以及在 Excel 2013 中对 Application 对象进行引用计数的一个重要变化。
但是,如果您想要一个简短的答案,那就是:
“并非所有对象都是平等的”。
现在继续阅读...
有些对象是在您的 Excel 会话的“自己的”内存空间中创建的,它们的内存分配由您的会话控制;一些对象具有在对象变量被解除后存在的持久组件;有些没有:
Set oDict = CreateObject("Scripting.Dictionary")
Set oWShell = CreateObject("Shell.Application")
在这两种情况下,都会分配内存,实例化对象变量(以及它们指向方法和属性的指针的 vTable),并且它们是你的命令,直到你关闭它们:
Set oDict = Nothing
Set oWShell = Nothing
而且,在被解雇时,他们没有留下任何痕迹。
但是这个对象是持久的:
Dim oWbk as Excel.Workbook
Set oWbk = Application.Workbooks.Add
...您已经创建了一个新的工作簿对象,如果您使用 关闭对象变量
Set oWbk = Nothing
,您将看到新的工作簿对象仍然作为可见的存在存在于用户界面中。
您实际创建的是一个 Workbook对象- 一个带有活动工作表和完整用户界面的工作簿窗口 - 以及一个 Workbook对象变量- 程序员的 COM 接口,Workbook 对象的方法和属性表 - 您可以使用命名实体“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)
) 将返回
“自动化错误”消息。
上一个答案中的一些评论提到该UserForm
对象 - 与 Dictionary 和 Shell 对象不同 - 是具有可见用户界面的对象。但是,此用户界面不是 Excel 用户界面中的持久新对象,如工作簿或工作表。
幸运的是,您创建的对象归您的 Excel 会话所有,您可以再次实例化一个对象变量,以获得相同的方法和属性框架,并再次控制该对象:
Set oWbk = Application.Workbooks(Application.Workbooks.Count)
...当然,假设您有某种方法可以确保您确定了正确的工作簿对象:但这根本不是您的问题。
这个答案的去向是:不是在 Excel 会话的“自己的”内存中创建的对象。
Set oApp = CreateObject("Excel.Application")
此语句将创建一个 Excel 对象,该对象与新的 Workbook 一样,具有用户界面(尽管您需要将
.Visible
属性设置为 True 才能看到它)和内存中的持久存在:再一次,该对象不仅仅是它的对象变量,并且关闭变量不会破坏对象。
与新的工作簿不同,它不是完全由您来指挥:它本身就是一个 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 部分”中有很多内容。