BIM Coordinator Program (INT) April 22, 2024

Find the next step in your career as a Graphisoft Certified BIM Coordinator!

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

ModifyHotlinkNodeID Not Working?

Anonymous
Not applicable
We're investigating ways to easily modify existing hot link source locations due to a server protocol and hostname change. I'm trying to change the source location path, and the API says the change has been made, but this isn't reflected in the Archicad UI (still shows old links even after re-opening).

What am I doing wrong?

I am running Archicad 22 in Trial mode to test the plugin.

Code:

GS::Array<API_Guid> gotlinkNodeIDs;
    if (ACAPI_Database (APIDb_GetHotlinkNodesID, NULL, &gotlinkNodeIDs) != NoError) {
        DBPrintf("Error getting hot link nodes!");
        return;
    }
    
    for (UIndex i = 0; i < gotlinkNodeIDs.GetSize (); i++) {
        GSErrCode err;
        
        // Get hotlink from GUID
        API_HotlinkNode hotlinkNode;
        BNZeroMemory (&hotlinkNode, sizeof (API_HotlinkNode));
        hotlinkNode.guid = gotlinkNodeIDs;
        
        GS::UniString string = APIGuidToString(hotlinkNode.guid);
        const char *guidString = string.ToCStr();
        DBPrintf("Getting Link %s ... \n", guidString);
        
        err = ACAPI_Database (APIDb_GetHotlinkNodeID, &hotlinkNode, nullptr);
        if (err != NoError) {
            DBPrintf("Error getting link! %d \n", err);
        }
        else {
            // Print current link + get current path
            GS::UniString displayLinkText = hotlinkNode.sourceLocation->ToDisplayText();
            const char *displayLinkTextCString = displayLinkText.ToCStr();
            
            IO::Path currentPath;
            hotlinkNode.sourceLocation->ToUTF8POSIXPath(&currentPath);
            GS::UniString currentPathString (currentPath);
            
            DBPrintf("\t Old: %s \n", displayLinkTextCString);

            // Remove old share from path
            currentPathString.DeleteFirst("/Studio Server");
            
            // Set new server and add current path
            GS::UniString newLocationURL ("smb://new.server.fqdn/Studio Server/");
            newLocationURL.Append(currentPathString);
            
            // Set hotlinknode sourceLocation to new location object
            IO::Location newFileLocation (newLocationURL);
            hotlinkNode.sourceLocation = &newFileLocation;

            // new display text link to show new link
            displayLinkText = newFileLocation.ToDisplayText();
            displayLinkTextCString = displayLinkText.ToCStr();
            
            DBPrintf("\t New: %s \n", displayLinkTextCString);
            
            // Update the hotlink via API
            err = ACAPI_Database (APIDb_ModifyHotlinkNodeID, &hotlinkNode);        // rename hotlink node
            
            if (err != NoError) {
                DBPrintf("\t ERROR: Hot link not updated: %d.", err);
            }
            else {
                DBPrintf("\t Hot link updated.");
            }
            
            // Update cache
            if (err == NoError && hotlinkNode.guid != APINULLGuid)   {
                // update cache content
                DBPrintf(" Updating cache.");
                ACAPI_Database (APIDb_UpdateHotlinkCacheID, const_cast<API_Guid*> (&hotlinkNode.guid));
            }
            DBPrintf("\n");
        }
    }
    
    WriteReport("Updated all links");
Result:

Dec 19 09:15:04  Archicad 22.app[4649]: Getting Link 396470F1-4EFC-DEDC-D658-45A971C4367B ... 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/Archicad Test file/modules/Level 1-4 - shell.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 New: smb://new.server.fqdn/Studio%20Server/TEMPORARY/Archicad%20Test%20file/modules/Level%201-4%20-%20shell.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Hot link updated. Updating cache.
Dec 19 09:15:04  Archicad 22.app[4649]: Getting Link B94B6C0E-53F4-CD2C-2EC7-187D7936434B ... 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/Archicad Test file/modules/19. Type A.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 New: smb://new.server.fqdn/Studio%20Server/TEMPORARY/Archicad%20Test%20file/modules/19.%20Type%20A.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Hot link updated. Updating cache.
Dec 19 09:15:04  Archicad 22.app[4649]: Getting Link 7962E17F-5ADC-A333-446E-B6EF1AAC39E0 ... 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/Archicad Test file/modules/Bathroom 1.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 New: smb://new.server.fqdn/Studio%20Server/TEMPORARY/Archicad%20Test%20file/modules/Bathroom%201.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Hot link updated. Updating cache.
Dec 19 09:15:04  Archicad 22.app[4649]: Getting Link BCFE0B7F-CCF2-7160-B1D2-A7A265105E42 ... 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/Archicad Test file/modules/Kitchen 1.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 New: smb://new.server.fqdn/Studio%20Server/TEMPORARY/Archicad%20Test%20file/modules/Kitchen%201.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Hot link updated. Updating cache.
Dec 19 09:15:04  Archicad 22.app[4649]: Getting Link 47CCE46F-9620-BE50-9ACB-147F8FFF0FE4 ... 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/Archicad Test file/modules/16. Type D.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 New: smb://new.server.fqdn/Studio%20Server/TEMPORARY/Archicad%20Test%20file/modules/16.%20Type%20D.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Hot link updated. Updating cache.
Dec 19 09:15:04  Archicad 22.app[4649]: Getting Link AE237499-B9A2-44B2-EC61-BD1BDBD4CE38 ... 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/Archicad Test file/modules/Kitchen 2.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 New: smb://new.server.fqdn/Studio%20Server/TEMPORARY/Archicad%20Test%20file/modules/Kitchen%202.mod 
Dec 19 09:15:04  Archicad 22.app[4649]: 	 Hot link updated. Updating cache.
1 ACCEPTED SOLUTION

Accepted Solutions
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Hi,

You're right, the APIDb_ModifyHotlinkNodeID method returns NoError even if the location modification was unsuccessful in case of an illegal input location. It's a bug in the API, I added it to our database.

I modified your code a little bit and it seems working now:
    GS::Array<API_Guid> gotlinkNodeIDs;
    if (ACAPI_Database (APIDb_GetHotlinkNodesID, NULL, &gotlinkNodeIDs) != NoError) {
        DBPrintf ("Error getting hot link nodes!");
        return;
    }

    for (UIndex i = 0; i < gotlinkNodeIDs.GetSize (); i++) {
        GSErrCode err;

        // Get hotlink from GUID
        API_HotlinkNode hotlinkNode = {};
        hotlinkNode.guid = gotlinkNodeIDs;

        GS::UniString guidString = APIGuidToString (hotlinkNode.guid);
        DBPrintf ("Getting Link %s ... \n", guidString.ToCStr ().Get ());

        err = ACAPI_Database (APIDb_GetHotlinkNodeID, &hotlinkNode, nullptr);
        if (err != NoError) {
            DBPrintf ("Error getting link! %d \n", err);
        } else {
            // Print current link + get current path
            GS::UniString displayLinkText = hotlinkNode.sourceLocation->ToDisplayText ();
            DBPrintf ("\t Old: %s \n", displayLinkText.ToCStr ().Get ());

            // Remove old share from path
            class : public IO::NameEnumerator {
                bool                 append = false;
            public:
                IO::RelativeLocation result;

                virtual void	NameFound (const IO::Name& name)
                {
                    if (append) {
                        result.Append (name);
                    } else {
                        if (name.ToString ().IsEqual ("Studio Server", GS::UniString::CaseInsensitive)) {
                            append = true;
                        }
                    }
                }
            } localNameEnumerator;

            hotlinkNode.sourceLocation->EnumerateLocalNames (&localNameEnumerator);

            // Set new server and add current path
            IO::Location newFileLocation ("smb://new.server.fqdn/Studio Server");
            newFileLocation.AppendToLocal (localNameEnumerator.result);

            // Set hotlinknode sourceLocation to new location object
            delete hotlinkNode.sourceLocation;
            hotlinkNode.sourceLocation = &newFileLocation;

            // new display text link to show new link
            displayLinkText = newFileLocation.ToDisplayText ();
            DBPrintf ("\t New: %s \n", displayLinkText.ToCStr ().Get ());

            // Update the hotlink via API
            err = ACAPI_Database (APIDb_ModifyHotlinkNodeID, &hotlinkNode);        // rename hotlink node

            if (err != NoError) {
                DBPrintf ("\t ERROR: Hot link not updated: %d.", err);
            } else {
                DBPrintf ("\t Hot link updated.");
            }

            // Update cache
            if (err == NoError && hotlinkNode.guid != APINULLGuid) {
                // update cache content
                DBPrintf (" Updating cache.");
                ACAPI_Database (APIDb_UpdateHotlinkCacheID, const_cast<API_Guid*> (&hotlinkNode.guid));
            }
            DBPrintf ("\n");
        }
    }

I found the following bugs in your code:
  • Before set hotlinkNode.sourceLocation, make sure to delete the previous location allocated by the API to prevent memory leak:
    delete hotlinkNode.sourceLocation;
    hotlinkNode.sourceLocation = &newFileLocation;
  • As you can see in your log too, there are two '/' signs next to each other at the middle of the location:
    afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/19. Type A.mod
    API handled this location as illegal, that caused your code did not work. The modified code avoids this issue.
    Note that you can check your location validity using the IsLegalInNativeFileSystem function:
    if (hotlinkNode.sourceLocation->IsLegalInNativeFileSystem ())
    If you want to check whether a given location is exists in the filesystem, then you can do it like this:
    bool exists = false;
    GSErrCode err = IO::fileSystem.Contains (projectLocation, &exists);
    if (err == NoError && exists)
    (including FileSystem.hpp can be necessary)
  • Your code was macOS platform specific. If you use it only on macOS, then it's not a problem. Now the modified code should work on both platforms.
Regards,
Tibor

View solution in original post

6 REPLIES 6
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Hi,

You're right, the APIDb_ModifyHotlinkNodeID method returns NoError even if the location modification was unsuccessful in case of an illegal input location. It's a bug in the API, I added it to our database.

I modified your code a little bit and it seems working now:
    GS::Array<API_Guid> gotlinkNodeIDs;
    if (ACAPI_Database (APIDb_GetHotlinkNodesID, NULL, &gotlinkNodeIDs) != NoError) {
        DBPrintf ("Error getting hot link nodes!");
        return;
    }

    for (UIndex i = 0; i < gotlinkNodeIDs.GetSize (); i++) {
        GSErrCode err;

        // Get hotlink from GUID
        API_HotlinkNode hotlinkNode = {};
        hotlinkNode.guid = gotlinkNodeIDs;

        GS::UniString guidString = APIGuidToString (hotlinkNode.guid);
        DBPrintf ("Getting Link %s ... \n", guidString.ToCStr ().Get ());

        err = ACAPI_Database (APIDb_GetHotlinkNodeID, &hotlinkNode, nullptr);
        if (err != NoError) {
            DBPrintf ("Error getting link! %d \n", err);
        } else {
            // Print current link + get current path
            GS::UniString displayLinkText = hotlinkNode.sourceLocation->ToDisplayText ();
            DBPrintf ("\t Old: %s \n", displayLinkText.ToCStr ().Get ());

            // Remove old share from path
            class : public IO::NameEnumerator {
                bool                 append = false;
            public:
                IO::RelativeLocation result;

                virtual void	NameFound (const IO::Name& name)
                {
                    if (append) {
                        result.Append (name);
                    } else {
                        if (name.ToString ().IsEqual ("Studio Server", GS::UniString::CaseInsensitive)) {
                            append = true;
                        }
                    }
                }
            } localNameEnumerator;

            hotlinkNode.sourceLocation->EnumerateLocalNames (&localNameEnumerator);

            // Set new server and add current path
            IO::Location newFileLocation ("smb://new.server.fqdn/Studio Server");
            newFileLocation.AppendToLocal (localNameEnumerator.result);

            // Set hotlinknode sourceLocation to new location object
            delete hotlinkNode.sourceLocation;
            hotlinkNode.sourceLocation = &newFileLocation;

            // new display text link to show new link
            displayLinkText = newFileLocation.ToDisplayText ();
            DBPrintf ("\t New: %s \n", displayLinkText.ToCStr ().Get ());

            // Update the hotlink via API
            err = ACAPI_Database (APIDb_ModifyHotlinkNodeID, &hotlinkNode);        // rename hotlink node

            if (err != NoError) {
                DBPrintf ("\t ERROR: Hot link not updated: %d.", err);
            } else {
                DBPrintf ("\t Hot link updated.");
            }

            // Update cache
            if (err == NoError && hotlinkNode.guid != APINULLGuid) {
                // update cache content
                DBPrintf (" Updating cache.");
                ACAPI_Database (APIDb_UpdateHotlinkCacheID, const_cast<API_Guid*> (&hotlinkNode.guid));
            }
            DBPrintf ("\n");
        }
    }

I found the following bugs in your code:
  • Before set hotlinkNode.sourceLocation, make sure to delete the previous location allocated by the API to prevent memory leak:
    delete hotlinkNode.sourceLocation;
    hotlinkNode.sourceLocation = &newFileLocation;
  • As you can see in your log too, there are two '/' signs next to each other at the middle of the location:
    afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/19. Type A.mod
    API handled this location as illegal, that caused your code did not work. The modified code avoids this issue.
    Note that you can check your location validity using the IsLegalInNativeFileSystem function:
    if (hotlinkNode.sourceLocation->IsLegalInNativeFileSystem ())
    If you want to check whether a given location is exists in the filesystem, then you can do it like this:
    bool exists = false;
    GSErrCode err = IO::fileSystem.Contains (projectLocation, &exists);
    if (err == NoError && exists)
    (including FileSystem.hpp can be necessary)
  • Your code was macOS platform specific. If you use it only on macOS, then it's not a problem. Now the modified code should work on both platforms.
Regards,
Tibor
Anonymous
Not applicable
Hi Tibor,

Thanks for the quick and informative reply

Unfortunately I'm still getting errors when setting a new valid location. As you can see in the logs, the new location paths pass the validation + file exists tests, however the API is returning error code: -2130312311 (APIERR_REFUSEDPAR)

What is the refused parameter here? It seems to pass all of the tests.

Code
GS::Array<API_Guid> gotlinkNodeIDs;
    if (ACAPI_Database (APIDb_GetHotlinkNodesID, NULL, &gotlinkNodeIDs) != NoError) {
        DBPrintf ("Error getting hot link nodes!");
        return;
    }
    
    for (UIndex i = 0; i < gotlinkNodeIDs.GetSize (); i++) {
        GSErrCode err;
        
        // Get hotlink from GUID
        API_HotlinkNode hotlinkNode = {};
        hotlinkNode.guid = gotlinkNodeIDs;
        
        GS::UniString guidString = APIGuidToString (hotlinkNode.guid);
        DBPrintf ("Getting Link %s ... \n", guidString.ToCStr ().Get ());
        
        err = ACAPI_Database (APIDb_GetHotlinkNodeID, &hotlinkNode, nullptr);
        if (err != NoError) {
            DBPrintf ("Error getting link! %d \n", err);
        } else {
            // Print current link + get current path
            GS::UniString displayLinkText = hotlinkNode.sourceLocation->ToDisplayText ();
            DBPrintf ("\t Old: %s \n", displayLinkText.ToCStr ().Get ());
            
            // Remove old share from path
            class : public IO::NameEnumerator {
                bool                 append = false;
            public:
                IO::RelativeLocation result;
                
                virtual void    NameFound (const IO::Name& name)
                {
                    if (append) {
                        result.Append (name);
                    } else {
                        if (name.ToString ().Contains ("Studio Server", GS::UniString::CaseInsensitive)) {
                            append = true;
                        }
                    }
                }
            } localNameEnumerator;
            
            hotlinkNode.sourceLocation->EnumerateLocalNames (&localNameEnumerator);
            
            // Set new server and add current path
            IO::Location newFileLocation ("smb://Storage.local/Storage");
            newFileLocation.AppendToLocal (localNameEnumerator.result);
            
            // new display text link to show new link
            displayLinkText = newFileLocation.ToDisplayText ();
            DBPrintf ("\t New: %s \n\t ", displayLinkText.ToCStr ().Get ());
            
            if (newFileLocation.IsLegalInNativeFileSystem ()) {
                DBPrintf("Path is legal... ");
            }
            else {
                DBPrintf("Path not legal... ");
            }
            
            bool exists = false;
            GSErrCode err = IO::fileSystem.Contains (newFileLocation, &exists);
            if (err == NoError && exists) {
                DBPrintf("Path exists... ");
            }
            else {
                DBPrintf("Path doesn't exist... ");
            }
            
            // Set hotlinknode sourceLocation to new location object
            delete hotlinkNode.sourceLocation;
            hotlinkNode.sourceLocation = &newFileLocation;
            
            // Update the hotlink via API
            err = ACAPI_Database (APIDb_ModifyHotlinkNodeID, &hotlinkNode);
            
            if (err != NoError) {
                DBPrintf ("ERROR: Hot link not updated: %d. ", err);
            } else {
                DBPrintf ("Hot link updated. ");
            }
            
            // Update cache
            if (err == NoError && hotlinkNode.guid != APINULLGuid) {
                // update cache content
                DBPrintf ("Updating cache.");
                ACAPI_Database (APIDb_UpdateHotlinkCacheID, const_cast<API_Guid*> (&hotlinkNode.guid));
            }
            DBPrintf ("\n");
        }
    }
Result
Dec 20 07:45:06 ARCHICAD 22.app[7578]: Getting Link 396470F1-4EFC-DEDC-D658-45A971C4367B ... 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/Level 1-4 - shell.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 New: smb://Storage.local/Storage/TEMPORARY/ARCHICAD%20Test%20file/modules/Level%201-4%20-%20shell.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Path is legal... Path exists... ERROR: Hot link not updated: -2130312311. 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: Getting Link B94B6C0E-53F4-CD2C-2EC7-187D7936434B ... 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/19. Type A.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 New: smb://Storage.local/Storage/TEMPORARY/ARCHICAD%20Test%20file/modules/19.%20Type%20A.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Path is legal... Path exists... ERROR: Hot link not updated: -2130312311. 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: Getting Link 7962E17F-5ADC-A333-446E-B6EF1AAC39E0 ... 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/Bathroom 1.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 New: smb://Storage.local/Storage/TEMPORARY/ARCHICAD%20Test%20file/modules/Bathroom%201.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Path is legal... Path exists... ERROR: Hot link not updated: -2130312311. 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: Getting Link BCFE0B7F-CCF2-7160-B1D2-A7A265105E42 ... 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/Kitchen 1.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 New: smb://Storage.local/Storage/TEMPORARY/ARCHICAD%20Test%20file/modules/Kitchen%201.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Path is legal... Path exists... ERROR: Hot link not updated: -2130312311. 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: Getting Link 47CCE46F-9620-BE50-9ACB-147F8FFF0FE4 ... 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/16. Type D.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 New: smb://Storage.local/Storage/TEMPORARY/ARCHICAD%20Test%20file/modules/16.%20Type%20D.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Path is legal... Path exists... ERROR: Hot link not updated: -2130312311. 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: Getting Link AE237499-B9A2-44B2-EC61-BD1BDBD4CE38 ... 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Old: afp://old.server.fqdn/Studio%20Server//TEMPORARY/ARCHICAD Test file/modules/Kitchen 2.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 New: smb://Storage.local/Storage/TEMPORARY/ARCHICAD%20Test%20file/modules/Kitchen%202.mod 
Dec 20 07:45:06 ARCHICAD 22.app[7578]: 	 Path is legal... Path exists... ERROR: Hot link not updated: -2130312311. 
Anonymous
Not applicable
Hmmm - OK I think this might be another issue.

I was running in Trial mode with a test project + modules that the client provided. The module required opening and re-saving as Trial mode in order for the re-link to work.

Does the API require that the new relinked file is available, valid, and accessible? What about links in locked elements, how can they be dealt with?
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
doingsomethingwrong wrote:
Does the API require that the new relinked file is available, valid, and accessible?
Yes, APIDb_ModifyHotlinkNodeID will return APIERR_REFUSEDPAR if the given location is valid but not accessible.
Anonymous
Not applicable
OK - that makes sense.

Is there a way to update the hot links within a nested hot link in the master ArchiCAD file? Or do the hot links require updating per linked module.

Also, is there any way to provide user feedback via an alert or window? I can't seem to find that in the API (sorry, perhaps I'm not looking hard enough).
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
doingsomethingwrong wrote:
Also, is there any way to provide user feedback via an alert or window?
The easiest way is to use DGAlert or DGResAlert functions (see DG.h header file):
short	DGAlert	(short alertType,
		 const GS::UniString& titleText = GS::UniString(),
		 const GS::UniString& largeText = GS::UniString(),
		 const GS::UniString& smallText = GS::UniString(),
		 const GS::UniString& button1Text = GS::UniString(),
		 const GS::UniString& button2Text = GS::UniString(),
		 const GS::UniString& button3Text = GS::UniString());
The return value is the index of the clicked button. So if it returns 1, then the button1 was clicked.
Example:
if (DGAlert (DG_WARNING, "Warning", "Are you sure?", "Description.", "Yes", "No") == 1) {
	// 'Yes' was clicked
}

DGResAlert is the same, but the texts will come from the resources, so you can set them in your GRC file:
short	DGResAlert (GSResModule resModule, short alertId);
Example:
if (DGResAlert (ACAPI_GetOwnResModule (), 130) == 1) {
	// ...
}

// GRC:
'GALR' 130 Warning "Warning" {
/* largeText */		"Are you sure?"
/* smallText */		"Description."
/* button1   */		"Yes"
/* button2   */		"No"
/* button3   */		""
}
Learn and get certified!