Home > Enterprise >  Delphi 10.4 64 bit compiler throws c0000005 ACCESS_VIOLATION on code that works in 32 bit mode. Brea
Delphi 10.4 64 bit compiler throws c0000005 ACCESS_VIOLATION on code that works in 32 bit mode. Brea

Time:09-06

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 }
  • Related