In Excel VBA how to persist key variables over a 'state loss' (without writing to a cell or a file)?

后端 未结 2 628
长情又很酷
长情又很酷 2020-12-06 08:17

Excel VBA is a flexible development environment. It is pesudo-compiled. However, sometimes during development a \"state loss\" can occur. A \"state loss\" is when all var

2条回答
  •  不知归路
    2020-12-06 08:48

    Attempting answer of my own question ...

    The solution is to have a simple container, I choose Scripting.Dictionary, compiled into a DLL and make accessible to VBA using COM. In the old days one could have used VB6.

    These days, one can also use C++ but here I present a C# solution (uses COM interop).

    using System.Runtime.InteropServices;
    
    namespace VBAStateLossProofStorageLib
    {
        // Code curated by S Meaden from Microsoft documentation
    
        // 1. C# Shared Class library
        // 2. In AssemblyInfo.cs set ComVisible(true)
        // 3. In Project Properties->Build check 'Register for Interop'
        // 4. Add Com reference to Microsoft Scripting Runtime
    
        public interface IVBAStateLossProofStorage
        {
            Scripting.Dictionary getGlobalDictionary();
        }
    
        [ClassInterface(ClassInterfaceType.None)]
        [ComDefaultInterface(typeof(IVBAStateLossProofStorage))]
        public class VBAStateLossProofStorage : IVBAStateLossProofStorage
        {
            public Scripting.Dictionary getGlobalDictionary()
            { return CVBAStateLossProofStorage.m_dictionary; }
        }
    
    
        // https://msdn.microsoft.com/en-gb/library/79b3xss3.aspx
        // "a static class remains in memory for the lifetime of the application domain in which your program resides. "
        [ComVisible(false)]
        static class CVBAStateLossProofStorage
        {
            public static Scripting.Dictionary m_dictionary;
    
            static CVBAStateLossProofStorage()
            {
                m_dictionary = new Scripting.Dictionary();
            }
        }
    }
    

    And here is some client VBA code to demonstrate. Requires a Tools->Reference to the type library (.tlb file) created alongside the Dll.

    Option Explicit
    
    Public gdicLossy As New Scripting.Dictionary
    Public gdicPermanent As Scripting.Dictionary
    
    Sub RunFirst()
    
        Set gdicLossy = New Scripting.Dictionary
        gdicLossy.add "Greeting", "Hello world!"
    
        Dim o As VBAStateLossProofStorageLib.VBAStateLossProofStorage
        Set o = New VBAStateLossProofStorageLib.VBAStateLossProofStorage
    
        Set gdicPermanent = o.getGlobalDictionary
        gdicPermanent.RemoveAll '* clears it down
        gdicPermanent.add "Greeting", "Bonjour!"
    
        End '* THIS PROVOKES A STATE LOSS - ALL VARIABLES ARE TORN DOWN - EVENT HANDLERS DISAPPEAR
    End Sub
    
    Sub RunSecond()
    
        Debug.Assert gdicLossy.Count = 0  '* sadly we have lost "Hello world!" forever
    
        Dim o As VBAStateLossProofStorageLib.VBAStateLossProofStorage
        Set o = New VBAStateLossProofStorageLib.VBAStateLossProofStorage
    
        Set gdicPermanent = o.getGlobalDictionary
        Debug.Assert gdicPermanent.Count = 1 '* Happily we have retained "Bonjour!" as it was safe in its compiled Dll
        Debug.Assert gdicPermanent.Item("Greeting") = "Bonjour!"
    
    End Sub
    

提交回复
热议问题