The Global ARCHICAD Community

Stay informed. Get help. Share your knowledge.

Discussions about using GRAPHISOFT's tools (API DevKit) for independent software developers

Moderators: Barry Kelly, Karl Ottenstein, LaszloNagy, ejrolon, gkmethy, Tibor Lorántfy, MOREH Tamas, Akos Somorjai, Ed Brown, Mihály Palenik

User avatar
By kzaremba
#286892
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?
User avatar
By Tibor Lorántfy
#286893
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.
User avatar
By kzaremba
#286896
Thx I will look into it. I thought it will be simpler :D.

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... :D
User avatar
By kzaremba
#286920
I did my first attempt base on this post:
viewtopic.php?f=23&t=43940&p=221023&hil ... le#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.
Code: Select all 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);
User avatar
By Tibor Lorántfy
#287079
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:
Code: Select all#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; }
User avatar
By kzaremba
#287331
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?

Code: Select allGS::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; }
Code: Select allGS::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; }
User avatar
By Tibor Lorántfy
#287334
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!
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.
User avatar
By kzaremba
#287373
Nice, I'm glad you succeed!
Thx to you :D
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.