Archicad C++ API
About Archicad add-on development using the C++ API.

Modify an existing Library part's section destroys Libpart

gehairing
Participant
Hello all

I try to modifiy an existing Library part by adding some parameters.

Here is a part of the code i use :
API_LibPart libPart;
			BNZeroMemory (&libPart, sizeof (API_LibPart));

			//TODO : Get type of libpart ?
			libPart.typeID   = APILib_ObjectID;
			libPart.location = &destinationFile;
			
			// Register the copied library part file
			err = ACAPI_LibPart_Register(&libPart);
			if(err == NoError)
			{
				// Get the library part
				err = ACAPI_LibPart_Get(&libPart);
				if(err == NoError)
				{

					// GS Doc says to free the location...
					if (libPart.location != NULL) 
					{
						delete libPart.location;
						libPart.location = NULL;
					}
					
					// Add parameters/properties for this object
					API_LibPartSection section;
					BNZeroMemory (&section, sizeof (API_LibPartSection));
					section.sectType = API_SectParamDef;

					GSHandle sectionHdl = NULL;
					err = ACAPI_LibPart_GetSection(libPart.index, &section, &sectionHdl, NULL);

					short nPars = 3;
					API_AddParType** addPars = reinterpret_cast<API_AddParType**>(BMAllocateHandle (nPars * sizeof (API_AddParType), ALLOCATE_CLEAR, 0));
					if (addPars != NULL) 
					{
						API_AddParType* pAddPar = &(*addPars)[0];
						pAddPar->typeID = APIParT_Mater;
						pAddPar->typeMod = 0;
						CHTruncate ("mat", pAddPar->name, sizeof (pAddPar->name));
						GS::ucscpy (pAddPar->uDescname, L("Tom's Material"));
						pAddPar->value.real = 1;
						
						pAddPar = &(*addPars)[1];
						pAddPar->typeID = APIParT_Length;
						pAddPar->typeMod = 0;
						CHTruncate ("len", pAddPar->name, sizeof (pAddPar->name));
						GS::ucscpy (pAddPar->uDescname, L("Tom's Length"));
						pAddPar->value.real = 2.5;

						pAddPar = &(*addPars)[2];
						pAddPar->typeID = APIParT_CString;
						pAddPar->typeMod = 0;
						CHTruncate ("myStr", pAddPar->name, sizeof (pAddPar->name));
						GS::ucscpy (pAddPar->uDescname, L("Tom's String parameter"));
						GS::ucscpy (pAddPar->value.uStr, L("This is Tom's string"));
						
						double aa = 1.0;
						double bb = 1.0;
						GSHandle Sect2DHdl = NULL;
						err = ACAPI_LibPart_GetSect_ParamDef (&libPart, addPars, &aa, &bb, Sect2DHdl, &sectionHdl);
						
						API_LibPartDetails details;
						BNZeroMemory (&details, sizeof (API_LibPartDetails));
						details.object.autoHotspot = false;
						err = ACAPI_LibPart_SetDetails_ParamDef (&libPart, sectionHdl, &details);

						err = ACAPI_LibPart_UpdateSection (libPart.index, &section, sectionHdl, NULL);

						BMKillHandle (reinterpret_cast<GSHandle*>(&addPars));
						BMKillHandle (&sectionHdl);
					}

After running this code on a valid library part the library part is no more valid. In the Library Part dialog there are messages indicating that the 2D symbol and 3D symbol are not valid.

Probably i'm not using correctly the APIs.

Do you have any idea what i am doing wrong ?

Thanks 😉
9 REPLIES 9
Ralph Wessel
Mentor
gehairing wrote:
I try to modifiy an existing Library part by adding some parameters.
After running this code on a valid library part the library part is no more valid. In the Library Part dialog there are messages indicating that the 2D symbol and 3D symbol are not valid.
Do you have any idea what i am doing wrong ?
I think you need to include both the new and additional parameters when you make this call:
                  err = ACAPI_LibPart_SetDetails_ParamDef (&libPart, sectionHdl, &details);
Your existing code will be setting the library part to have only the 3 parameters in sectionHdl.
Ralph Wessel BArch
Software Engineer Speckle Systems
gehairing
Participant
Hello Ralph,

Thank's for your answer.

I don't understand what you mean
(Probably i don't understand how these api calls work togheter)

I "get" a section, create some additional parameters, "update" the section with these added parameters.
gehairing
Participant
When i use following code i have no problems at all :
// Set parameters to the given libPart
void TestSetParameters(const API_LibPart& libPart)
{
	// Add parameters/properties for this object
	API_LibPartSection section;
	BNZeroMemory (&section, sizeof (API_LibPartSection));
	section.sectType = API_SectParamDef;

	GSHandle sectionHdl = NULL;
	GSErrCode err = ACAPI_LibPart_GetSection(libPart.index, &section, &sectionHdl, NULL);
	
	API_LibPartDetails details;
	BNZeroMemory (&details, sizeof (API_LibPartDetails));
	details.object.autoHotspot = false;
	err = ACAPI_LibPart_SetDetails_ParamDef (&libPart, sectionHdl, &details);
	
	err = ACAPI_LibPart_UpdateSection (libPart.index, &section, sectionHdl, NULL);

	BMKillHandle (&sectionHdl);
}
When is use this code i have the problem :
// Set parameters to the given libPart
void TestSetParameters(const API_LibPart& libPart)
{
	// Add parameters/properties for this object
	API_LibPartSection section;
	BNZeroMemory (&section, sizeof (API_LibPartSection));
	section.sectType = API_SectParamDef;

	GSHandle sectionHdl = NULL;
	GSErrCode err = ACAPI_LibPart_GetSection(libPart.index, &section, &sectionHdl, NULL);

	short nPars = 1;
	API_AddParType** addPars = reinterpret_cast<API_AddParType**>(BMAllocateHandle (nPars * sizeof (API_AddParType), ALLOCATE_CLEAR, 0));
	if (addPars != NULL) 
	{
		API_AddParType* pAddPar = &(*addPars)[0];
		pAddPar->typeID = APIParT_Mater;
		pAddPar->typeMod = 0;
		CHTruncate ("mat", pAddPar->name, sizeof (pAddPar->name));
		GS::ucscpy (pAddPar->uDescname, L("Tom's testing parameter"));
		pAddPar->value.real = 1;
						
		double aa = 1.0;
		double bb = 1.0;
		GSHandle Sect2DHdl = NULL;
		err = ACAPI_LibPart_GetSect_ParamDef (&libPart, addPars, &aa, &bb, Sect2DHdl, &sectionHdl);

		BMKillHandle (reinterpret_cast<GSHandle*>(&addPars));
	}	
	
	err = ACAPI_LibPart_UpdateSection (libPart.index, &section, sectionHdl, NULL);

	BMKillHandle (&sectionHdl);
}

This is only testing code based on code i found in the Libpart_test project.
When i create a LibPart i have no problems at all.

I didn't find code samples for modifying existing Libpart.

I forgot to say that i have no error codes returned on any of the above api calls.
Ralph Wessel
Mentor
gehairing wrote:
When i use following code i have no problems at all :
[...]
When is use this code i have the problem :
[...]
This is only testing code based on code i found in the Libpart_test project.
When i create a LibPart i have no problems at all.
There is a marked difference between your 2 examples. Consider the contents of sectionHdl when you reach this part of the code:
   err = ACAPI_LibPart_UpdateSection (libPart.index, &section, sectionHdl, NULL);
In the first example, sectionHdl contains all the parameters because you retrieved them with ACAPI_LibPart_GetSection. In the second example, sectionHdl will contain only 1 parameter populated from addPars.

The call to ACAPI_LibPart_UpdateSection replaces all the parameters in the object with a new parameter list. I don't think it can be used to just add parameters. What you want to do is:
  • 1. Get all the existing parameters in a handle;
    2. Add the new parameter to the handle (so the list contains both new and existing parameters);
    3. Call ACAPI_LibPart_UpdateSection and pass the list with existing and new parameters in it.
Ralph Wessel BArch
Software Engineer Speckle Systems
gehairing
Participant
Thanks for your reply.

I thought getting the current section with GetSection and adding the new data with GetSect_ParamDef would update the section with the new data. That's what i understand after reading the API doc. But my english is not so good so i probably misunterstood this.

Do you have an idea how i can :
- Get actual parameters,
- Add a few new ones,
- Set all back again.
Ralph Wessel
Mentor
gehairing wrote:
Thanks for your reply.

I thought getting the current section with GetSection and adding the new data with GetSect_ParamDef would update the section with the new data. That's what i understand after reading the API doc. But my english is not so good so i probably misunterstood this.

Do you have an idea how i can :
- Get actual parameters,
- Add a few new ones,
- Set all back again.
The code you've written is almost right. Try getting the existing parameters with ACAPI_LibPart_GetParams and then append the new parameters in addPars. You might be able to use BMHandleAndHandle, but that depends on whether the result from ACAPI_LibPart_GetParams is an ordinary handle. The documentation advises disposing of it with ACAPI_DisposeAddParHdl, which suggests it's not an ordinary GSHandle. The safest is probably to allocate a handle large enough to accommodate the new and old parameters and populating that.
Ralph Wessel BArch
Software Engineer Speckle Systems
gehairing
Participant
Thanks Ralph. I'll try to find how to do what you've explained.

After my previous message i have tried following :

Creating a "dump" code :
(completely based on sample code i found in the API doc)
static void  DumpParameters (Int32 libInd, API_LibTypeID typeID)
{
    API_LibPartDetails   details;
    double               a, b;
    Int32                addParNum, i, ind, i1, i2;
    API_AddParType       **addPars;
    double               value;
    char                 *valueStr;
    GSErrCode            err;

    err = ACAPI_LibPart_GetParams (libInd, &a, &b, &addParNum, &addPars);
    if (err) return;

    for (i = 0; i < addParNum; i++) 
	{
        if ((*addPars).typeMod == API_ParSimple) 
		{
			ACAPI_WriteReport ((*addPars).name, false);
        } 
		else 
		{
            ind = 0;
			ACAPI_WriteReport ((*addPars).name, false);           
            for (i1 = 1; i1 <= (*addPars).dim1; i1++) 
			{
                for (i2 = 1; i2 <= (*addPars).dim2; i2++) 
				{
                    if ((*addPars).typeID != APIParT_CString) 
					{
                        value = (Int32) ((double *) *(*addPars).value.array) [ind];
                        valueStr = NULL;
                        ind ++;
                    } else 
					{
                        value = 0.0;
                        valueStr = *(*addPars).value.array + ind;
                        ind += strlen (valueStr) + 1;
                    }
                    ACAPI_WriteReport (valueStr, false);
                }
            }
        }
    }
    ACAPI_DisposeAddParHdl (&addPars); 
}
Called this method to dump all parameters before my code :
A
B
ZZYZX
AC_show2DHotspotsIn3D
ac_bottomlevel
ac_toplevel
showerType
services
matFinish
SKU
gs_2D_representation
gs_cont_pen
gs_fill_type
gs_fill_pen
gs_back_pen
gs_list
gs_list_cost
gs_list_manufacturer
gs_list_note
gs_list_location
gs_list_accessories
FM_Type
iFMType
FM_InventoryNumber
FM_SerialNumber
FM_ProductionYear
FM_ObjectWeight
FM_ObjectWeightUnit
gs_list_custom1
gs_list_custom2
gs_list_custom3
gs_list_custom4
gs_list_custom5
gs_3D_representation
gs_detlevel_3D
gs_shadow
doorOpen
Then i run this code (the same as in my previous message) to "add" a parameter :
// Set parameters to the given libPart 
void TestSetParameters(const API_LibPart& libPart) 
{ 
   // Add parameters/properties for this object 
   API_LibPartSection section; 
   BNZeroMemory (&section, sizeof (API_LibPartSection)); 
   section.sectType = API_SectParamDef; 

   GSHandle sectionHdl = NULL; 
   GSErrCode err = ACAPI_LibPart_GetSection(libPart.index, &section, &sectionHdl, NULL); 

   short nPars = 1; 
   API_AddParType** addPars = reinterpret_cast<API_AddParType**>(BMAllocateHandle (nPars * sizeof (API_AddParType), ALLOCATE_CLEAR, 0)); 
   if (addPars != NULL) 
   { 
      API_AddParType* pAddPar = &(*addPars)[0]; 
      pAddPar->typeID = APIParT_Mater; 
      pAddPar->typeMod = 0; 
      CHTruncate ("mat", pAddPar->name, sizeof (pAddPar->name)); 
      GS::ucscpy (pAddPar->uDescname, L("Tom's testing parameter")); 
      pAddPar->value.real = 1; 
                   
      double aa = 1.0; 
      double bb = 1.0; 
      GSHandle Sect2DHdl = NULL; 
      err = ACAPI_LibPart_GetSect_ParamDef (&libPart, addPars, &aa, &bb, Sect2DHdl, &sectionHdl); 

      BMKillHandle (reinterpret_cast<GSHandle*>(&addPars)); 
   }    
    
   err = ACAPI_LibPart_UpdateSection (libPart.index, &section, sectionHdl, NULL); 

   BMKillHandle (&sectionHdl); 
} 
Then run the dump code again :
A
B
ZZYZX
AC_show2DHotspotsIn3D
ac_bottomlevel
ac_toplevel
gs_2D_representation
gs_cont_pen
gs_fill_type
gs_fill_pen
gs_back_pen
gs_list
gs_list_cost
gs_list_manufacturer
gs_list_note
gs_list_location
gs_list_accessories
FM_Type
iFMType
FM_InventoryNumber
FM_SerialNumber
FM_ProductionYear
FM_ObjectWeight
FM_ObjectWeightUnit
gs_list_custom1
gs_list_custom2
gs_list_custom3
gs_list_custom4
gs_list_custom5
mat
"mat" is the one i "added".
So It seem's to me that i have lost (or destroyed or whatever )some parameters but not all.
Ralph Wessel
Mentor
gehairing wrote:
Thanks Ralph. I'll try to find how to do what you've explained.
After my previous message i have tried following :
Creating a "dump" code :
(completely based on sample code i found in the API doc)
[...]
"mat" is the one i "added".
So It seem's to me that i have lost (or destroyed or whatever )some parameters but not all.
I suspect that all the parameters before 'mat' are required for the object subtype, so ARCHICAD automatically injects them. So your original list is destroyed and replaced with the subtype parameters and the 1 you added.
Ralph Wessel BArch
Software Engineer Speckle Systems
gehairing
Participant
I have found out how to manage that.
Thank you Ralph for guiding me.

Here is some testing code that works, hope it can help someone one day.
Now i have to do the real clean code...
static void AddParameterTest (const API_LibPart& libPart)
{
    GSErrCode err;

	API_LibPartSection section;
	BNZeroMemory (&section, sizeof (API_LibPartSection));
	section.sectType = API_SectParamDef;

	GSHandle sectionHdl = NULL;
	err = ACAPI_LibPart_GetSection(libPart.index, &section, &sectionHdl, NULL);
	if (err) return;

	double a, b;
	Int32 addParNum, i;
    API_AddParType **addPars;
    err = ACAPI_LibPart_GetParams (libPart.index, &a, &b, &addParNum, &addPars);
    if (err) return;

	// Create enough space for all parameters
	Int32 myParameterCount = 1;
	short nPars = addParNum + myParameterCount;
	API_AddParType** newAddPars = reinterpret_cast<API_AddParType**>(BMAllocateHandle (nPars * sizeof (API_AddParType), ALLOCATE_CLEAR, 0));
	if (addPars != NULL) 
	{
		// Copy all parameters
		for (i = 0; i < addParNum; i++) 
		{
			(*newAddPars) = (*addPars);
		}

		// Add our new parameter
		API_AddParType* pAddPar = &(*newAddPars)[nPars-1];
		pAddPar->typeID = APIParT_Mater;
		pAddPar->typeMod = 0;
		CHTruncate ("mat", pAddPar->name, sizeof (pAddPar->name));
		GS::ucscpy (pAddPar->uDescname, L("Tom's testing parameter"));
		pAddPar->value.real = 1;
						
		// Build a section handle with all parameters
		GSHandle Sect2DHdl = NULL;
		err = ACAPI_LibPart_GetSect_ParamDef (&libPart, newAddPars, &a, &b, NULL, &sectionHdl);

		BMKillHandle (reinterpret_cast<GSHandle*>(&newAddPars));
	}	

	// Update our section...
	err = ACAPI_LibPart_UpdateSection (libPart.index, &section, sectionHdl, NULL);
	BMKillHandle (&sectionHdl);

    ACAPI_DisposeAddParHdl (&addPars); 
}