Local string array initialization

332 views Asked by At

I have sporadic problems (access violation in unit System).
Application is running 24 x 7 and it happens one - two times in a week.
I have a procedure with local string array, and I found there are cases I assign non initialised array member to string variable like in code below.
Could it be a reason for access violation?

procedure tform1.DoSomething;
var
   sarr: array [1..4] of String;
   s: String;
begin
   sarr[1] := 'aaaa';
   sarr[2] := 'bbbb';
   s := sarr[3]; // Can I get here an access violation???
end;

Real function code below exception happens when obj.opcode = cmdp_done function is called from thread message queue. obj is created in another thread, and sent in PostThreadMessage as msg.lparam

procedure ORTR_ProcessFiscalResponse(obj: TDataForOrpak);
    const
      maxdim = 4;
    var
      s: array [1..maxdim] of String;
      i, n, fiscalNr, statusToSend: Integer;
      sql, pdf, ortrName, docType, rut: String;
      ortr: TCustomizedOrtr;
      oFiscal: TFiscalDevice;
      fpds: TFPDeviceState;
    begin
      try
        case obj.opcode of
          cmdp_status:    N := 3;
          cmdp_done:      N := 2;
          plcl_docissued: N := 4;
          plcl_docfailed: N := 1;
          else
            Exit;
        end;
        for i:=1 to n do
          s[i] := GetTerm(obj.ident, i, ';');
        if s[1] = '' then
          Exit;
        statusToSend := 0;
        ortrName := GetTerm(s[1], 1, '+');
        fiscalNr := StrToIntDef(GetTerm(s[1]+'+', 2, '+'), 999999);
        docType := s[3];
        rut := s[4];
        ortr := TCustomizedOrtr.GetTerminalByName(ortrName) as TCustomizedOrtr;
        if ortr = nil then
          Exit;
        if (ortr.FPState = fps_idle) or (ortr.fiscalNr <> fiscalNr) then begin
          if (StrToIntDef(s[2], 0) <> 0) and (obj.opcode = cmdp_done) then
            fiscal_Confirm(obj.otherdevname, obj.ident);
          if obj.opcode = plcl_docissued then begin
            try
              PLCL_SetDocState(s[1], rut, false, StrToInt(s[2]), StrToInt(docType));
            except
              AddToLogFile('*** PLCL_SetDocState', log_exceptions);
            end;
          end;
          Exit;
        end;
        oFiscal := fiscal_Assigned(ortr.ctlPump.PumpID) as TFiscalDevice;
        case obj.opcode of
          plcl_docissued:
            begin
              ortr.authData.ECRReceiptNr := s[2];
              pdf := StringFromHexPresentation(obj.rawdata);
              sql := format(sql_PaperlessAdd, [
                ToLocalSQL_DateTime(ortr.ctlPump.FinalTime),
                ToLocalSQL_Integer(ortr.ctlPump.PumpID),
                ToLocalSQL_String(pdf)]);
              try
                UpdateLocalDB(sql);
              except
                AddToLogFile('*** PL save pdf', log_exceptions);
              end;
              try
                PLCL_SetDocState(s[1], rut, true, StrToInt(s[2]), StrToInt(docType));
              except
                AddToLogFile('*** PLCL_SetDocState', log_exceptions);
              end;
              ortr.FPState := fps_idle;
              ortr.currStage := TTextIndexType(0);
              ortr.currStage := tivirt_towelcome;  // VADIM
              ExternalProcessPumpState(ortr.authData.gsPump.PumpID);
            end;
          plcl_docfailed:
            ortr.FPState := fps_plerror;
          cmdp_status:
            begin
              ortr.FPError := StrToIntDef(s[3], 0);
              fpds := TFPDeviceState(StrToIntDef(s[2], 0));
              ortr.FPState := fpds;
              if (fpds in [fps_nocomm, fps_error]) and (ortr.fiscalMode = 1) and
                 (ortr.authData = nil) and (ortr.fiscalNr < 0) then
                    SpecialInterface.SendFiscalNrToPromax(-ortr.fiscalNr, '0');
              case fpds of
                fps_nopaper:  statusToSend := wph_prnpaperout;
                fps_nocomm:   statusToSend := wph_prncommfailure;
                fps_error:    statusToSend := wph_prngenericfailure;
              end;
            end;
          cmdp_done:
            begin
              if ortr.fiscalMode = 1 then begin
                if ortr.authData = nil then begin // DRY GOOD
                  SpecialInterface.SendFiscalNrToPromax(-fiscalNr, s[2]);
                end
                else begin
                  ortr.authData.ECRReceiptNr := s[2];
                  ExternalProcessPumpState(ortr.authData.gsPump.PumpID);
                end
              end;
              if StrToIntDef(s[2], 0) <> 0 then
                fiscal_Confirm(obj.otherdevname, obj.ident);
              statusToSend := wph_prnidle;
              ortr.FPState := fps_idle;
              ortr.currStage := ti_takereceipt;
            end;
        end;
        if (statusToSend <> 0) and (oFiscal <> nil) then
          PostNonVisualCommand(nv_devicestate, statusToSend, Integer(oFiscal));
      finally
        obj.free;
      end;
    end;
1

There are 1 answers

0
NGLN On

Your initial piece of code, the tform1.DoSomething routine, is unable of producing an access violation:

Thus you are simply assigning an empty string, and s remains empty.


Concerning your actual code, assuming that it does produce the access violation, my guess would be that:

  • the obj parameter still refers to an already destroyed object,
  • that obj.opcode reads an invalid piece of memory, but since it is compared to an numerical value, will pass,
  • that Exit is called in de case else clause, and
  • that obj.Free fails in the finally clause.

1 All string variables are initilized to empty, except string function results:

If the function exits without assigning a value to Result or the function name, then the function's return value is undefined.

The missing compiler warning is still a bug.