Stability and Testing

Debugging

Set the command line parameter g_alloc=debug to find memory leaks. For more information please see Debugging Plugins.

Memory Statistics Dialog

Use the Cinema 4D SDK Memory Statistics Dialog to check for realtime useage of memory.

memory_statistics.png

Constants

Always use constants to avoid compatibility problems.

Wrong example:

BaseContainer* bc = obj->GetDataInstance();
if (!bc) goto error;
bc->SetInt32(1001, 1.0);

Correct example:

#include "../../advanced render/res/description/Xsss.h"
BaseContainer* bc = obj->GetDataInstance();
if (!bc) goto error;

Global Static Classes

Do not use global static classes.

Not allowed:

AutoAlloc<String> my_global_string1;
String my_global_string2;
Filename my_global_filename;

Elementary data types (Int32, Float, Char, GE_SPINLOCK, etc.) are allowed.

Cleanup

Double-check your cleanup code.

Low-Level cleanup:

  • Strings, Filenames etc.
  • unloading of imported DLLs
Note
Be careful with BaseContainers that contain CustomDataTypes.
void PluginEnd(void)
{
BaseBitmap::Free(g_pNoisePreview);
DeleteObj(g_pbMenuContainer);
Semaphore::Free(g_pbakeSemaphore);
g_NoiseContainer.FreeContainers();
FreeNoisePreviews();
}

High-Level cleanup:

  • Dialogs, BaseContainers, DataTypes etc.
  • Finishing of all disk activity, preferences storage etc.
  • Terminating of any running tasks!
Bool PluginMessage(Int32 id, void* data)
{
switch (id)
{
return true;
FreeBaker();
return true;
}
return false;
}

Dangerous Hooks

Most dangerous places for hooks are:

All of those can greatly affect the application's stability and lead to data loss for the end user!

Attention
Be extremely careful, use defensive coding style and take the time to properly test when working on those areas.

Virtual Function Calls

This Virtual Function Call will crash:

PluginVideoPost* node;
for (node = renderdata->GetFirstVideoPost(); node; node=node->GetNext())
{
VideoPostData* pVPData = (VideoPostData*)node->GetNodeData();
Int32 info = pVPData->GetRenderInfo();
}
  • Beware of calling NodeData members!
  • pVPData can be nullptr, check necessary
  • A plugin is not allowed to use the virtual function table of another plugin! This will only work with the same plugin, not over plugin boundaries

To avoid crashes the call above must be replaced by:

PluginVideoPost* node;
for (node = renderdata->GetFirstVideoPost(); node; node=node->GetNext())
{
VideoPostData* pVPData = (VideoPostData*)node->GetNodeData();
if (!pVPData) continue;
Int32 info = ((pVPData->*((VIDEOPOSTPLUGIN*)C4DOS.Bl->RetrieveTableX(pVPData,0))->GetRenderInfo)(node);
}

Threading

Please also see Important Threading Information.

The following code will crash as the viewport thread creates the cache while the coordinate manager retrieves the dimensions, accessing the cache that is being written to at the same time.

void MyObjectData::GetDimension(BaseObject* op, Vector* mp, Vector* rad)
{
BaseObject* cop = op->GetCache();
if (!op) return;
*mp = cop->GetMp();
*rad = cop->GetRad();
}

Preventing other threads from working

  • Any modal dialog calls StopAllThreads() ahead of time (but redraw can be started in the background again, this is important to know for helper threads)
  • Call StopAllThreads() before any operation in non-modal dialogs (e.g. if the user clicks on a button)

Current State To Object (CSTO)

CSTO has to be executed on a duplicate of an object because CSTO modifies the existing caches. In some cases you have to use a temporary document as well (ObjectData::GetVirtualObjects()).

Cinema 4D and OS Code

  • Include OS headers before Cinema 4D headers
  • Keep code with shared OS and Cinema 4D calls as small as possible and gathered in one place (better portability and less datatype conflicts).

Miscellaneous

  • Any API function may fail! Check for everything, even if it seems obvious.
  • Most prominent: NewMem(), DeleteMem()
  • Any division needs to be checked for 0.0
  • SSE2 changes runtime behaviour: both examples will eventually crash!

Crash Example 1:

Float q, v = oldvalue;
if (v != 0.0) q = 1.0 / Sqrt(v);

Crash Example 2:

Float q = v;
if (v != 0.0) q = 1.0 / q;