Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
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.
Live Selection
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.
FlushAll
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.
SplineData
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 ...