在MS-Access VBA的AppDomain中设置数据后,为什么Access拒绝正确关闭?

时间:2019-07-08 16:33:45

标签: vba ms-access appdomain

编辑注:感谢Mathieu Guindon使我意识到TempVars系列!在那和数组的本地表之间,我将能够跳过appdomain的使用。

我正在使用所附的代码将全局变量保留在MS-Access中。但是,在Access关闭后,由于它从未完全离开任务管理器,因此无法正确重新打开。

在VBA中是否可以做一些事情以确保其正常关闭?我将MS Access 2010用作SQL Server 2012后端的前端。

我得出的结论是,仅当我添加了代码以使用DefaultAppDomain存储脚本字典时才出现此问题,并希望可以在域侧执行一些清理代码以允许App完全关闭。

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 = none。 我实现了.stop,并尝试在退出前端时进行卸载,但出现了自动化错误-2146234347。之后,我通过单击数据库窗口关闭按钮来强制关闭,但是访问仍然无法关闭。 新密码


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 `


2 个答案:

答案 0 :(得分:1)

据我所知,这可能是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

这将创建一个自动实例化的对象,这意味着如果您这样做:

Set vHost = Nothing

对象引用将设置为Nothing,应用程序应该正确关闭。除非您再次引用它,否则:

Debug.Print vHost Is Nothing ' will always be False

避免使用As New,尤其是对于您不拥有的对象,尤其是。

也就是说,ICorRuntimeHost接口有一个Stop方法,应该在某个时候调用它,还有一个UnloadDomain方法应该用于卸载AppDomain对象-假设首先有一个真正的原因产生并Start

答案 1 :(得分:1)

仅提供一种替代方法,使其保持纯VBA格式,并避免对.NET的新依赖性,您可以考虑使用Access 2007中引入的TempVars集合。这将在VBIDE重置后继续有效。请注意,它只能存储原始数据类型(例如字符串,数字,日期,但不能存储对象),并且它在全球范围内可用。

您还可以考虑将本地Access表用作数据存储并对其进行读/写操作,这也将在重置后仍然存在,并且在重启后也可能存在,这取决于是否需要变量,这可能是合乎需要的对于。如果您希望它在重新启动后无法幸免,那么只需清除表就足够了。

我喜欢使用AppDomain提供内存中的数据存储的想法,该存储将无法在重新启动后继续运行,但会在重置后出现,但是确实感觉很费力,特别是如果那是您唯一使用的.NET CLR。我对评论也有些担心:

  

我使用此命令的原因是,任何未捕获的错误都会导致在Access vba中清除所有全局变量。

那不是正常的婚外情。通常,未捕获的错误将显示VBIDE错误消息,您可以在其中调试或结束。应该注意的是,单击End按钮将重置状态,就像单击“停止”按钮或执行Stop语句一样。为了进行开发,使用“调试”按钮并正常退出过程(例如,通过将黄色箭头移至出口)将避免重置状态。根据用户的经验,除非使用ACCDE或在运行时模式下运行Access,否则他们永远不会看到这种“重置”,这意味着没有可用的VBIDE进行调试,因此唯一有意义的事情就是做好一切然后退出。在这种情况下,这意味着错误应该首先被捕获。

如果错误处理是一个足够大的问题,则您可能需要考虑使用诸如vbWatchDog之类的商业第三方加载项,这对改善错误UX有很大帮助,尤其是对于ACCDE或运行时环境。

IOW,我有点担心AppDomain的想法正在解决开发人员专用的问题。

相关问题