Development with non Visual C++ nor Xcode

As described in section System Requirements, the preferred developer environment is Microsoft’s Visual C++ 2015 on Windows or Xcode 8.x on macOS, with both of them you can use C++14 features. All the libraries and header files are created for these systems.

We also provide a solution to move your development to other environments using a different type of communication with the API.

This document gives a detailed description of how you can build your add-on in developer environments other than described above. You can also take a look at the Dll_Test example.


Problems to solve

We assume that you are familiar with the basics of Archicad add-on development. If you are not, then please read the corresponding documentation before continuing with this text. It is also necessary to understand the usage of Dynamic Link Libraries.

There are two restrictions on developing add-ons for Archicad. First, the environment you use should be able to generate a Dynamic Link Library (Win) / bundle (macOS). The second requirement is that you need to be able to include the necessary resources into the created DLL/bundle documented in the Structure of an Add-On paper. If any of these requirements cannot be fulfilled you will not able to create an Archicad add-on.

 

Using the header files

There are several header files, which should be included into your source files. They are given in C++ format only.

It is very easy to compile your DLL/bundle if you develop your code in C or C++ language. In this case, you can use the header files found on here directly.

 

The communication library

There is a static library (ACAP_STAT.lib / libACAP_STAT.a) which should be statically linked to your compiled source files.

This library handles many things. First of all it controls all of the communication between Archicad and your add-on and it also owns the main entry point of your DLL/bundle.

If you are not able to link to this library, you are requested to make additional work to code these functionality. In order to make this work as easy as possible we also ship the library as a DLL/bundle named ACAP_DLL.apx (on both platforms).

This apx must be placed in the Add-Ons folder of Archicad. Archicad looks for it at launch time and loads into the memory if it is installed.

The working of Archicad add-ons that are compiled with the static library are similar to the add-ons which use this DLL/bunlde. The only difference is that the function calls that are going toward the Archicad engine are working via this communication DLL/bundle, instead of calling the running executable file directly (Figure 1).

Archicad calls

Figure 1: The communication between Archicad and an APX

 

This means that you have to handle the initialization and the termination of your add-on on your own. You are also required to get the needed Archicad function addresses from the communication DLL/bundle. If you have initiated the environment of your add-on correctly, then the further development and the working of your code will be the same as if you were linking to the static library.


The initialization and termination procedures

When Archicad calls your add-on, it will call three main initialization functions, so these functions should be exported from the APX file. In Microsoft’s Visual C++ environment the library which is given to the developers contains the needed code of these functions, but here you have to implement them.

 


// ---------------------------------------------------------------
// Pass the required entry points to the server application
// ---------------------------------------------------------------
ACAP_DLL_CLIENT void __ACDLL_CALL GetExportedFuncAddrs (GSPtr expList)
{
    ExpList_ACAP        *expPtr;
    BNZeroMemory (expList, sizeof(ExpList_ACAP));

    expPtr = (ExpList_ACAP *) expList;

    expPtr->version = (API_MAIN_VERSION << 16) | (API_RELEASE_VERSION & 0xFFFF);

    expPtr->checkenvironment  = CheckEnvironment;
    expPtr->registerinterface = RegisterInterface;
    expPtr->initialize        = My_Initialize;
    expPtr->freedata          = My_FreeData;

    expPtr->devkitNumber      = API_DEVKIT_NUMBER;

        return;
}       /* GetExportedFuncAddrs */


// ---------------------------------------------------------------
// Receive the callbacks from the server application
// ---------------------------------------------------------------
ACAP_DLL_CLIENT GSErrCode __ACDLL_CALL SetImportedFuncAddrs (GSPtr /*impList*/)
{
    return NoError;
}       /* SetImportedFuncAddrs */

// ---------------------------------------------------------------
// Called every time when the addon is loaded into the memory
// to execute a service
// ---------------------------------------------------------------
static GSErrCode __ACDLL_CALL My_Initialize (void* clientId)
{
    gOwnClientID = clientId;
    GSErrCode err = Initialize ();

    return err;
}       /* My_Initialize */


// ---------------------------------------------------------------
// Called every time before the addon is unloaded from the memory
// ---------------------------------------------------------------
static GSErrCode __ACDLL_CALL My_FreeData (void)
{
    GSErrCode err = FreeData ();

    if (gOwnResModule != nullptr) {
        gFunctionTable->closeownresfile (gOwnResModule);
        gOwnResModule = nullptr;
    }

    gOwnClientID = nullptr;
    if (gFunctionTable != nullptr) {
        delete gFunctionTable;
        gFunctionTable = nullptr;
    }

    return err;
}       /* My_FreeData */
Figure 2: The main initialization functions

 

These first two functions should be included in the add-on’s exports clause. Each time Archicad starts the add-on, it calls these functions irrespective of whether the add-on was kept in memory or not when it previously exited.

The gACAPIDLL handle can be used to identify the communication DLL/bundle when using the GetProcAddress function. This way you do not have to worry about the placement of the communication DLL/bundle. If this value is zero, then it means that Archicad could not find the communication DLL/bundle, and did not load it into memory. In this case your add-on can not run, and must exit.

In the GetExportedFuncAddrs function you have to add another 2 function pointers to the local Initialize (My_Initialize) an FreeData (My_FreeData) functions. Both of them are mandatory, they are called always in case of loading and freeing your dll library.

After calling these three initialization functions, Archicad calls the CheckEnvironment and RegisterInterface functions, the pointer of which was given back via the GetExportedFuncAddrs function.

After the main initialization functions remember to initialize the other resources of the applications. Finally when your add-on finishes its job remember to free up the memory from them.


Getting the function addresses

After the add-on has got the value of gACAPIDLL, it can get the addresses of the needed functions using a series of GetProcAddress functions. The name of the communication DLL/bundle is ACAP_DLL.APX. You do not have to care about version controlling. If you get the instance handle of it, you can be sure it is installed and implements the required version of communication.

The way of using an Archicad function is that you should define its type, create a variable of that type, set its value using the GetProcAddress function, and then you can make calls using it. A sample can be seen in Figure 3.


typedef GSErrCode  (__ACENV_CALL *acapi_attribute_getProc)
    (ClientID clientID, API_Attribute *attribute);

gFunctionTable->acapi_attribute_get =
    (acapi_attribute_getProc) GetProcAddress (gACAPIDLL, "ACAPI_Attribute_Get");
Figure 3: Getting an Archicad function’s address

 


Using resources

Your responsibility is the usage of correct resources in your Archicad add-on. Your development environment should have the ability to link the resource file into the DLL/bundle. To create the corresponding resource please consult the relevant API documentation.