Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    BaseContainer FlushAll crashes

    Cinema 4D SDK
    r19 c++
    2
    3
    688
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • C4DSC
      C4DS
      last edited by

      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;
      }
      
      
      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by m_adam

        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.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        1 Reply Last reply Reply Quote 2
        • C4DSC
          C4DS
          last edited by

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

          1 Reply Last reply Reply Quote 0
          • First post
            Last post