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