Skip to main content

Issues & Workarounds

In this section we list and describe the most common problems that we encounter during porting projects.

No matter how good the porting procedure is, there are some fundamental differences between CTD and .NET that have to be addressed manually.

Format Strings​

In .NET custom numeric format strings, the period and comma are format tokens; the output separators come from the selected culture. This is not a requirement to display numbers using the invariant culture. Ice Porter automatically converts SAL format strings to .NET compliant format strings when porting controls. However, it is impossible for Ice Porter to determine if it´s a string constant or is used as a format string and it is also impossible to convert localized symbols at runtime.

If your application is using localized format strings you have to change them by hand (or write a custom function that changes the strings at runtime) because you only know the locale that was used for those strings. .NET will localize the invariant format strings at runtime using Windows settings or the locale that is configured programmatically for the running application.

For example:

Sal.FmtFormatNumber(nValue, "#.##0,00");

Is invalid and should be changed manually to:

Sal.FmtFormatNumber(nValue, "#,##0.00");

SalCompileAndEvaluate Expressions​

Expressions that are executed by SalCompileAndEvaluate are string and are written in the original application using SAL syntax.

However the built-in interpreter in the PPJ Framework uses C# syntax. As a result some expressions may need to be fixed manually. In alternative we can provide a plug-in custom parser that supports SAL syntax.

For example:

Call SalCompileAndEvaluate('SalMessageBox("Hello, World", "", 0)')

This excerpt illustrates a legacy interpreted call, not the complete CompileAndEvaluate signature. Simple calls and member expressions may be supported through the parser's compatibility rules, but must still resolve in the active context. Review their function names, arguments, and returned types.

What needs to be changed are the boolean operators, operators precedence and string concatenation.

SAL SyntaxC# Syntax
n = 1n == 1
s || "test"s + "test"
b1 AND b2b1 && b2
b1 OR b2b1 || b2
NOT b1!b1

One particular tricky issue is the difference between the single equal symbol in SAL and the double equal symbol in C#. In Team Developer an expression like "n = 1" is interpreted to test the value of "n" against 1 and return TRUE or FALSE. In C# it's executed as an inline assignment and 1 is assigned to n.

The same expression in C# should be "n == 1". The problem is that the original expression is legal, but it's executed differently.

For this reason, inline assignments are disabled by default and executing an assignment in SalCompileAndEvaluate() without the keyword "Set" causes an exception.

If you are sure that all your interpreted expressions are safe in this respect, you can enable inline assignments in the PPJ Framework by using:

PPJ.Runtime.Scripting.ScriptEngine.AllowInlineAssignments = true;

The PPJ Framework allows for the substitution of the parser. The default parser uses C#-style syntax; another syntax requires a compatible parser implementation. See Custom Parsers.

Number/Boolean Type Mismatch​

SAL permits numeric/Boolean uses that ordinary C# int/bool declarations would reject. That does not make every numeric value an appropriate Boolean result; preserve the source operation's intended meaning.

After porting, the new code will use either SalNumber or SalBoolean and the two are also compatible. However, when there is a receive parameter of type SalNumber, you cannot pass a SalBoolean type, or vice versa. This kind of mismatch causes a compiler error and must be resolved by hand.

Handle Type Mismatch​

It's common to find Window handles, File handles, Sql handles and Session handles mixed and misused.

In Team Developer there was really no difference between the handles and one could assign the various types to each other without even a warning.

After porting, the wrong assignments will generate compiler errors and have to be fixed by hand.

VisMenu Functions​

All the VisMenu functions are supported in the PPJ.Runtime.Vis library.

However, some functions have an added parameter that was missing in the Team Developer library.

This change causes compilation errors that have to be fixed by hand. The missing parameter is the form that owns the menu.

The VisMenu functions with the added parameter are:

  • Vis.MenuCheck
  • Vis.MenuDisabl
  • Vis.MenuEnable
  • Vis.MenuGetCount
  • Vis.MenuGetPopupHandle
  • Vis.MenuIsChecked
  • Vis.MenuIsEnabled
  • Vis.MenuUncheck

COM Properties​

Parameterized COM properties may not map directly to ordinary C# properties. Inspect the generated interop signature and use its generated accessor methods or indexer where available. Reflection or a wrapper may be needed for legacy declarations; test optional and receive arguments.

External Functions with Receive Strings​

Strings in older ANSI Team Developer applications use an ANSI encoding and it is possible that an external custom DLL alters the content of the passed string addressing it like a simple text buffer.

Managed strings use UTF-16, while the native boundary still uses the declaration's encoding and buffer contract. An input string is not a writable receive buffer. Match the native output parameter, capacity, length units, and marshalling attributes; merely changing its character set or adding a receive keyword cannot repair an incorrect native signature.

.NET Objects Created as COM Objects​

If the source application accesses a .NET component through COM, inspect its COM registration, visibility, runtime, and bitness. Such components do not inherently fail just because the caller is now .NET. Direct managed references may simplify deployment and remove an unnecessary interop boundary.

In this case you can either change the code to use the .NET class directly, or simply change the internals of the COM wrappers created by Ice Porter from using COM interfaces to using the .NET class directly.

WM_NCCREATE​

Some Team Developer applications use WM_NCCREATE to change the style of the window using SetWindowLong(GWL_STYLE) and to remove the frame.

Usually this is done to create splash windows. This technique will not work in .NET because the .NET Framework re-applies the window styles in various places. Code using WM_NCCREATE should be removed and the regular .NET properties should be used.

Receive Object References​

Legacy SAL code can rely on receive/reassignment behavior for object arguments. Identify whether the callee is expected to mutate the existing object or replace the caller's reference; those are different operations in generated C#.

Generated object parameters commonly pass the reference by value. The callee can modify the referenced object, but replacing the reference does not update the caller's variable unless the parameter is passed by reference. Review any source code that depends on replacement.

However, in the few cases where this functionality was actually desired by the developer, an explicit receive parameter may be needed. Use ref when the callee must read the incoming reference and may replace it; use out when the method supplies an assigned result on every normal return. Update the call sites and tests together.

SalTblPopulate Error Handling​

In Team Developer when an invalid SQL statement is used with SalTblPopulate it simply returns FALSE and no When SqlError section is reached.

Ported code behaves in a different way: an invalid SQL statement raises an exception when used with SalTblPopulate. That means it is different to Team Developer but consistent with SqlPrepare and other Sql calls.

Migration Regression Cases​

Keep a small repeatable set of cases for null versus empty values, numeric rounding, localized input, invalid handles, failed SQL, and object-reference replacement. Compare results and side effects, not only whether the code compiles. For numeric pictures, see Microsoft's custom numeric format strings.

For PPJ Web, include two simultaneous users, session expiry, file upload/download, and report delivery. Native desktop integrations need an explicit target design.