Differences for Synergy .NET

This topic, along with Features that are not supported for Synergy .NET development, lists ways in which Synergy DBL support for .NET differs from traditional Synergy development. For information on differences for .NET 6 and higher and .NET Standard, see Differences for SDK-style projects.

Runtime

Synergy .NET assemblies run under a .NET CLR rather than the Synergy Runtime, but some Synergy runtime functionality is required for Synergy .NET. This is supplied by runtime libraries that are required and are automatically referenced in Synergy .NET projects. See Synergy runtime and build libraries for more information.

Building (compiling)

With Synergy .NET, you use MSBuild (e.g., via Visual Studio build features) to create assemblies from Synergy projects. (Running the Synergy .NET compiler directly from the command line is not supported.) Note the following:

You can use the “Generate warning when stack size exceeds # bytes” option on the Compile page of Visual Studio project properties to detect if the total stack data used by a routine exceeds a given size. For more information, see Compile page of Visual Studio project properties.

Debugging

For Windows development, Synergy .NET uses the Visual Studio .NET debugger (rather than the Synergy debugger). See Debugging Synergy .NET Code for more information on limitations and special features for Synergy .NET.

Data types

Arrays

You cannot declare a real array of a .NET type (e.g., [10]D_ADDR or [10]Int) because with .NET, D_ADDR is shorthand for System.IntPtr, and Int is shorthand for System.Int32. Instead use either an array of i4 for D_ADDR on 32-bit, i8 for D_ADDR on 64-bit, or a dynamic array ([#]int or [#]D_ADDR).

When passing a Synergy real array, the number of ranks must match the argument definition in the called routine.

Passing a non-arrayed field to a pseudo array argument — i.e., an argument defined with (*) — passes a single dimension array of one element. With traditional Synergy, you can do this if -qcheck is not used, and you can subscript beyond the end of the field. This is not possible with Synergy .NET because of strong bounds checking (which operates as if -qcheck were specified with traditional Synergy). And with Synergy .NET, passing ^M(field, data_area) to a pseudo array or real array argument will result in an array of field whose dimension is determined by the number of these fields that will fit in the memory area.

Data type identifiers

Handles for ^M should be defined using D_HANDLE. D_ADDR is not supported for use with ^M or arguments to functions that take a handle.

Decimal

A decimal assignment to an integer or unsigned integer derivative will cause a BIGNUM error if the decimal value exceeds the maximum for a 64-bit int.

Covariant return types

When a parent class method is overridden, the signature for the derived class method must match the signature for the parent class method that’s being overridden, except in .NET 6 and higher where covariant types are supported. For information on covariant return types, see Overriding a method.

Integer

For optimization, integer fields (which are usually descriptor types) are in many cases converted to native .NET data types (value types):

Generally, these conversions are seamless; there’s no need to consider them as you code. They can, however, cause problems if you rely on automatic boxing or unboxing. For example, the following code (which works in traditional Synergy) won’t work with Synergy .NET because casting ivar as (object) results in an @int, which can’t be unboxed to an (@i4). (You can’t unbox one type to another.)

record
num             ,@object
ivar            ,i4
proc
num=(object)ivar                ;Results in an @int
ivar=(i4)num                    ;Attempts to unbox the @int to an (@i4)

To prevent this, force the data type as you box/unbox to ensure you use the same type. For example, the above would work for both traditional Synergy and Synergy .NET if ivar was explicitly boxed using (@i4):

num=(@i4)ivar

or unboxed using (int):

ivar=(int)num

Additionally, note the following:

Literals

Types for literals (and literals cast as object types or passed to parameters of object types) are changed from Synergy literal types to corresponding .NET literal types. For example, “abc” is type string, and 10 is int or @int. If you want Synergy literal types, cast the literal as the desired Synergy type (@a or @i).

Objects and value types

Objects and certain .NET value types (such as IntPtr) in named entities (structures and records) are automatically aligned on native boundaries for .NET CLR requirements. This causes an automatic align warning to notify of the implicit alignment of such fields. Use .ALIGN to suppress this warning. Additionally, structures that contain alignable types are padded to a multiple of the highest alignment size for use in arrays. A warning is reported when this occurs (WALIGN, “Align warning: structure padded because of alignment”). To avoid this, add a filler.

Overloading

Overloading by using a BYVAL parameter and a BYREF parameter of the same type is not supported.

Arguments cannot be overloaded, so passing a d. value to a d argument results in the d argument accessing a rounded whole value. Use ^D to correctly cast such variables.

Parameters

Note the following when passing parameters:

If you do not explicitly use ^DATATYPE and ^A and want to pass an alpha to a routine n argument, change the call to use ^D() instead of making the routine MISMATCH n.

String

A new String() cannot take an alpha argument in Synergy .NET. Instead use stringvar = “abc”.

Structfields

Numeric types cannot be assigned to structfields. Attempting to do so results in a NETALLOW error during compilation.

Destructors

Destructors are nondeterministic on .NET. Order and timing are at the discretion of garbage collection, and they may even execute after a STOP statement.

Directives

Some directives are not supported for Synergy .NET. See Directives.

.INCLUDE

Repository field names with prefixes (created by the PREFIX qualifier) are not truncated. In traditional Synergy, a repository field name is truncated if it is longer than 30 characters.

Boxing and unboxing

If you have System.Object=@d, you can unbox the object only to a d, and you must unbox it explicitly. The object cannot be automatically unboxed because the compiler cannot detect its type.

Boxed types are automatically unboxed only when a boxed type argument is passed to an unboxed type parameter or to a boxed type assigned to an unboxed type. The types must match or must both be integer or numeric, and the boxed variable must be explicitly typed. In all other cases, you must explicitly cast the variable to unbox it. (With traditional Synergy, several circumstances result in automatic unboxing.) For more information, see Boxing.

Exception handling

If an exception is thrown by a method called by XSUBR, and the exception is caught in a TRY-CATCH block in the calling method, the caught method will not have the same type as the original exception thrown in the called method. Instead, it will have the type System.Reflection.TargetInvocationException. This type includes the original exception as the InnerException property and is necessary to preserve stack trace information. (ONERROR processing is different: with ONERROR, the error number is preserved.)

Note the following:

Memory

Unlike traditional Synergy, Synergy .NET uses garbage collection for nondeterministic destruction of objects. For information on emulating deterministic destruction of objects, which may be necessary with resource-intensive objects (e.g., large Synergy arrays and Select objects), see Microsoft’s documentation on implementing the Dispose pattern.

Structures, records, and fields

If your application uses global commons, global data sections, or public class fields that are accessed across assemblies, whenever one of those elements changes, you must recompile all projects that reference the assembly containing the element. We recommend that you use assembly versioning on your dependent projects as well. (Traditional Synergy does not have this problem because names are resolved at runtime, not at build time.)

Structures

Synergy .NET supports both Synergy structures and .NET structures. (Synergy structures are defined using STRUCTURE statements, and .NET structures are defined using CLS STRUCTURE statements.) Unlike Synergy structures, .NET structures are compatible with C# and other .NET languages within the public namespace. However, .NET structures cannot contain descriptor types or have overlays, and they cannot be used with ^M, passed as alpha arguments, or declared as real arrays (only dynamic arrays).

You cannot use a local structure to define a structfield in a global data section. With Synergy .NET, global data sections and commons are true global entities, and only global structures can be used to define structfields.

Records and fields

Note the following for records and fields:

Statements

TRY-CATCH and ONERROR

An exception from XCALL EXITE or a runtime-signaled error can be caught by TRY-CATCH in the current or previous routine, or by ONERROR in any prior routine. (With traditional Synergy, an XCALL EXITE always transfers control to a prior routine and cannot cause a program to stop with a fatal error.)

ACCEPT, GETS, and READS

With .NET Framework and Windows-specific .NET versions of .NET 6 and higher (e.g., when targeting “.NET 6.0 Windows”), terminal channel (TT:) functionality for ACCEPT, GETS, and READS is supported only for console applications. And with these .NET versions, these routines do not accept characters from applications that use the Synergy Windowing API. Instead use WD_ACCEPT, WD_GETS, and WD_READS.

FOREACH

Note the following when using FOREACH for .NET development:

FOREACH loop_var in collection [AS type]

READ

An optional subroutine argument that is omitted cannot subsequently be used as the key_spec argument to a READ statement.

RETURN

A call to RETURN behaves as a call to XRETURN if there are no more items on the call stack, regardless of whether a CALL has occurred. (In traditional Synergy, a RETURN behaves as an XRETURN if at least one CALL has occurred and causes a NOCALL runtime error if there has been no prior CALL.)

STOP chaining

With .NET, when chaining to a program with a STOP statement there is a delay when the program is started, and any on-screen data is cleared and re-created, which may cause flicker. This is a .NET limitation. We do not recommend using STOP to chain with Synergy .NET.

USING and CASE

USING and CASE operate as if the -qnoargnopt option were specified in traditional Synergy: numeric types are honored, string control variables in a USING statement cause string comparisons, and match labels for USING ranges are not rounded to whole numbers.

Subroutines and functions

With Synergy .NET, subroutines cannot be called as functions. To work around this, convert the subroutine to a ^VAL function.

^ARG* routines

We strongly recommend against using the ^ARG* routines with declared arguments because of the high overhead they incur.

^D and ^I

Using ^D or ^I on null alpha literals or intermediate results returned from %ATRIM correctly generates a NULARG error because these types cannot have a length of 0.

%ERLIN, ERRMOD, %ERROR, and MODNAME

%ERLIN, ERRMOD, %ERROR, and MODNAME have limited line number support, and the file_number argument for MODNAME is always returned as 0 with Synergy .NET.

%NUMARGS

%NUMARGS returns the number of the last passed argument, which can be different than the return value in traditional Synergy if there are optional arguments. For example, if a subroutine called mysub has three optional arguments, %NUMARGS will return 2 for both traditional Synergy and Synergy .NET for this example:

xcall mysub(arg1, arg2)

But for the following, it will return 3 for traditional Synergy and 2 for Synergy .NET:

xcall mysub(arg1, arg2, )

And for the following, if mysub has one optional argument, %NUMARGS will return 1 for traditional Synergy but 0 for Synergy .NET:

xcall mysub()

OPENELB, %XADDR, and XSUBR

In traditional Synergy, ELBs linked to an executable (.dbr) are automatically loaded when the executable is run, and ELBs linked to a loaded ELB are automatically loaded. This behavior enables %XADDR and XSUBR to work. With .NET, however, referencing an assembly does not cause the assembly to be loaded. And the .NET method Assembly.Load does not always work with %XADDR or XSUBR. To load an assembly, use either OPENELB or add method calls to the referenced assembly. Note the following:

%TNMBR

%TNMBR (which is deprecated) always returns either the environment variable TNMBR or -1.

XSTAT

XSTAT is for use only with SHELL and SPAWN.

APIs

Synergy DLL API

We recommend you use the .NET DllImport attribute instead of %DLL_NETCALL or %DLL_CALL.

Synergy XML API

To use the XML API for .NET Framework development, you must add a reference to Synergex.SynergyDE.synxml.dll. For SDK-style projects, use the Synergex.SynergyDE.synxml NuGet package.

Synergy windowing API

The Unix-compatible (non-mouse) functionality of the Synergy windowing API for traditional Synergy is fully supported. You can set the SYN_RESIZE_SCALE environment variable to 1 to make the application window resizable and maximizable. However, see APIs for information on differences in support for SDK-style projects.

Synergy socket API and HTTP document transport API

You should explicitly close channels and sockets used by these APIs and free global memory handles. Do not assume that shutting down the program or AppDomain will do this.

Repository subroutine library

To use the Repository subroutine library (the DD_ routines) for .NET Framework development, you must add a reference to Synergex.SynergyDE.ddlib.dll. For SDK-style projects, use the Synergex.SynergyDE.ddlib NuGet package.

Environment variables and initialization files

Initialization files and some environment variables can be used for .NET development, and in some cases they can be used for runtime settings. See Environment variables and Visual Studio development for more information, and see Environment variables for a list of unsupported environment variables.