C/C++ Development

The C API exposes all same functions that are exposed to the GXC language. Since it is much harder to use than the C++ API it is no longer recommended to make direct use of the C API.

The GX C++ API is a recent introduction and is simply a wrapper that hides the complexity of the C API functions behind a cleaner interface. It was developed to implement the GX Python API (9.9 reference) so the class and method naming follows the same conventions. The C++ class uses exceptions to report errors and also uses smart pointers and internal thread local storage geosoft handle to prevent having to pass this to all API calls.

The information below discusses how to implement C entry points into dlls. This is still the only way to get Oasis montaj to call into a C/C++ dll. See the Using the C++ API section on how to use it after the entry points are reached.

C and C++ include files, lib files and examples can be found in the gxcore repository.

Content

GX Based DLL Development

In order for your DLL code to interact via a GX, all of the functionality must be wrapped in a function which will be called within GXC code. The function must be exported from the DLL using the "C" calling convention in order to be accessible from within GXC. The first parameter in the exported function MUST BE the Geosoft handle, and it is the only required parameter. If your function is providing it's own user interface that will typically be all that is passed.

excerpt from callfunc.c
// In this example taken from the callfunc example the user interface 
// is being provided by the GX and all parameters are being passed in.  
// GX_OBJECT_PTR is a void pointer and is defined in geoengine.core.gxlib.h
__declspec(dllexport) 
long __cdecl iSum_CALLFUNC(       // returns sum of two numbers
		GX_OBJECT_PTR pGeo,  // geosoft handle 
 		const long *pl1,     // first number
		const long *pl2)     // second number


// Strings are a special case, you need to pass the length as well
__declspec(dllexport)
long __cdecl iStringFunction(
		GX_OBJECT_PTR pGeo,  // geosoft handle 
		char * myString,
        const long * pmyStringLen)


The exported function will not be visible to your GXC code without being declared either in a GXH file or within the GXC itself. The function declaration does not include the geosoft handle. Note also that the integer pointers expected by the DLL are declared simply as type int within the header. There are no pointers in the GXC language. Strings are a special case. The length of the string must always be passed along with the string.

Callfunc.gxh
//----------------------------------------------------------
// iSum_CALLFUNC    Return the sum of two numbers
[callfunc] // --- this is the name of the DLL (callfunc.dll) ---
int iSum_CALLFUNC( int,      // first number
                   int );    // second number

[MyDLL] // name of DLL containing iStringFunction
int  iStringFunction(	string,
						int); // length of string;

 The alternative to declaring the function in a GXH is to declare it within the GXC file. See the iMyFunction example below.

Finally, the last step is to call the function from within a GX. 

Error Handling

It is good practice for your DLL function to return a code to the calling GX indicating successful completion or an error code. For example if the user presses Cancel on your DLL controlled form it would be advantageous to capture that information in the GX so that the GX and any script calling the GX can be halted.

__declspec(dllexport)
long __cdecl iMyFunction(
		GX_OBJECT_PTR pGeo){
	const GX_RESULT_OK = 0;
	const GX_RESULT_CANCEL = 1;
	const GX_RESULT_ERROR = -1;
	
	if ( .. something that returns an error...) {
		return GX_RESULT_ERROR;
	} else if ( .. user pressed cancel...) {
		return GX_RESULT_CANCEL;
	} else {
		return GX_RESULT_OK;
	}	
}
MyFunction.gxc
//=================================================
NAME              ="MyFunction"
VERSION           ="v1.00  "
DESCRIPTION       ="Does something wonderful, I'm sure
"
//===========================================================================
#include <all.gxh>
[MyDLL] int iMyFunction();
//===========================================================================
{	
	switch  (iMyFunction()) {
		case -1:
			Abort_SYS();
		case 1:
			Cancel_SYS();
	}
}

NOTE: The actual effect of Cancel_SYS is to cause the GX to end without logging it in the event log or displaying an error message, which is sufficient in an interactive sense. However when the user cancels the execution of a GX during a script they generally want the script to end as well. This can be accomplished by beginning the script with the "CANCEL_AS_ERROR ON" command as the first item. Terminating your GX with Abort_SYS will always terminate a script and show an error message.

Using the C++ API

To use the C++ API one has to simply construct a single GXContext object to wrap the pGeo pointer and maintain it in thread local storage for the duration of the code that will uses the API. To illustrate here is a simple example that uses the C++ AP to open a GDB with its filename (it is assumed that a GX was written to browse for the file and pass it into the entry point):

excerpt from callfunc.c
using namespace geosoft::gx::geogx;


__declspec(dllexport) 
long __cdecl iProcessDB(GX_OBJECT_PTR pGeo, const char *file)

{
	auto ctx = GXContext::create_internal(pGeo);

	try
	{
		auto db = GXDB.open(name, 'SUPER', '');
		// Do stuff with it
	}
	catch (GXCancel&)
	{
		return 1;
	}
	catch (GXExit &)
	{
		return 0;
	}
	catch (GXError & e)
	{
		MessageBox(NULL, e.message(), _L("GXError"), MB_OK | MB_ICONERROR));
		return -1;
	}
	catch (GXAPIError &api_e)
	{
		MessageBox(NULL, api_e.message(), _L("GXError"), MB_OK | MB_ICONERROR));
		return 0;
	}

The exception handlers shown are all required since unhandled exceptions will lead to Oasis montaj crashing. The way the exceptions are handled can change of course. Depending on your code, you might need to handle other C++ exceptions. Beware of using GX C++ API calls that might throw inside exception handlers.

Installing your custom DLL

Third party applications are usually placed in the <geosoft>\bin or the <geosoft>\resourcefiles\bin directory but can be placed in a different location if desired.  A registry setting is required to locate each DLL that is not placed in a \bin directory.  Add the following registry key to the system running the application:

HKEY_LOCAL_MACHINE\SOFTWARE\Geosoft\{KEY}\3rdPartyDLLs.

The {KEY} is the version of Oasis montaj that will be running. In the standard Oasis montaj version this key is “Oasis montaj” and for the Viewer it is “Oasis montaj Viewer”. However, some special versions of Oasis montaj for our CS customers do have unique key names. Note that in this case, version doesn't refer to the version number, but rather the name of the product.

Once this key is created, populate it with the names of each DLL needing to be redirected to a different location.  For example, if a custom GX calls a method in my geocustom.dll file installed in c:\program files\custom, I would create a new string in the registry:

 Name: geocustom.dll

Data: <Program Files>\custom\geocustom.dll

The first time your GX is run, Oasis montaj will try to load the DLL from <geosoft>\bin first.  If it is not found there, it will check this registry setting for any matches of the DLL name and load it from the specified path instead.  Until Oasis montaj has been restarted, the DLL will always be loaded from the alternate location. 

Connecting your code to the Geosoft environment

There are several different ways of connecting your code to the Geosoft environment of your choice:

  • GX based - In this scenario, which is the simplest, you create a GX which calls an exported function or functions in your DLL. That function receives a "handle" to the Geosoft object which is necessary to interact with the API functions. Upon completion, the DLL function returns control to the GX. In this scenario your function behaves similarly to an API function, in that its lifespan is limited to the GX execution.
  • .NET based - A function exported from a .NET assembly can be called directly from a menu item without wrapping it in a GX, but it is otherwise the same as above.
  • External application - A standalone program can be built which creates a separate instance of the Geosoft executable in memory and accesses databases or maps. Some parts of the API are not available using this method. Furthermore the external application can not interact with a running instance of the Geosoft program. However, a GX can be created to prepare your Geosoft project so that it can be opened and processed by the external application, which can be run using the GX iShellExecute_SYS() method. The Python package also allows you to create fully 64-bit external applications that access the GX API.
  • GeoXTool application - This is a DLL based program that becomes part of the running instance of Geosoft. A GeoXTool is able to interact with the program and 'react' to user actions such as changing line, selecting data or zooming/ panning a map. This type of tool is most difficult to create but very powerful.

GeoXTool Application

A GeoXTool is a DLL based application that integrates itself into the environment. Once launched from a GX, the process stays resident and responds to events within the environment. If the tool has a user interface, the exposed form will become part of the sidebar or will be displayed as stay-on-top if undocked. Currently the C and GXNET APIs may be used to create a GeoXTOOL, and examples are provided of each.

Several steps are necessary to create a GeoXTool.

  • Create an exported function that initializes the GeoXTool.
  • Create an object that IS the GeoXTool. This object can respond to the events in the GeoXTool Interface and can trigger events in the GeoXTool API via callbacks. Typically it will display a form that gets parented (i.e. embedded into) the sidebar. Once initialized, the interface method callbacks will be called whenever appropriate. The API callback functions can be triggered by user actions or within the interface methods.
  • Create a GX to launch the GeoXTool.

Creating a GeoXTool

For C developers all of the function prototypes are provided in "geoxtool.h". All of the functions in the API and Interface need to be separately created and assigned to their respective callbacks during the creation of the tool. The GeoXTool MFC example is a complete example.

from MyGeoXTool.cpp
extern "C" __declspec(dllexport) void * _cdecl
hCreate_GEOXTOOL(void *hGEO,                    // Geosoft Handle
                 HWND hParent,                  // Parent Window
                 long lMETA,                    // META object to get info from
                 HWND *phWND,                   // Filled with new HWND of your tool
           const GEOXTOOL_API       *pAPI,      // API Functions
                 GEOXTOOL_INTERFACE *pFuncs,    // Filled with function pointers
                 char *pcToolName,              // Buffer for the name of the tool
                 long lToolNameSize)            // Size of Tool Name Buffer

For .Net developers the process is somewhat simpler. By creating a control descending from ToolControl most of the plumbing is already taken care of. ToolControl is implemented in the geoengine.core.gxnetx assembly

GeoXTool API

This object can respond to the following events in the environment:

    1. GeneralInfo
    2. ChangeArea
    3. ChangeLocation
    4. ChangeProjection