Solved BaseContainer FlushAll crashes

In continuation from the disussion concerning ToolData container, I am encountering an issue I cannot seem to put my finger on. So I extracted the offending code into a separate dummy plugin.

The whole code is below.
A command data when executed will parse the Live Selection description and store all settings into a basecontainer, which is located in a separate class.

When Cinema 4D is then closed the class hosting the basecontainer is destroyed, which calls the basecontainer's destructor, which will perform a FlushAll.
For one reason or another this call crashes.

I have pinpointed the issue to the SplineData which is part of the soft-selection settings of the Live Tool (while not actively shown this attribute is still part of the description).
This SplineData is a custom data type, and when I omit copying this attribute into the test basecontainer all goes well when class is destroyed.

To me, it seems as if copying the data into the container does not make a copy, but uses the original pointer. Hence, when the test class and its test basecontainer is destroyed it will try to destroy the SplineData ... which has already been destroyed as a result of Cinema 4D closing (... at least, that's my guess).

If I store the settings into a basecontainer of the command data then there seem to be no problem.
Which does make me think I am doing something wrong with my test class allocation/deallocation.
I just cannot seem to see what exactly it is I am doing wrong.

Tried with R16, R17, ...

// ========================
// Testing
// Dummy "empty" plugin
// ========================

#include "c4d.h"

// Dummy IDs - for demonstration purposes only
#define MYCOMMAND_PLUGIN_ID	1999999

class Test
{
public:
	Test() {}
	virtual ~Test() {}

	BaseContainer mTestContainer;
};

Test* gTest = nullptr;

// ====================================
// CommandData
// ====================================

class MyCommand : public CommandData
{
	INSTANCEOF(MyCommand, CommandData)

public:
	virtual Bool Execute(BaseDocument* doc);
	virtual Bool ExecuteSubID(BaseDocument* doc, Int32 subid);
	virtual Int32 GetState(BaseDocument* doc);
	virtual Bool RestoreLayout(void* secret);
	virtual Bool Message(Int32 type, void* data);

//	BaseContainer	mToolData;
};

Bool MyCommand::Execute(BaseDocument* doc)
{
	// get the live selection description
	const Int32 id = ID_MODELING_LIVESELECTION;
	BasePlugin* plug = FindPlugin(id, PLUGINTYPE_TOOL); // needs explicitely to be PLUGINTYPE_TOOL, as PLUGINTYPE_ANY does not work correctly
	if (!plug)
		return FALSE;

	AutoAlloc<Description> desc;
	if (desc && plug->GetDescription(desc, DESCFLAGS_DESC_0))
	{
		void *handle = desc->BrowseInit();

		DescID dcid, groupid;
		GeData gd;
		const BaseContainer *objBc = NULL;
		while (desc->GetNext(handle, &objBc, dcid, groupid))
		{
			if (objBc) {
				const String name = objBc->GetString(DESC_NAME);
				const Int32 id = dcid[0].id;

				plug->GetParameter(DescID(dcid), gd, DESCFLAGS_GET_0);
				const Int32 type = gd.GetType();
				// attributes can be buttons, bars, ...
				// anything without a user input value, skip these
				if (type == DA_NIL)
					continue;
				
				//mToolData.SetData(id, gd);
				gTest->mTestContainer.SetData(id, gd);
			}
		}
		desc->BrowseFree(handle);   //Free the memory used by the Browse function  
	}

	return TRUE;
}

Bool MyCommand::ExecuteSubID(BaseDocument* doc, Int32 subid)
{
	return TRUE;
}


Int32 MyCommand::GetState(BaseDocument* doc)
{
	return CMD_ENABLED;
}

Bool MyCommand::RestoreLayout(void* secret)
{
	return TRUE;
}

Bool MyCommand::Message(Int32 type, void* data)
{
	return SUPER::Message(type, data);
}

Bool RegisterMyCommand(void)
{
	return RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Testing", 0, AutoBitmap("icon.png"), "Test", NewObjClear(MyCommand));
}

// ====================================
// Plugin Main 
// ====================================
Bool PluginStart(void)
{
	RegisterMyCommand();

	gTest = NewObjClear(Test);
	if (!gTest)
		return FALSE;

	return TRUE;
}
void PluginEnd(void) 
{
	if (gTest)
	{
		gTest->mTestContainer.FlushAll(); // This line was added for testing out the flushing -> it crashes ... why?
		// without the above line, the destructor of the Test class would call BaseContainer's destructor, which would perform a FlushAll -> this crashes

		DeleteObj(gTest);
	}
}
Bool PluginMessage(Int32 id, void * data)
{
	switch (id) {
	case C4DPL_INIT_SYS:
		if (!resource.Init())
			return FALSE;
		return TRUE;
	case C4DMSG_PRIORITY:
		return TRUE;
	case C4DPL_BUILDMENU:
		break;
	case C4DPL_ENDACTIVITY:
		return TRUE;
	}
	return FALSE;
}

Hi @C4DS

You should free your stuff in C4DPL_ENDACTIVITY.

Documentation of PluginEnd will be improved to point to C4DPL_ENDACTIVITY, in any case, see Plugin Functions Manual.

Cheers,
Maxime.

Seems I have been doing it wrong for all those years.
Thanks for leading me onto the right path ...