После установки данных в AppDomain в MS-Access VBA, почему Access отказывается корректно завершать работу? - PullRequest
0 голосов
/ 08 июля 2019

Отредактированное примечание: Спасибо Матье Гиндону за то, что он ознакомил меня с коллекцией TempVars!Между этим и локальными таблицами для массивов я смогу пропустить использование домена приложения.

Я использую прикрепленный код для хранения глобальных переменных в MS-Access.Тем не менее, после закрытия доступа он не будет вновь открыт должным образом, потому что он никогда полностью не покинул диспетчер задач.

Есть ли что-то, что я могу сделать в VBA, чтобы убедиться, что он отключается должным образом?Я использую MS Access 2010 в качестве внешнего интерфейса для SQL Server 2012 Backend.

Я пришел к выводу, что эта проблема возникает только тогда, когда я добавил код, чтобы использовать 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

Перед комментариями

Я разработал задачу, чтобы обеспечить закрытие доступа.Это было то, что закрывало приложение.Я бы предпочел не полагаться на это.

После комментариев ...

Если я не использую «как новый», переменные, похоже, не загружаются, и я получаю объект с ошибкой или переменная не установлена, но я добавил набор= ничегоЯ реализовал .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 ]

1 голос
/ 09 июля 2019

Просто для того, чтобы предложить альтернативу, позволяющую сохранить его чисто VBA и избежать новой зависимости от .NET, вы можете рассмотреть возможность использования коллекции TempVars, которая была представлена ​​в Access 2007. Это переживет сброс VBIDE. Обратите внимание, что он может хранить только примитивные типы данных (например, строки, числа, даты, но не объекты), и он доступен глобально.

Вы также можете рассмотреть возможность использования локальной таблицы доступа в качестве хранилища данных и чтения / записи в нее вместо этого, что также выдержит перезагрузку и также выдержит перезагрузку, что может быть желательным или нет, в зависимости от того, какие переменные вам нужны за. В случае, если вы предпочитаете, чтобы он не пережил перезапуски, достаточно просто очистить стол.

Мне нравится идея использования AppDomain для предоставления хранилища данных в памяти, которое не переживет перезапуск, но выполнит перезагрузку, но при этом выглядит неуклюжим, особенно если это единственное использование .NET CLR, которое у вас есть. Я также немного обеспокоен комментарием:

Причина, по которой я это использую, заключается в том, что любая неисследованная ошибка приводит к сбросу всех глобальных переменных в access vba.

Это не нормальное состояние дел. Обычно неперехваченная ошибка вызывает сообщение об ошибке VBIDE, где вы можете отладить или завершить. Следует отметить, что нажатие кнопки End сбрасывает состояние так же, как нажатие кнопки «Стоп» или выполнение оператора Stop. Для разработки, использование кнопки «Отладка» и обычный выход из процедуры (например, путем перемещения желтой стрелки к выходу) позволит избежать сброса состояния. По опыту пользователей, они никогда не увидят такой «сброс», за исключением случая использования ACCDE или запуска Access в режиме выполнения, что означает, что VBIDE недоступен для отладки, поэтому единственное значимое, что нужно сделать, это хорошо, закончить и выход. В этом случае это означает, что ошибка должна быть поймана в ловушку.

Если обработка ошибок является достаточно большой проблемой, вы можете рассмотреть возможность использования коммерческой сторонней надстройки, такой как vbWatchDog , которая чрезвычайно помогает в улучшении UX ошибок, особенно для ACCDE или среды выполнения. окружающая среда.

Итак, я немного обеспокоен тем, что идея AppDomain решает проблему только для разработчиков.

1 голос
/ 08 июля 2019

Насколько я могу судить, это может быть реализация для 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 в первую очередь.

...