在 MS-Access VBA 中的 AppDomain 中设置数据后,为什么 Access 拒绝正常关闭?
After setting data in AppDomain within MS-Access VBA why does Access refuse to shutdown properly?
已编辑注释:感谢 Mathieu Guindon 让我知道了 TempVars 集合!在那个和数组的本地表之间,我将能够跳过 appdomain 的使用。
我正在使用附加代码将全局变量保存在 MS-Access 中。但是,在 Access 关闭后,它将无法正常重新打开,因为它从未完全离开任务管理器。
我可以在 VBA 内做些什么来确保它正常关闭吗?我正在使用 MS Access 2010 作为 SQL Server 2012 后端的前端。
我得出的结论是,只有当我添加代码以利用 DefaultAppDomain 来存储脚本字典时才会出现此问题,并希望我可以在域端执行一些清理代码以允许应用程序完全关闭。
Private Function GetDomainDict() As Object
Const vName = "dict-data"
Static vDict As Object
If vDict Is Nothing Then
Dim vDomain As mscorlib.AppDomain
Dim vHost As New mscoree.CorRuntimeHost
'get the vDomain from .net
vHost.Start
vHost.GetDefaultDomain vDomain
'Get the Disctionary for this app from .net or create it
If IsObject(vDomain.GetData(vName)) Then
Set vDict = vDomain.GetData(vName)
Else
Set vDict = CreateObject("Scripting.Dictionary")
vDomain.SetData vName, vDict
End If
End If
Set GetDomainDict = vDict
End Function
Public Sub StGV(vVarName As String, vVar As Variant)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists and set value either way
If pGlobals.Exists(vVarName) Then
pGlobals(vVarName) = vVar
Else
pGlobals.Add vVarName, vVar
End If
End Sub
Public Function GtGv(vVarName As String)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists return its value or null if nonexistant
If pGlobals.Exists(vVarName) Then
GtGv = pGlobals(vVarName)
Else
GtGv = Null
End If
End Function
评论前
我设计了一个 taskkill 来确保关闭访问。那就是关闭应用程序的原因。我宁愿不依赖那个。
评论后...
如果我不使用 "as new" 变量似乎不会加载并且我得到对象或变量未设置错误,但我确实添加了 set = nothing。
我实施了 .stop 并尝试在退出前端时执行卸载,但出现自动化错误 -2146234347。之后我通过单击数据库 window 关闭按钮强制关闭,但访问仍然没有关闭。
新代码
Dim vDomain As mscorlib.AppDomain
Dim vHost As New mscoree.CorRuntimeHost
'get the vDomain from .net
vHost.Start
vHost.GetDefaultDomain vDomain
'folowing line errors out automation error
vHost.UnloadDomain vDomain
vHost.Stop
Set vDomain = Nothing
Set vHost = Nothing
DoCmd.Quit
End Sub
Private Function GetDomainDict() As Object
Const vName = "dict-data"
Static vDict As Object
If vDict Is Nothing Then
Dim vDomain As mscorlib.AppDomain
Dim vHost As New mscoree.CorRuntimeHost
'get the vDomain from .net
vHost.Start
vHost.GetDefaultDomain vDomain
'Get the Disctionary for this app from .net or create it
If IsObject(vDomain.GetData(vName)) Then
Set vDict = vDomain.GetData(vName)
Else
Set vDict = CreateObject("Scripting.Dictionary")
vDomain.SetData vName, vDict
End If
vHost.Stop
Set vDomain = Nothing
Set vHost = Nothing
End If
Set GetDomainDict = vDict
End Function
Public Sub StGV(vVarName As String, vVar As Variant)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists and set value either way
If pGlobals.Exists(vVarName) Then
pGlobals(vVarName) = vVar
Else
pGlobals.Add vVarName, vVar
End If
End Sub
Public Function GtGv(vVarName As String)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists return its value or null if nonexistant
If pGlobals.Exists(vVarName) Then
GtGv = pGlobals(vVarName)
Else
GtGv = Null
End If
End Function `
据我所知,这可能是 GetDomainDict
的实现(尽管名称令人困惑):
Public Function GetDomainDict() As Object
Static dict As Object
If dict Is Nothing Then Set dict = CreateObject("Scripting.Dictionary")
Set GetDomainDict = dict
End Function
启动 .NET 应用程序域是完全不必要的步骤 - 特别是考虑到 Scripting.Dictionary
是 Microsoft Scripting Runtime COM 类型库中定义的数据类型时。 . 首先与 .NET 没有任何关系。
这里不需要涉及任何.NET。即使您想使用 System.Collections.IDictionary
,也无需创建 AppDomain 即可使用它。
也就是说...
Dim vHost As New mscoree.CorRuntimeHost
这将创建一个 auto-instantiated 对象,这意味着如果您这样做:
Set vHost = Nothing
对象引用将设置为 Nothing
并且应用 应该 正确关闭。除非你再引用它:
Debug.Print vHost Is Nothing ' will always be False
避免As New
,尤其是您不拥有的对象。
就是说,ICorRuntimeHost
接口有一个 Stop
方法可能应该在某个时候调用,还有一个 UnloadDomain
方法应该用于卸载 AppDomain 对象- 假设有一个实际的理由来生成并且 Start
它首先出现。
只是为了提供一种替代方法,使其保持纯粹 VBA 并避免对 .NET 的新依赖,您可以考虑使用 Access 2007 中引入的 TempVars
集合。这将在 VBIDE 中继续存在重启。请注意,它只能存储原始数据类型(例如字符串、数字、日期,但不能存储对象)并且它是全局可用的。
您也可以考虑使用本地 Access table 作为数据存储,然后 read/write 改为使用它,这也可以在重置后继续存在,也可以在重新启动后继续存在,这可能是可取的或不可取的,取决于您需要变量的用途。如果您希望它在重启后无法存活,只需清除 table 即可。
我喜欢使用 AppDomain 提供 in-memory 数据存储的想法,它不会在重启后继续存在,但会在重置后但确实感觉 heavy-handed,特别是如果这是唯一的用途.NET CLR 你有。我也有点担心评论:
The reason I use this is that any untrapped error in this causes all global variables to clear in access vba.
这不是正常情况。通常,未捕获的错误会显示 VBIDE 错误消息,您可以在其中调试或结束。需要注意的是,点击End
按钮是重置状态,就像点击停止按钮或执行Stop
语句一样。对于开发,使用调试按钮并正常退出程序(例如,将黄色箭头移至退出)将避免状态重置。对于用户的体验,他们永远不会看到这样的 "reset",除非在运行时模式下使用 ACCDE 或 运行 Access,这意味着没有 VBIDE 可用于调试,因此唯一有意义的事情很好,结束事情并退出。在那种情况下,这意味着错误 should 首先被捕获。
如果错误处理是一个足够大的问题,您可能需要考虑使用商业 third-party add-in,例如 vbWatchDog,这对改善错误 UX 有很大帮助,尤其是对于ACCDE 或运行时环境。
IOW,我有点担心 AppDomain 的想法正在解决 developer-only 问题。
已编辑注释:感谢 Mathieu Guindon 让我知道了 TempVars 集合!在那个和数组的本地表之间,我将能够跳过 appdomain 的使用。
我正在使用附加代码将全局变量保存在 MS-Access 中。但是,在 Access 关闭后,它将无法正常重新打开,因为它从未完全离开任务管理器。
我可以在 VBA 内做些什么来确保它正常关闭吗?我正在使用 MS Access 2010 作为 SQL Server 2012 后端的前端。
我得出的结论是,只有当我添加代码以利用 DefaultAppDomain 来存储脚本字典时才会出现此问题,并希望我可以在域端执行一些清理代码以允许应用程序完全关闭。
Private Function GetDomainDict() As Object
Const vName = "dict-data"
Static vDict As Object
If vDict Is Nothing Then
Dim vDomain As mscorlib.AppDomain
Dim vHost As New mscoree.CorRuntimeHost
'get the vDomain from .net
vHost.Start
vHost.GetDefaultDomain vDomain
'Get the Disctionary for this app from .net or create it
If IsObject(vDomain.GetData(vName)) Then
Set vDict = vDomain.GetData(vName)
Else
Set vDict = CreateObject("Scripting.Dictionary")
vDomain.SetData vName, vDict
End If
End If
Set GetDomainDict = vDict
End Function
Public Sub StGV(vVarName As String, vVar As Variant)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists and set value either way
If pGlobals.Exists(vVarName) Then
pGlobals(vVarName) = vVar
Else
pGlobals.Add vVarName, vVar
End If
End Sub
Public Function GtGv(vVarName As String)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists return its value or null if nonexistant
If pGlobals.Exists(vVarName) Then
GtGv = pGlobals(vVarName)
Else
GtGv = Null
End If
End Function
评论前
我设计了一个 taskkill 来确保关闭访问。那就是关闭应用程序的原因。我宁愿不依赖那个。
评论后...
如果我不使用 "as new" 变量似乎不会加载并且我得到对象或变量未设置错误,但我确实添加了 set = nothing。 我实施了 .stop 并尝试在退出前端时执行卸载,但出现自动化错误 -2146234347。之后我通过单击数据库 window 关闭按钮强制关闭,但访问仍然没有关闭。 新代码
Dim vDomain As mscorlib.AppDomain
Dim vHost As New mscoree.CorRuntimeHost
'get the vDomain from .net
vHost.Start
vHost.GetDefaultDomain vDomain
'folowing line errors out automation error
vHost.UnloadDomain vDomain
vHost.Stop
Set vDomain = Nothing
Set vHost = Nothing
DoCmd.Quit
End Sub
Private Function GetDomainDict() As Object
Const vName = "dict-data"
Static vDict As Object
If vDict Is Nothing Then
Dim vDomain As mscorlib.AppDomain
Dim vHost As New mscoree.CorRuntimeHost
'get the vDomain from .net
vHost.Start
vHost.GetDefaultDomain vDomain
'Get the Disctionary for this app from .net or create it
If IsObject(vDomain.GetData(vName)) Then
Set vDict = vDomain.GetData(vName)
Else
Set vDict = CreateObject("Scripting.Dictionary")
vDomain.SetData vName, vDict
End If
vHost.Stop
Set vDomain = Nothing
Set vHost = Nothing
End If
Set GetDomainDict = vDict
End Function
Public Sub StGV(vVarName As String, vVar As Variant)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists and set value either way
If pGlobals.Exists(vVarName) Then
pGlobals(vVarName) = vVar
Else
pGlobals.Add vVarName, vVar
End If
End Sub
Public Function GtGv(vVarName As String)
'Set Global Variable Dictionary to the dictionary saved in .net for this Application
Set pGlobals = GetDomainDict()
'Check if variable exists return its value or null if nonexistant
If pGlobals.Exists(vVarName) Then
GtGv = pGlobals(vVarName)
Else
GtGv = Null
End If
End Function `
据我所知,这可能是 GetDomainDict
的实现(尽管名称令人困惑):
Public Function GetDomainDict() As Object
Static dict As Object
If dict Is Nothing Then Set dict = CreateObject("Scripting.Dictionary")
Set GetDomainDict = dict
End Function
启动 .NET 应用程序域是完全不必要的步骤 - 特别是考虑到 Scripting.Dictionary
是 Microsoft Scripting Runtime COM 类型库中定义的数据类型时。 . 首先与 .NET 没有任何关系。
这里不需要涉及任何.NET。即使您想使用 System.Collections.IDictionary
,也无需创建 AppDomain 即可使用它。
也就是说...
Dim vHost As New mscoree.CorRuntimeHost
这将创建一个 auto-instantiated 对象,这意味着如果您这样做:
Set vHost = Nothing
对象引用将设置为 Nothing
并且应用 应该 正确关闭。除非你再引用它:
Debug.Print vHost Is Nothing ' will always be False
避免As New
,尤其是您不拥有的对象。
就是说,ICorRuntimeHost
接口有一个 Stop
方法可能应该在某个时候调用,还有一个 UnloadDomain
方法应该用于卸载 AppDomain 对象- 假设有一个实际的理由来生成并且 Start
它首先出现。
只是为了提供一种替代方法,使其保持纯粹 VBA 并避免对 .NET 的新依赖,您可以考虑使用 Access 2007 中引入的 TempVars
集合。这将在 VBIDE 中继续存在重启。请注意,它只能存储原始数据类型(例如字符串、数字、日期,但不能存储对象)并且它是全局可用的。
您也可以考虑使用本地 Access table 作为数据存储,然后 read/write 改为使用它,这也可以在重置后继续存在,也可以在重新启动后继续存在,这可能是可取的或不可取的,取决于您需要变量的用途。如果您希望它在重启后无法存活,只需清除 table 即可。
我喜欢使用 AppDomain 提供 in-memory 数据存储的想法,它不会在重启后继续存在,但会在重置后但确实感觉 heavy-handed,特别是如果这是唯一的用途.NET CLR 你有。我也有点担心评论:
The reason I use this is that any untrapped error in this causes all global variables to clear in access vba.
这不是正常情况。通常,未捕获的错误会显示 VBIDE 错误消息,您可以在其中调试或结束。需要注意的是,点击End
按钮是重置状态,就像点击停止按钮或执行Stop
语句一样。对于开发,使用调试按钮并正常退出程序(例如,将黄色箭头移至退出)将避免状态重置。对于用户的体验,他们永远不会看到这样的 "reset",除非在运行时模式下使用 ACCDE 或 运行 Access,这意味着没有 VBIDE 可用于调试,因此唯一有意义的事情很好,结束事情并退出。在那种情况下,这意味着错误 should 首先被捕获。
如果错误处理是一个足够大的问题,您可能需要考虑使用商业 third-party add-in,例如 vbWatchDog,这对改善错误 UX 有很大帮助,尤其是对于ACCDE 或运行时环境。
IOW,我有点担心 AppDomain 的想法正在解决 developer-only 问题。