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

Custom GUID

Anonymous
Not applicable
I'm playing around with different Create functions. I have noticed that a new element has always new GUID. When I'm passing GUID in element it is getting new GUID anyway. In some cases, I would like to define my own GUID and create an element with it. Is this possible?
1 ACCEPTED SOLUTION

Accepted Solutions
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
I suggest you to create a simple class for your userdata.
I wrote an example. In this way you can store any kind of data. Feel free to extend it:
#include	"MemoryIChannel.hpp"
#include	"MemoryOChannel.hpp"
#include	"SetPlatformProtocol.hpp"

class MyUserData {
	Int32	id;
	char	string[128];

	GSErrCode LoadFromUserData (const API_ElementUserData& userData);
	GSErrCode SaveToUserData (API_ElementUserData& userData) const;

public:
	MyUserData () : id (-1) {}
	MyUserData (Int32 id, const char* cstr) : id (id) {
		CHTruncate (cstr, string, sizeof (string));
	}

	GSErrCode GetFromElement (const API_Guid& elemGuid);
	GSErrCode SetToElement (const API_Guid& elemGuid) const;
};

// -----------------------------------------------------------------------------

// How to set:

MyUserData userData (1234, "My own ID");
userData.SetToElement (elemGuid);

// How to get:

MyUserData userData2;
if (userData2.GetFromElement (elemGuid2) != APIERR_NOUSERDATA) {
	//
}

// -----------------------------------------------------------------------------

GSErrCode MyUserData::LoadFromUserData (const API_ElementUserData& userData)
{
	IO::MemoryIChannel memChannel (*(userData.dataHdl), BMGetHandleSize (userData.dataHdl));
	IO::SetPlatformIProtocol (memChannel, static_cast<GS::PlatformSign> (userData.platformSign));
	GSErrCode err = NoError;
	err = memChannel.Read (id);
	if (err != NoError)
		return err;
	err = memChannel.Read (string);
	if (err != NoError)
		return err;

	return NoError;
}


GSErrCode MyUserData::SaveToUserData (API_ElementUserData& userData) const
{
	userData.dataVersion = 1;
	userData.platformSign = GS::Act_Platform_Sign;
	userData.flags = APIUserDataFlag_FillWith | APIUserDataFlag_Pickup;

	IO::MemoryOChannel memChannel;
	memChannel.Write (id);
	memChannel.Write (string);

	const USize nBytes = memChannel.GetDataSize ();
	const char* pData = memChannel.GetDestination ();

	userData.dataHdl = BMAllocateHandle (nBytes, ALLOCATE_CLEAR, 0);
	if (userData.dataHdl == nullptr)
		return APIERR_MEMFULL;

	BNCopyMemory (*(userData.dataHdl), pData, nBytes);
	return NoError;
}

GSErrCode MyUserData::GetFromElement (const API_Guid& elemGuid)
{
	API_ElementUserData userData = {};
	API_Elem_Head elemHead = {};
	elemHead.guid = elemGuid;
	GSErrCode err = ACAPI_Element_GetUserData (&elemHead, &userData);
	if (err != NoError)
		return err;

	err = LoadFromUserData (userData);
	BMKillHandle (&userData.dataHdl);
	return err;
}

GSErrCode MyUserData::SetToElement (const API_Guid& elemGuid) const
{
	API_ElementUserData userData = {};
	GSErrCode err = SaveToUserData (userData);
	if (err != NoError)
		return err;

	API_Elem_Head elemHead = {};
	elemHead.guid = elemGuid;
	err = ACAPI_Element_SetUserData (&elemHead, &userData);
	BMKillHandle (&userData.dataHdl);
	return err;
}

View solution in original post

15 REPLIES 15
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
No, it's not possible. The GUID will be generated by ARCHICAD for the new elements.
What's your purpose? Why do you need an element with your specific own GUID?

You can set custom 'Element ID' string to the elements. That could be also a good way to identify your own elements.
Or you can set custom userdata for the elements using ACAPI_Element_SetUserData function.
Or you can create your own property definition and set that property to any custom value/string for the elements.
Anonymous
Not applicable
Thx I will look into it. I thought it will be simpler .

I'm trying to create external database. And some operations can't be updated with modify function so I'm deleting elements and createing new ones. Of course then I get different guids in files and database...
Anonymous
Not applicable
I did my first attempt base on this post:
https://archicad-talk.graphisoft.com/viewtopic.php?f=23&t=43940&p=221023&hilit=GSHandle#p221023

However, I got the only Version and when I read value I got 0 or a bunch of integers. So I assume either I didn't assign value and It's 0 from allocation or in another case I'm saving pointer instead of value.

There is only one difference with an example in BMPtrAndHandle second parameter is a pointer, not value. The function didn't accept the dereferenced value.

	int savedData = 234;
	int* savedDataPtr = &savedData;
	int** sDataPtrPtr = &savedDataPtr;
	API_AttributeUserData MyUserData;

	userData.dataVersion = 1234;
	userData.platformSign = GS::Win_Platform_Sign;
	userData.dataHdl = BMAllocateHandle(sizeof(int), ALLOCATE_CLEAR,0);
		
	GSErr ud_err = BMPtrAndHandle(&sDataPtrPtr, userData.dataHdl, sizeof(int));
	
	GS::ErrCode err = ACAPI_Attribute_SetUserData(&attr.header, &userData);
Anonymous
Not applicable
Any suggestions would be greatly appreciated
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
I suggest you to create a simple class for your userdata.
I wrote an example. In this way you can store any kind of data. Feel free to extend it:
#include	"MemoryIChannel.hpp"
#include	"MemoryOChannel.hpp"
#include	"SetPlatformProtocol.hpp"

class MyUserData {
	Int32	id;
	char	string[128];

	GSErrCode LoadFromUserData (const API_ElementUserData& userData);
	GSErrCode SaveToUserData (API_ElementUserData& userData) const;

public:
	MyUserData () : id (-1) {}
	MyUserData (Int32 id, const char* cstr) : id (id) {
		CHTruncate (cstr, string, sizeof (string));
	}

	GSErrCode GetFromElement (const API_Guid& elemGuid);
	GSErrCode SetToElement (const API_Guid& elemGuid) const;
};

// -----------------------------------------------------------------------------

// How to set:

MyUserData userData (1234, "My own ID");
userData.SetToElement (elemGuid);

// How to get:

MyUserData userData2;
if (userData2.GetFromElement (elemGuid2) != APIERR_NOUSERDATA) {
	//
}

// -----------------------------------------------------------------------------

GSErrCode MyUserData::LoadFromUserData (const API_ElementUserData& userData)
{
	IO::MemoryIChannel memChannel (*(userData.dataHdl), BMGetHandleSize (userData.dataHdl));
	IO::SetPlatformIProtocol (memChannel, static_cast<GS::PlatformSign> (userData.platformSign));
	GSErrCode err = NoError;
	err = memChannel.Read (id);
	if (err != NoError)
		return err;
	err = memChannel.Read (string);
	if (err != NoError)
		return err;

	return NoError;
}


GSErrCode MyUserData::SaveToUserData (API_ElementUserData& userData) const
{
	userData.dataVersion = 1;
	userData.platformSign = GS::Act_Platform_Sign;
	userData.flags = APIUserDataFlag_FillWith | APIUserDataFlag_Pickup;

	IO::MemoryOChannel memChannel;
	memChannel.Write (id);
	memChannel.Write (string);

	const USize nBytes = memChannel.GetDataSize ();
	const char* pData = memChannel.GetDestination ();

	userData.dataHdl = BMAllocateHandle (nBytes, ALLOCATE_CLEAR, 0);
	if (userData.dataHdl == nullptr)
		return APIERR_MEMFULL;

	BNCopyMemory (*(userData.dataHdl), pData, nBytes);
	return NoError;
}

GSErrCode MyUserData::GetFromElement (const API_Guid& elemGuid)
{
	API_ElementUserData userData = {};
	API_Elem_Head elemHead = {};
	elemHead.guid = elemGuid;
	GSErrCode err = ACAPI_Element_GetUserData (&elemHead, &userData);
	if (err != NoError)
		return err;

	err = LoadFromUserData (userData);
	BMKillHandle (&userData.dataHdl);
	return err;
}

GSErrCode MyUserData::SetToElement (const API_Guid& elemGuid) const
{
	API_ElementUserData userData = {};
	GSErrCode err = SaveToUserData (userData);
	if (err != NoError)
		return err;

	API_Elem_Head elemHead = {};
	elemHead.guid = elemGuid;
	err = ACAPI_Element_SetUserData (&elemHead, &userData);
	BMKillHandle (&userData.dataHdl);
	return err;
}
Anonymous
Not applicable
Thanks a lot, Tibor,
You are a life saver. I will test it right away!
Anonymous
Not applicable
It's working perfectly. I added Attributes as well there is a slight adjustment since ACAPI_Attribute_Set_UserData takes only type and Idx instead of GUID. So I'm passing API_Attribute instead of GUID.

I was recently wondering why the whole API is not structured as classes?? But with structs and all methods as separate functions. It would be much easier to have API_Element as a class with all parameters and methods warped up in one class. I'm actually doing in spare time but it would be great to have it ready made by professional developers. Since there is always some conversion confusion and memory issues to consider.

Anyway is there any reason or benefit of using such kind of coding style?

GS::ErrCode MyUserData::SetToAttrib(const API_Attribute& attrib) const
{
	API_AttributeUserData userData = {};
	GSErrCode err = SaveToUserDataAtt(userData);
	if (err != NoError)
		return err;

	API_Attr_Head attHead = {};
	attHead.index = attrib.header.index;
	attHead.typeID = attrib.header.typeID;
	err = ACAPI_Attribute_SetUserData(&attHead, &userData);
	BMKillHandle(&userData.dataHdl);
	return err;
}
GS::ErrCode MyUserData::GetFromAttribute(const API_Attribute& attrib)
{
	API_AttributeUserData userData = {};
	API_Attr_Head attHead = {};

	attHead.index = attrib.header.index;
	attHead.typeID = attrib.header.typeID;

	GSErrCode err = ACAPI_Attribute_GetUserData(&attHead, &userData);
	if (err != NoError)
		return err;

	err = LoadFromUserDataAtt(userData);
	BMKillHandle(&userData.dataHdl);
	return err;
}
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
kzaremba wrote:
It's working perfectly. I added Attributes as well there is a slight adjustment since ACAPI_Attribute_Set_UserData takes only type and Idx instead of GUID. So I'm passing API_Attribute instead of GUID.
Nice, I'm glad you succeed!
kzaremba wrote:
I was recently wondering why the whole API is not structured as classes?? But with structs and all methods as separate functions. It would be much easier to have API_Element as a class with all parameters and methods warped up in one class.
It has historical reasons. The first ARCHICAD version was released more than 30 years ago and the first API version is also more than 20 years old. Back then C++ was still just a baby...
You're absolutely right, it would be much more easier that way, but ARCHICAD database is a very complex system.
Anonymous
Not applicable
Tibor wrote:
Nice, I'm glad you succeed!
Thx to you
Tibor wrote:
it would be much more easier that way
So if it's only historical... Do you think there is a possibility to start GitRepository (or similar solution) to create the library of classes working with actual API (like the example above)? So if someone is interested in developing such an approach could collaborate and build upon some examples like this? This might be an even better way to share this knowledge than the forum where some topics are covered by the others.