In the process of converting a number of old applications from Delphi 5 to Delphi 10.4 and from there from 32 bit to 64 bit, I've come across some weird behavior of the Delphi 10.4 64 bit compiler. At the moment, the code works perfectly within Delphi 10.4 when compiled in 32 bit. Furthermore, I've managed to adjust the code and settings/configuration so that the project compiles in 64 bit mode. Although not yet perfect, I could do some functionality testing and that shows some weird behavior when trying to call an external, 3rd party supplied, library (I don't think it's relevant, but the call is to the Lingo17 64 bit modelsolver).
The library call happens in a separate unit and looks like this:
function LSexecuteScriptLng( pnEnv: Integer; cScript: pAnsiChar): Integer;
stdCall; external 'Lingd64_17.dll';
When run in debugging mode, a 'Debugger Exception Notification' popup is prompted, with text 'Project MyProject.exe raised exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'' and button options 'Break', 'Continue' and 'Help'. When I choose Break, it goes to the line where the call to LSexecuteScriptLng is made:
intLingoError := LSexecuteScriptLng(intLingoEnvironment,PAnsiChar(strModelExecuteScript));
EDIT: Here is all the code relevant to strModelExecuteScript
:
unit unLingoSolver
const
cCrLf= Chr(10) Chr(13); // Carriage return line feed
type TLingoSolver = class(TObject)
private
strModelScript : string;
function Solve(Model : TModel; LingoSettings : TLingoSettings): boolean;
protected
public
property ModelScript : string read strModelScript write SetModelScript;
procedure TLingoSolver.SetModelScript(strModelText: string);
begin
strModelScript := 'MODEL:' cCrLf
'TITLE:COCOS;' cCrLf
Trim(strModelText) cCrLf
'END' cCrLf ;
end;
function TLingoSolver.Solve(Model : TModel ; LingoSettings : TLingoSettings): boolean;
var strModelExecuteScript : AnsiString;
ModelScript : string;
begin
ModelScript := Model.ModelText; // Model.ModelText is assigned from a TOracleQuery earlier in the code.
// It is basically a long string filled with the definitions and equations of the actual model
strModelExecuteScript := '';
strModelExecuteScript := LingoSettings.GetSettings() ; // See code in different unit below
strModelExecuteScript := strModelExecuteScript
ModelScript cCrLf
'GO' cCrLf
'QUIT' cCrLf ;
intLingoError := LSexecuteScriptLng(intLingoEnvironment,PAnsiChar(strModelExecuteScript));
end;
end.
// The following code is in a different unit
unit unLingoSettings;
interface
uses sysutils;
const cCr = Chr(10); // Carriage return
implementation
function TLingoSettings.GetSettings() : string ;
var strSettings : string;
begin
strSettings :='SET TERSEO ' IntTostr(Settings.TERSEO) cCr
'SET ECHOIN ' IntTostr(Settings.ECHOIN) cCr
'SET LINLEN ' IntTostr(Settings.LINLEN) cCr
//TIMLIM is controlled by callback function. See unLingoSolver.pas
//Setting TIMLIM > 0 will cause error "Time limit execeeded" of first calculation after midnigt.
//This is a bug in Lingo. Confirmed by Lindo Systems.
//'SET TIMLIM ' IntTostr(Settings.TIMLIM) cCr
'SET ITRLIM ' IntTostr(Settings.ITRLIM) cCr
'SET NSTEEP ' IntTostr(Settings.NSTEEP) cCr
'SET NSLPDR ' IntTostr(Settings.NSLPDR) cCr
'SET INFTOL ' StringReplace(FloatToStr(Settings.INFTOL),',','.',[rfReplaceAll, rfIgnoreCase]) cCr
'SET FNFTOL ' StringReplace(FloatToStr(Settings.FNFTOL),',','.',[rfReplaceAll, rfIgnoreCase]) cCr
'SET NOPTOL ' StringReplace(FloatToStr(Settings.NOPTOL),',','.',[rfReplaceAll, rfIgnoreCase]) cCr
'SET DERCMP ' IntTostr(Settings.DERCMP) cCr
'SET NCRASH ' IntTostr(Settings.NCRASH) cCr
'SET LCRASH ' IntTostr(Settings.LCRASH) cCr
'SET MULTIS ' IntTostr(Settings.MULTIS) cCr
'SET DUALCO ' IntTostr(Settings.DUALCO) cCr
'SET SELCON ' IntTostr(Settings.SELCON) cCr
{$IFDEF LINGO17}
'SET NLPVER ' IntTostr(Settings.NLPVER) cCr
{$ENDIF}
'' ;
result := strSettings; // Actual values of Settings are irrelevant for the question
end;
end.
At this point, I thought the error resembled this SO post: Delphi 10.3.1 compiler generates code that issues an exception when compiled to 64 bits, but the weird assembly code from that post does not show up in my project when I check CPU-VIEW. Just to be sure, I tried the accepted solution from that post by adding const
before the cScript parameter in the LSexecuteScriptLng function, but that does not change anything.
It gets even more weird when I place a breakpoint on the codeline where the Debugger breaks to. From there I can step into the LSexecuteScriptLng function in the separate unit, so the ACCESS_VIOLATION when calling that function is gone apparently? But the results returned by the .dll call make no sense; no actual model calculation results and status/logging text message that seem to randomly contain characters from an Asian alphabet? (I'm not trying to be racist/ignorant here, I just simply don't know the characters I'm seeing, here's an example from the Lingo logfile:
㸀 伀䈀䨀 > MI⁐ 㸀 䴀䤀倀06-09 10:48:40:739 Mode 㸀 䰀椀渀最漀 渀漀 猀漀氀甀琀椀漀渀 猀琀愀 㸀 䔀砀攀挀 㘀ⴀ 㤀 㨀㐀㠀㨀㐀 㨀㜀㌀㤀 䰀椀渀最漀 猀漀氀瘀椀渀最 攀爀爀漀爀㨀 㘀ⴀ 㤀 㨀㐀㤀㨀㐀㜀㨀㐀㜀 䰀椀
This is from a logfile that is otherwise comprised of perfectly comprehensible English.
CodePudding user response:
From the online documentation:
#define pLSenvLINGO void*
int LSexecuteScriptLng( pLSenvLINGO pL, char* pcScript)
This routine is the main workhorse of the LINGO DLL that processes LINGO command scripts. The script may be contained entirely in memory, or it may contain one or more TAKE commands to load scripts from disk.
Arguments: pL Pointer to a LINGO environment created by a previous call to LScreateEnvLng(). pcScript Pointer to a character string containing a LINGO command script. Each line must be terminated with a linefeed character (ASCII 10), and the entire script must be terminated with a NULL (ASCII 0).
Return Value:
Returns 0 if no error occurred. Otherwise, one of the nonzero error codes listed in section LINGO DLL Error Codes is returned.
You are passing an Integer rather than a pointer for the first argument and in 64-bit a pointer is 8 bytes.
Function LSexecuteScriptLng( pnEnv: Pointer; cScript: pAnsiChar): Integer;
stdCall; external 'Lingd64_17.dll';
You will also need to update other functions using the environment pointer such as LScreateEnvLng
CodePudding user response:
My best guess is that your definition of the function for 64-bit is wrong. It's unlikely that a 64-bit library uses ANSI encoding of text, so it should probably be defined as
function LSexecuteScriptLng( pnEnv: Integer; cScript: pChar): Integer;
stdCall; external 'Lingd64_17.dll';
and at the call site, you should remove the hard cast to PAnsiChar:
intLingoError := LSexecuteScriptLng(intLingoEnvironment,{$IFDEF CPU32BITS} PAnsiChar {$ENDIF }(strModelExecuteScript));
assuming that strModelExecuteScript is a STRING / UnicodeString type.
(if you use other than 32/64 bit Windows, you might need to adjust the $IFDEF accordingly)
Also, double-check your 32-bit version. If strModelExecuteScript is a STRING / UnicodeString type then you should use
PAnsiChar(AnsiString(strModelExecuteScript))
at the call site, and either
intLingoError := LSexecuteScriptLng(intLingoEnvironment,{$IFDEF CPU32BITS} PAnsiChar(AnsiString {$ELSE } ( {$ENDIF }(strModelExecuteScript));
or
{$IFDEF CPU32BITS }
intLingoError := LSexecuteScriptLng(intLingoEnvironment,PAnsiChar(AnsiString(strModelExecuteScript));
{$ELSE }
intLingoError := LSexecuteScriptLng(intLingoEnvironment,strModelExecuteScript);
{$ENDIF }
to handle both cases.
Addendum: If the 32-bit version of the library expects ANSI string and the 64-bit version UNICODE, and the strModelExecuteScript variable is AnsiString, then I suggest that you either update the strModelExecuteScript to UNICODE string, or do the following:
function LSexecuteScriptLng( pnEnv: Integer; cScript: pChar): Integer;
stdCall; external 'Lingd64_17.dll';
{$IFDEF CPU32BITS }
intLingoError := LSexecuteScriptLng(intLingoEnvironment,PAnsiChar(strModelExecuteScript);
{$ELSE }
intLingoError := LSexecuteScriptLng(intLingoEnvironment,PChar(UnicodeString(strModelExecuteScript));
{$ENDIF }