Native external functions: Capabilities

Different versions of OrcaFlex have different capabilities and features. When OrcaFlex communicates with an external function DLL it needs to know what level of functionality that DLL can support.

For example, OrcaFlex 9.5 introduced support for Unicode text. In order for an external function DLL to take advantage of this feature, the interface between OrcaFlex and the external function needs to be modified. If OrcaFlex used the new Unicode interface for all communication with external functions, then older external functions would no longer function and would have to be re-compiled. In order to avoid this undesirable scenario, OrcaFlex allows external function DLLs to opt-in to new functionality.

In order to opt-in to optional functionality, an external function DLL must export a function called RegisterCapabilities. In C++ the function would be declared as:

void __stdcall RegisterCapabilities(LPDWORD lpCapabilities);

In Delphi the function would be declared as:

procedure RegisterCapabilities(var Capabilities: DWORD); stdcall;

Note that since the function is identified solely by its name, only a single RegisterCapabilities function can be exported per external function DLL. This means that if multiple external functions are exported from a single DLL, then those external functions must support the same capabilities. Should you wish to implement functions with different capabilities, then multiple external function DLLs would be required.

If this function is not exported from the DLL, then OrcaFlex assumes that none of the optional capabilities are supported.

Parameters

lpCapabilities (IN, OUT)

Points to an integer in which the DLL's capabilities flags are returned. On entry this value is set to 0 which indicates that none of the optional capabilities are supported.

The DLL can indicate support for the optional capabilities by adding combinations of the following flags:

Value Meaning
efcUnicode
0x00000001
Specifies that the DLL's external functions support, and wish to use, Unicode text. When this flag is included, external functions will be passed TExtFnInfoW. Otherwise, TExtFnInfoA is passed.
efcImplicitIntegration
0x00000002
Specifies that the DLL's external functions support implicit integration. If this flag is not included, the external function will be restricted to simulations which use explicit integration.

Remarks

Unicode

Including the efcUnicode capability results in your external functions receiving TExtFnInfoW rather than TExtFnInfoA.

The OrcFxAPI header file defines TExtFnInfo as an alias to either TExtFnInfoW or TExtFnInfoA, based on the value of the UNICODE conditional. The recommended approach is include efcUnicode if the the UNICODE conditional, as demonstrated in the sample implementation below. This allows you to use the alias TExtFnInfo in your code and let the compiler choose the appropriate concrete type.

You are at liberty to call Unicode OrcFxAPI functions from a non-Unicode external function. However, it would be quite confusing to do so, and so this practice is generally to be avoided.

Implicit integration

Versions of OrcaFlex prior to 9.5 did not allow external functions to be used in conjunction with implicit integration. Many external functions that were written under the assumption that explicit integration will be used will not work under implicit integration. The most common problem will be for external function that perform some form of time integration, e.g. a PID controller. For more details of this issue please refer to the NewTimeStep field of TExtFnInfo.

Hence we took the decision to force external function authors to declare that their functions supported implicit integration. By doing so we hope to raise awareness that some external functions will require re-work in order to be compatible with implicit integration.

Sample C++ implementation

void __stdcall RegisterCapabilities(LPDWORD lpCapabilities)

{

#ifdef UNICODE

*lpCapabilities = *lpCapabilities | efcUnicode;

#endif

*lpCapabilities = *lpCapabilities | efcImplicitIntegration;

}

In C++, the UNICODE conditional is typically set as part of the project build configuration.

Sample Delphi implementation

procedure RegisterCapabilities(var Capabilities: DWORD); stdcall;

begin

{$IFDEF UNICODE}

Capabilities := Capabilities or efcUnicode;

{$ENDIF}

Capabilities := Capabilities or efcImplicitIntegration;

end;

In Delphi, the UNICODE conditional is determined by the compiler version. It is defined for Delphi 2009 and later, and not defined for earlier versions. Basing the Unicode capability on this conditional allows you to write an external function that can be compiled with different versions of Delphi.

See also

Native External Functions.