Embedding ODTK With C# and C++

In addition to invoking a full copy of ODTK application, you can now use a slimmed down version of the ODTK.Engine, but you still have the responsibility to initialize most of its environment yourself. The following examples show how to integrate the ODTK Engine into C# or C++ applications. Please contact AGI Technical Support for any additional questions.

C# Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ODTKEngLib;
namespace ODTKEngTestCS
{
    class Program
    {
        static void Main(string[] args)
        {
            Type oType = Type.GetTypeFromProgID("ODTK7.Engine");
            //Type oType = Type.GetTypeFromProgID("ODTK7.Automation");
            dynamic eng1 = Activator.CreateInstance(oType);
            eng1.Visible = false;
            eng1.UserControl = false;
			eng1.PrefFilesMode = AgEPrefFilesMode.ePrefFiles_NoLoad_NoSave;
            eng1.OpenLogFile(@"c:\temp\logCS.txt", AgEOpenLogFileMode.eOpenLogFileForWriting);
            eng1.LogMsg(AgEUiLogMsgType.eUiLogMsgInfo, "Hi");
            
            eng1.LoadPersonality("ODTK.Engine");
            dynamic ODTK = eng1.Personality;
            string userPath = ODTK.ApplicationPathUserData.value;
            dynamic ne = ODTK.Application.TrackingDataFileTypes.NewElem();
                ne.Extensions = "geosc;geos;dat";
                ne.PluginID   = "IAgODProvideTrackingData7.AgODProvideGeoscTrackingData";
            ODTK.Application.TrackingDataFileTypes.insert(ne);
                ne.Extensions = "gobs;s";
                ne.PluginID   = "IAgODProvideTrackingData7.AgODProvideGenericTrackingData";
            ODTK.Application.TrackingDataFileTypes.insert(ne);
            
            dynamic res = (ODTK.Application.LoadObject("", @"c:\temp\Scenario1.sco")).Value;
            if (res == true)
            {
                dynamic scen = ODTK.Children.Scenario.Item(0);
                string scName = scen.name.Value;
                eng1.LogMsg(AgEUiLogMsgType.eUiLogMsgDebug, "scenName = " + scName);

                dynamic fil = scen.Filter.Item(0);
                dynamic res2 = fil.Go();
                eng1.LogMsg(AgEUiLogMsgType.eUiLogMsgDebug, "filter.Go() returned = " + res2.value);
                ODTK.Application.DeleteObject(scen);
            }
        }
    }
}

C++ Example

#define _CRT_SECURE_NO_WARNINGS
#include 
#define _AFXDLL
#include 
#include 
#include 
#include 
#include 

#import "C:\Program Files\AGI\ODTK 7\bin\ODTKEng.dll"
int lAgFpieeeHandler(_FPIEEE_RECORD *pieee);
#define X(expr) if( !(SUCCEEDED(expr)) ) exit(__LINE__)
#define GetDD(DD,name,propDD) { VARIANT v; X(DD.GetPropertyByName(name,&v)); propDD.p = v.pdispVal; }
#define GetVal(DD,name,propV) { CComDispatchDriver propDD; GetDD(DD,name,propDD); X(propDD.GetPropertyByName(L"value",propV)); }
#define SetVal(DD,name,propV) { CComVariant vv(propV); X(DD.PutPropertyByName(name,&vv));	}
#define ConvertRetVal(retVar) { CComDispatchDriver propDD; propDD.p = retVar.pdispVal; retVar.vt = VT_EMPTY; X(propDD.GetPropertyByName(L"value",&retVar)); }
#define Iv0( DD,funcName) 	  { DISPID funcID; X(DD.GetIDOfName(funcName,&funcID)); X(DD.Invoke0(funcID)); }
#define Iv0r(DD,funcName,ret) { DISPID funcID; X(DD.GetIDOfName(funcName,&funcID));	X(DD.Invoke0(funcID,ret)); }
#define Iv1( DD,funcName,a1)   { DISPID funcID; CComVariant v1(a1); X(DD.GetIDOfName(funcName,&funcID)); X(DD.Invoke1(funcID,&v1)); }
#define Iv1r(DD,funcName,a1,r) { DISPID funcID; CComVariant v1(a1); X(DD.GetIDOfName(funcName,&funcID)); X(DD.Invoke1(funcID,&v1,r)); }
#define Iv2(DD,funcName,arg1,arg2) 	\
	{DISPID funcID;	CComVariant varg1(arg1), varg2(arg2);\
	X(DD.GetIDOfName(funcName,&funcID));\
	X(DD.Invoke2(funcID,&varg1,&varg2));\
	}
#define GetItem(DD,scopeName,i,itemDD) \
	{ CComDispatchDriver scopeDD;\
	  GetDD(DD,scopeName,scopeDD);\
	  VARIANT v; Iv1r(scopeDD, L"Item",i,&v); itemDD.p = v.pdispVal; }

void run(void)
{
	char str[128];
	CComVariant v1;
	CComBSTR z, z2, z3;
	ODTKEngLib::IAgODTKEnginePtr eng1;
	CComDispatchDriver persDD, appDD, appChDD, scenDD, simDD, filDD, trkDD, neDD, rDD;
	X(CoInitialize(0));
	X(eng1.CreateInstance("ODTK7.Engine"));
 	//X(eng1->put_LoggingMode(eLogActive));
	X(eng1->raw_OpenLogFile(L"c:\\temp\\log1.txt", ODTKEngLib::eOpenLogFileForWriting,&v1.boolVal));
	eng1->raw_GetLicenseHostID(&z);
	eng1->get_Version(&z2);
	eng1->get_LogFile(&z3);
	X(eng1->raw_LogMsg(ODTKEngLib::eUiLogMsgDebug,z));
	X(eng1->get_Personality(&persDD.p));
	GetDD(persDD, L"Application", appDD );
	GetDD(persDD, L"Children",	  appChDD );
	GetDD(appDD,  L"TrackingDataFileTypes", trkDD );
	Iv0r(trkDD,L"NewElem",&v1); neDD = v1.pdispVal;
		SetVal(neDD,L"Extensions",L"geosc;geos;dat");
		SetVal(neDD,L"PluginID",L"IAgODProvideTrackingData7.AgODProvideGeoscTrackingData");
	Iv1(trkDD, L"insert", CComVariant(neDD.p));
		SetVal(neDD, L"Extensions",L"gobs;s");
		SetVal(neDD, L"PluginID",  L"IAgODProvideTrackingData7.AgODProvideGenericTrackingData");
	Iv1(trkDD, L"insert", CComVariant(neDD.p));
	//
	//  Assuming scenario has ready to run Simulator and Filter objects
	//
	Iv2(appDD,L"LoadObject",L"",L"c:\\temp\\Scenario1.sco");
	GetVal(appChDD, L"size", &v1 ); 	sprintf_s(str,"#scenario loaded = %d", v1.intVal);	X(eng1->LogMsg(ODTKEngLib::eUiLogMsgDebug,str));
	GetItem(appChDD,L"Scenario",  0, scenDD);
	GetItem(scenDD, L"Simulator", 0, simDD);
	GetItem(scenDD, L"Filter",    0, filDD);
	Iv0r(simDD, L"Go", &v1);	ConvertRetVal(v1);		sprintf_s(str, "# simulator run successfully = %s", v1.boolVal != FALSE ? "true" : "false");	X(eng1->LogMsg(ODTKEngLib::eUiLogMsgDebug, str));
	Iv0r(filDD, L"Go", &v1);	ConvertRetVal(v1);		sprintf_s(str,    "# filter run successfully = %s", v1.boolVal != FALSE ? "true" : "false");	X(eng1->LogMsg(ODTKEngLib::eUiLogMsgDebug, str));
}
int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR lpCmdLine, int nCmdShow)
{
	// By default all exceptions are masked.  Value of the environment 
	// variable determines which ones will be unmasked based on the 
	// exception masks defined in float.h.  _control87 is used rather 
	// than _controlfp because it allows us to control the denormal 
	// exceptions.
	//
	// for example, you could set the environment variable to any 
	// combination of the following.  A typical value to trap 
	// zero divide or invalid numbers would be 0x18.
	// 
	//	_EM_INEXACT    = 0x00000001 
	//	_EM_UNDERFLOW  = 0x00000002 
	//	_EM_OVERFLOW   = 0x00000004 
	//	_EM_ZERODIVIDE = 0x00000008 
	//	_EM_INVALID    = 0x00000010 
	//  _EM_DENORMAL   = 0x00080000
	unsigned int flag = 0x18;
	char *flagStr = getenv("AGI_FLOATING_POINT_EXCEPTIONS");
	if (flagStr != NULL)
	{
		sscanf(flagStr, "%i", &flag);
	}
	// limit the possible values so you can't really
	// screw up the FP processor
	flag &= 0x0008001f;
	int cw = _control87( 0,0 );
	_control87( cw & ~flag, MCW_EM );
	SetProcessAffinityMask(GetCurrentProcess(), 0);//m_nProcAffinityMsk);
	__try
	{
		run();
	}
	__except((_fpieee_flt(GetExceptionCode(), GetExceptionInformation(), lAgFpieeeHandler)))
	{
	}
	
	return 0;
}    

int lAgFpieeeHandler(_FPIEEE_RECORD *pieee)
{
	_clearfp();
	char *sMessage = "Floating point arithmetic error.";
	if(pieee->Cause.ZeroDivide)
	{
		sMessage = "Division by zero.";
	}
	else if(pieee->Cause.InvalidOperation)
	{
		sMessage = "Invalid floating point operation.";
	}
	else if (pieee->Cause.Inexact)
	{
		sMessage = "Inexact result.";
	}
	else if (pieee->Cause.Underflow)
	{
		sMessage = "Underflow occurred.";
	}
	else if (pieee->Cause.Overflow)
	{
		sMessage = "Overflow occurred.";
	}
	else
	{
		sMessage = "An unknown floating point error occurred.";
	}
	fprintf(stderr, "FP exception caught: %s", sMessage);
	return EXCEPTION_CONTINUE_EXECUTION;
}