Call IModelDoc2::Access3rdPartyStorageStore extension method to access the 3rd storage store. Pass the boolean parameter to read or write storage.

Use this approach when it is required to store multiple data structures which need to be accessed and managed independently. Prefer this instead of creating multiple streams

Storage Access Handler

To simplify the handling of the storage lifecycle, use the Documents Manager API from the SwEx.AddIn framework:

using CodeStack.SwEx.AddIn;
using CodeStack.SwEx.AddIn.Attributes;
using CodeStack.SwEx.AddIn.Base;
using CodeStack.SwEx.AddIn.Core;
using CodeStack.SwEx.AddIn.Enums;
using SolidWorks.Interop.sldworks;
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Serialization;

namespace CodeStack.SwEx
{
    [AutoRegister]
    [ComVisible(true), Guid("64684CEF-131C-4F08-88F7-B3C3BAA7004E")]
    public class ThirdPartyDataAddIn : SwAddInEx
    {
        private IDocumentsHandler<DocumentHandler> m_StorageDocHandler;

        public override bool OnConnect()
        {
            m_StorageDocHandler = CreateDocumentsHandler();
            m_StorageDocHandler.HandlerCreated += OnStorageHandlerCreated;
            return true;
        }

        private void OnStorageHandlerCreated(DocumentHandler doc)
        {
            doc.Access3rdPartyData += OnAccess3rdPartyStorageStore;
        }

        private void OnAccess3rdPartyStorageStore(DocumentHandler docHandler, Access3rdPartyDataState_e state)
        {
            switch (state)
            {
                case Access3rdPartyDataState_e.StorageRead:
                    LoadFromStorageStore(docHandler.Model);
                    break;

                case Access3rdPartyDataState_e.StorageWrite:
                    SaveToStorageStore(docHandler.Model);
                    break;
            }
        }
    }
}

Reading data

IThirdPartyStoreHandler::Storage property returns null for the storage which not exists on reading.

private const string STORAGE_NAME = "CodeStackStorage";
private const string STREAM1_NAME = "CodeStackStream1";
private const string STREAM2_NAME = "CodeStackStream2";
private const string SUB_STORAGE_NAME = "CodeStackSubStorage";

public class StorageStreamData
{
    public int Prp3 { get; set; }
    public bool Prp4 { get; set; }
}

private StorageStreamData m_StorageData;

private void LoadFromStorageStore(IModelDoc2 model)
{
    using (var storageHandler = model.Access3rdPartyStorageStore(STORAGE_NAME, false))
    {
        if (storageHandler.Storage != null)
        {
            using (var str = storageHandler.Storage.TryOpenStream(STREAM1_NAME, false))
            {
                if (str != null)
                {
                    var xmlSer = new XmlSerializer(typeof(StorageStreamData));
                    m_StorageData = xmlSer.Deserialize(str) as StorageStreamData;
                }
            }

            using (var subStorage = storageHandler.Storage.TryOpenStorage(SUB_STORAGE_NAME, false))
            {
                if (subStorage != null)
                {
                    using (var str = subStorage.TryOpenStream(STREAM2_NAME, false))
                    {
                        if (str != null)
                        {
                            var buffer = new byte[str.Length];
                            str.Read(buffer, 0, buffer.Length);
                            var dateStr = Encoding.UTF8.GetString(buffer);
                            var date = DateTime.Parse(dateStr);
                        }
                    }
                }
            }
        }
    }
}

Writing data

IThirdPartyStoreHandler::Storage will always return the pointer to the storage (stream is automatically created if it doesn’t exist).

private const string STORAGE_NAME = "CodeStackStorage";
private const string STREAM1_NAME = "CodeStackStream1";
private const string STREAM2_NAME = "CodeStackStream2";
private const string SUB_STORAGE_NAME = "CodeStackSubStorage";

public class StorageStreamData
{
    public int Prp3 { get; set; }
    public bool Prp4 { get; set; }
}

private StorageStreamData m_StorageData;

private void SaveToStorageStore(IModelDoc2 model)
{
    using (var storageHandler = model.Access3rdPartyStorageStore(STORAGE_NAME, true))
    {
        using (var str = storageHandler.Storage.TryOpenStream(STREAM1_NAME, true))
        {
            var xmlSer = new XmlSerializer(typeof(StorageStreamData));

            xmlSer.Serialize(str, m_StorageData);
        }

        using (var subStorage = storageHandler.Storage.TryOpenStorage(SUB_STORAGE_NAME, true))
        {
            using (var str = subStorage.TryOpenStream(STREAM2_NAME, true))
            {
                var buffer = Encoding.UTF8.GetBytes(DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss"));
                str.Write(buffer, 0, buffer.Length);
            }
        }
    }
}

Explore the methods of IComStorage for information of how to create sub streams or sub storages and enumerate the existing elements.