Recent

Author Topic: SetLength() is not reliable?  (Read 4846 times)

jamie

  • Hero Member
  • *****
  • Posts: 6077
Re: SetLength() is not reliable?
« Reply #15 on: August 21, 2019, 10:47:09 pm »
There is a stringlist created, but it's never free'd…

At least I don't see where Strs.Free is before the code block exists...?
The only true wisdom is knowing you know nothing

senglit

  • Full Member
  • ***
  • Posts: 131
Re: SetLength() is not reliable?
« Reply #16 on: August 22, 2019, 03:01:37 am »
Code: Pascal  [Select][+][-]
  1.       strs.DelimitedText:=s;
  2.       strs.Delimiter:=#9;
  3.  

Does it really work?

1. First set Delimiter and only after - DelimitedText (the text which will processed with respect of setted earlier Delimiter)
2. Check strs.Count before things like MyDev[DevCount].ConnTo:=strs[6];

Yes, it really works.
I use Win10 + Lazarus 2.2.0 + FPC 3.2.2. All 64bit.

senglit

  • Full Member
  • ***
  • Posts: 131
Re: SetLength() is not reliable?
« Reply #17 on: August 22, 2019, 03:03:06 am »
DevCount is global initialized variable that permanently grows.
What is it?!

DevCount is a global var. it is increased while reading ini file on main form's creation and used by other module later.
I use Win10 + Lazarus 2.2.0 + FPC 3.2.2. All 64bit.

senglit

  • Full Member
  • ***
  • Posts: 131
Re: SetLength() is not reliable?
« Reply #18 on: August 22, 2019, 03:15:31 am »
@senglit

Can you please provide the source code that we can compile and test? Usually with compile-able code to test and inspect, I can debug and find the trouble line in hours. So do the many other seniors here.

Create a new folder, copy and paste all the necessary files except: the binary (exe file), *.bak, lib and backup folders. Compress the folder and send the zip file here. If you're not willing to publicize your project, you can write a demo that shows the issue.

Woks?
Yes it cooks fine and taste good too  O:-)

I was in the middle of having my meal when posting the post, so I unconsciously typed the word related with it.  :D

Hi, I uploaded the source code in the attachment. Thanks for your offering help.

One strange thing I found is that: the program didn't crash after the ini file was loaded. I stepped in to the loadsys procedure and found everything was ok. It crashed on application.run. I stepped into and found it crashed when WidgetSet.AppRun(@RunLoop);

I used a package "industrialstaff". I installed it from online package manager. Maybe there is something wrong with this package?

ps. the glable data type and var are defined in dataexchange.pas
« Last Edit: August 22, 2019, 03:17:18 am by senglit »
I use Win10 + Lazarus 2.2.0 + FPC 3.2.2. All 64bit.

Handoko

  • Hero Member
  • *****
  • Posts: 5122
  • My goal: build my own game engine using Lazarus
Re: SetLength() is not reliable?
« Reply #19 on: August 22, 2019, 09:50:34 am »
I've tested your code. As I am a Linux user and I'm not familiar with multitreading programming, I simplified your code and made some necessary changes:

- Removed the dependency of Windows unit (see line #117)
- Commented out all the calls to log function
- Changed MyCom's type from TSurveillance to TObject (see line #64)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, strutils, math;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     procedure Button1Click(Sender: TObject);
  17.     function LoadSysFile():boolean;
  18.   end;
  19.  
  20.   PRecConflictions = ^RecConflictions;
  21.   RecConflictions = record    //冲突定义:当A处在AState状态时,B不能进入BState状态,否则就发生冲突
  22.     APart:integer;        //A的DeviceID(在MyDev中的序号)。特殊定义--->高真空:0,
  23.     AState:boolean;       //A的状态。特殊定义:真空度为TRUE时表示高真空(<5Pa),否则为低真空
  24.     BPart:integer;
  25.     BState:boolean;
  26.     Next:PRecConflictions;
  27.   end;
  28.  
  29.   PRecDevInfo=^RecDevInfo;
  30.   RecDevInfo = record                //系统下挂的设备
  31.     DevId:integer;
  32.     Name:string;
  33.     Model:string;
  34.     //多端口模块下挂的设备端口号,例如5510A下的第1路DO
  35.     ConnTo:string;    //对于不是直接连COM的设备(阀门等),它是通过谁连上COM的?
  36.     ComPort: integer;
  37.     ComListId:integer; //指令发向哪个COM线程
  38.     Page:integer;       //设备在界面上的显示位置 页面位置:0系统决定,1真空阀,2分子泵,3离子源系统,4束流系统,5辅助系统开关设备-输出,6辅助系统开关设备-输入,7辅助系统模拟设备速出,8,辅助系统模拟设备输入
  39.     dType:integer;     //设备类型:1数字开关,2真空计    3分子泵    4电源    5软件自设    6流量计        7PLC模块    8开关监控量        9模拟输出量        10模拟输入量
  40.     Addr: integer;
  41.     IsOn:boolean;
  42.     PlcData:array[1..8] of char;
  43.     PlcAddr:array[1..8] of integer;
  44.     Value1:real;
  45.     Value2:real;
  46.     Range1:real;
  47.     Range2:real;
  48.   end;
  49.  
  50.   PEquipType = ^EquipType;
  51.   EquipType = record
  52.     Model:string;              //型号
  53.     LeiXing:integer;           //类型
  54.     r1:integer;                //值限1
  55.     r2:integer;                //值限2
  56.   end;
  57.  
  58. var
  59.   Form1: TForm1;
  60.   DevCount:integer=0;
  61.   DevCnflHead: PRecConflictions=nil;
  62.   DevCnflTail: PRecConflictions=nil;
  63.   MyDev:array of RecDevInfo;
  64.   MyCom: array of {TSurveillance;} TObject;
  65.   MyTypes:array[0..127] of EquipType;
  66.   TypeCount:integer=0;
  67.   CnflCount:integer=0;
  68.  
  69. implementation
  70.  
  71. function IsDecNumberic(Vaule:String):Boolean;   //判断Vaule是不是数字
  72. var
  73. i:integer;
  74. begin
  75. result:=true;   //设置返回值为 是(真)
  76. Vaule:=trim(Vaule);  //去空格
  77.   for i:=1 to length(Vaule) do  //准备循环
  78.     begin
  79.       if not (Vaule[i] in ['0'..'9']) then  //如果Vaule的第i个字不是0-9中的任一个
  80.         begin
  81.           result:=false;  //返回值 不是(假)
  82.           exit;  //退出函数
  83.         end;
  84.     end;
  85. end;
  86.  
  87. function DevIdByName(DevName:string):integer;
  88. var
  89.   i:integer;
  90. begin
  91.   result:=-100;
  92.   for i:=0 to DevCount do
  93.   begin
  94.     if MyDev[i].Name=DevName then
  95.     begin
  96.        result:=i;
  97.        exit;
  98.     end;
  99.   end;
  100. end;
  101.  
  102. function PosInArray(value:integer;arr:array of integer):integer;
  103. var
  104. i:integer;
  105. begin
  106.   result:=-1;
  107.   for i:=0 to length(arr)-1 do
  108.   begin
  109.     if arr[i]=value then
  110.     begin
  111.        result:=i;
  112.        exit;
  113.     end;
  114.   end;
  115. end;
  116.  
  117. procedure CopyMemory(Destination:Pointer; Source:pointer; Length:DWORD);
  118. begin
  119.   Move(Source^, Destination^, Length);
  120. end;
  121.  
  122. {$R unit1.lfm}
  123.  
  124. { TForm1 }
  125.  
  126. function TForm1.LoadSysFile():boolean;
  127. var
  128.   MyIniFile:TextFile;   //注意,Sys文件必须要保存成UNICODE编码
  129.   s,tmp:string;
  130.   strs:TStrings;
  131.   LineNow:integer=0;
  132.   i:integer;
  133.   ComId:array of integer;
  134.   ComCount:integer=0;
  135.   Match,ParantId:integer;
  136.   Section:integer;
  137.   const TotalSection=4;
  138.   SectionName:array[1..TotalSection] of string=('COM','SHEBEI','UNDER_PLC','CHONGTU');
  139. begin
  140.  
  141.   LineNow:=0;
  142.   result:=false;
  143.   if not fileExists('sys.pzwj') then
  144.     begin
  145. //      log('系统配置文件丢失');
  146.       exit;
  147.     end;
  148.   SetLength(MyDev,1);        //此处多次用SetLength来重设MyDev数组大小,会报执行时内存冲突。估计是Lazarus有问题,改为固定数组后就没问题。
  149.   MyDev[0].Name:='高真空';
  150.   MyDev[0].IsOn:=false;
  151.   MyDev[0].DevId:=0;
  152.   MyDev[0].Model:='ComThread';
  153.   AssignFile(MyIniFile,'sys.pzwj');
  154.   Reset(MyIniFile);
  155.   strs:=TStringList.Create;
  156.   try
  157.     while not eof(MyIniFile) do
  158.     begin
  159.       inc(LineNow);
  160.       Readln(MyIniFile,s);
  161.       s:=UpperCase(Trim(s));
  162.       if Length(s)=0 then continue; //跳过空行
  163.       if leftStr(s,1)='#' then continue; //跳过注释行
  164.       strs.DelimitedText:=s;
  165.       strs.Delimiter:=#9;
  166.       //for i:=0 to strs.Count-1 do
  167.       //  begin
  168.       //    strs[i]:=trim(strs[i]);
  169.           //rmLog.Lines.Add(strs[i]);
  170.       //  end;
  171.       Match:=0;
  172.       if '['=LeftStr(strs[0],1) then
  173.         begin
  174.           for i:=1 to TotalSection do
  175.             begin
  176.               if MidStr(strs[0],2,Length(strs[0])-2)=SectionName[i] then
  177.                 begin
  178.                   Section:=i;
  179.                   Match:=i;
  180.                   break;
  181.                 end;
  182.             end;
  183.           if 0=Match then
  184.           begin
  185. //            log('系统第'+IntToStr(LineNow)+'行错误');
  186.             exit;
  187.           end;
  188.           Section:=Match;
  189.           continue;
  190.         end;
  191.       if (Section<TotalSection) then    //除冲突列表外,设备名查重
  192.         if 0<DevIdByName(strs[0]) then
  193.           begin
  194. //            log('系统文件第'+IntToStr(LineNow)+'行重名');
  195.             exit;
  196.           end;
  197.       if 1=Section then
  198.         begin
  199.           if 'COM'<>leftstr(strs[0],3) then
  200.             begin
  201. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  202.               exit;
  203.             end;
  204.           if 4<>strs.Count then
  205.             begin
  206. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  207.               exit;
  208.             end;
  209.           if (not IsDecNumberic(strs[1])) or (not IsDecNumberic(strs[2])) or (not IsDecNumberic(strs[3])) then
  210.             begin
  211. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  212.               exit;
  213.             end;
  214.           if (not InRange(strtoint(strs[1]),0,7)) or (not InRange(strtoint(strs[2]),0,3))
  215.               or (not InRange(strtoint(strs[3]),100,1000)) then
  216.             begin
  217. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  218.               exit;
  219.             end;
  220.           inc(ComCount);
  221.           SetLength(ComId,ComCount);
  222.           ComId[ComCount-1]:=strtoint(rightstr(strs[0],length(strs[0])-3));
  223.           SetLength(MyCom,ComCount);
  224. //          MyCom[ComCount-1]:=TSurveillance.Create(ComId[ComCount-1],strtoint(strs[1]),
  225. //            strtoint(strs[2]),strtoint(strs[3]));
  226.         end;
  227.       if 2=Section then                       //COM接的设备
  228.         begin
  229.           if (6<>strs.Count) or (not IsDecNumberic(strs[2])) or (not IsDecNumberic(strs[4])) then
  230.             begin
  231. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  232.               exit;
  233.             end;
  234.           if (StrToInt(strs[2])<1) then
  235.             begin
  236. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  237.               exit;
  238.             end;
  239.           if 'COM'<>LeftStr(strs[1],3) then
  240.             begin
  241. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  242.               exit;
  243.             end;
  244.           if not IsDecNumberic(RightStr(strs[1],length(strs[1])-3)) then
  245.             begin
  246. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  247.               exit;
  248.             end;
  249.           if (strtoint(RightStr(strs[1],length(strs[1])-3))<1) then
  250.             begin
  251. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  252.               exit;
  253.             end;
  254.           tmp:=strs[1];
  255.           tmp:=rightstr(tmp,Length(tmp)-3);
  256.  
  257.           if (255<>strtoint(tmp)) and (-1 = PosInArray(strtoint(tmp),ComId)) then
  258.             begin
  259. //              log('系统文件第'+IntToStr(LineNow)+'行错误');
  260.               exit;
  261.             end;
  262.           inc(DevCount);
  263.           SetLength(MyDev,DevCount+1);           //此处多次用SetLength来重设MyDev数组大小,会报执行时内存冲突。估计是Lazarus有问题,改为固定数组后就没问题。
  264.           MyDev[DevCount].DevId:=DevCount;
  265.           MyDev[DevCount].Name:=strs[0];
  266.           MyDev[DevCount].ComPort:=StrToInt(tmp);
  267.           MyDev[DevCount].Addr:=StrToInt(strs[2]);
  268.           MyDev[DevCount].Model:=strs[3];
  269.           MyDev[DevCount].Page:=strtoint(strs[4]);
  270.           MyDev[DevCount].dType:=strtoint(strs[5]);
  271.           MyDev[DevCount].ConnTo:='';
  272.           MyDev[DevCount].IsOn:=false;
  273.           if (255<>MyDev[DevCount].ComPort) or (255<>MyDev[DevCount].Addr) then
  274.             begin
  275.               for i:=0 to TypeCount-1 do                   //读出该设备对应的可设值限
  276.                 begin
  277.                   if MyTypes[i].Model=MyDev[DevCount].Model then
  278.                     begin
  279.                       MyDev[DevCount].Range1:=MyTypes[i].r1;
  280.                       MyDev[DevCount].Range2:=MyTypes[i].r2;
  281.                       break;
  282.                     end;
  283.                 end;
  284.               for i:=0 to length(ComId)-1 do            //找出指令要发到哪个COM口
  285.                 begin
  286.                   if MyDev[DevCount].ComPort = ComId[i] then
  287.                     begin
  288.                       MyDev[DevCount].ComListId:=i;
  289.                       break;
  290.                     end;
  291.                 end;
  292.             end;
  293.         end;
  294.       if 3=Section then     //不直接挂在COM下的设备,通过PLC连接
  295.         begin
  296.           ParantId:=DevIdByName(strs[3]);
  297.           if ParantId=-100 then
  298.             begin
  299. //              log('系统文件第'+IntToStr(LineNow)+'行错误,父节点无效');
  300.               exit;
  301.             end;
  302.           inc(DevCount);
  303.           SetLength(MyDev,DevCount+1);
  304.           MyDev[DevCount].DevId:=DevCount;
  305.           MyDev[DevCount].Name:=strs[0];
  306.           MyDev[DevCount].Model:=strs[1];
  307.           MyDev[DevCount].dType:=strtoint(strs[2]);
  308.           MyDev[DevCount].ConnTo:=strs[3];
  309.           MyDev[DevCount].Page:=strtoint(strs[4]);
  310.           if strs.Count>=7 then
  311.             begin
  312.               CopyMemory(@MyDev[DevCount].PlcData[1],PChar(strs[5]),1);
  313.               MyDev[DevCount].PlcAddr[1]:=strtoint(strs[6]);
  314.             end;
  315.           if strs.Count>=9 then
  316.             begin
  317.               CopyMemory(@MyDev[DevCount].PlcData[2],PChar(strs[7]),1);
  318.               MyDev[DevCount].PlcAddr[2]:=strtoint(strs[8]);
  319.             end;
  320.           if strs.Count>=11 then
  321.             begin
  322.               CopyMemory(@MyDev[DevCount].PlcData[3],PChar(strs[9]),1);
  323.               MyDev[DevCount].PlcAddr[3]:=strtoint(strs[10]);
  324.             end;
  325.           if strs.Count>=13 then
  326.             begin
  327.               CopyMemory(@MyDev[DevCount].PlcData[4],PChar(strs[11]),1);
  328.               MyDev[DevCount].PlcAddr[4]:=strtoint(strs[12]);
  329.             end;
  330.           if strs.Count>=15 then
  331.             begin
  332.               CopyMemory(@MyDev[DevCount].PlcData[5],PChar(strs[13]),1);
  333.               MyDev[DevCount].PlcAddr[5]:=strtoint(strs[14]);
  334.             end;
  335.           if strs.Count>=17 then
  336.             begin
  337.               CopyMemory(@MyDev[DevCount].PlcData[6],PChar(strs[15]),1);
  338.               MyDev[DevCount].PlcAddr[6]:=strtoint(strs[16]);
  339.             end;
  340.           if strs.Count>=19 then
  341.             begin
  342.               CopyMemory(@MyDev[DevCount].PlcData[7],PChar(strs[17]),1);
  343.               MyDev[DevCount].PlcAddr[7]:=strtoint(strs[18]);
  344.             end;
  345.           if strs.Count>=21 then
  346.             begin
  347.               CopyMemory(@MyDev[DevCount].PlcData[8],PChar(strs[19]),1);
  348.               MyDev[DevCount].PlcAddr[8]:=strtoint(strs[20]);
  349.             end;
  350.           MyDev[DevCount].ComPort:=MyDev[ParantId].ComPort;
  351.           MyDev[DevCount].ComListId:=MyDev[ParantId].ComListId;
  352.           MyDev[DevCount].Addr:=MyDev[ParantId].Addr;
  353.           for i:=0 to TypeCount-1 do                   //读出该设备对应的可设值限
  354.             begin
  355.               if MyTypes[i].Model=MyDev[DevCount].Model then
  356.                 begin
  357.                   MyDev[DevCount].Range1:=MyTypes[i].r1;
  358.                   MyDev[DevCount].Range2:=MyTypes[i].r2;
  359.                   break;
  360.                 end;
  361.             end;
  362.         end;
  363.       if TotalSection=Section then              //冲突
  364.         begin
  365.           if (strs.Count<>4) or (('ON'<>strs[1]) and ('OFF'<>strs[1])) or
  366.           (('ON'<>strs[3]) and ('OFF'<>strs[3])) or (strs[0]=strs[2])then
  367.             begin
  368. //              log('系统文件第'+IntToStr(LineNow)+'行,格式错误');
  369.               exit;
  370.             end;
  371.           if (0>DevIdByName(strs[0])) or (0>DevIdByName(strs[2])) then
  372.             begin
  373. //              log('系统文件第'+IntToStr(LineNow)+'行,设备无效');
  374.               exit;
  375.             end;
  376.           inc(CnflCount);
  377.           if nil = DevCnflHead then
  378.             begin
  379.               DevCnflHead:=new(PRecConflictions);
  380.               DevCnflTail:=DevCnflHead;
  381.             end
  382.           else
  383.             begin
  384.               DevCnflTail^.Next:=new(PRecConflictions);
  385.               DevCnflTail:=DevCnflTail^.Next;
  386.               DevCnflTail^.Next:=nil;
  387.             end;
  388.           DevCnflTail^.APart:=DevIdByName(strs[0]);
  389.           DevCnflTail^.AState:=(strs[1]='ON');
  390.           DevCnflTail^.BPart:=DevIdByName(strs[2]);
  391.           DevCnflTail^.BState:=(strs[3]='ON');
  392.           continue;
  393.         end;
  394.     end;
  395.   finally
  396.     CloseFile(MyIniFile);
  397.   end;
  398.   //SetLength(MyDev,DevCount+1);
  399.   strs.Free;
  400.   result := true;
  401.  
  402.   ShowMessage(
  403.     'LineNow = '  + LineNow.ToString  + LineEnding +
  404.     'ComCount = ' + ComCount.ToString + LineEnding +
  405.     'DevCount = ' + DevCount.ToString
  406.     );
  407. end;
  408.  
  409. procedure TForm1.Button1Click(Sender: TObject);
  410. begin
  411.   if LoadSysFile then
  412. //    log('加载系统文件成功')
  413.   else
  414.     begin
  415. //      log('加载系统文件失败',clRed);
  416.       exit;
  417.     end;end;
  418. end.

If anyone interested, you can download my simplified version (test.zip) attached in this post. And the original full version (brtc-half.zip) can be downloaded on the previous post.

Because the original source code depends on many units, Windows and multithreading, so I only tested the simplified version.

My test result, it works perfectly okay. No stack error, no memory error, no SIGSEG error, etc.

But if you click the button twice, a 201 runtime error will occurs. It is understandable because some variables aren't reset to the initial variables. It won't happen on the original version because the code (LoadSysFile function) only called once by OnFormCreate event.

@senglit, can you try this test.zip? You said your code crashed but if running test.zip won't crash on your system, I believe the issue is not in the LoadSysFile.

My guess, it maybe in TSurveillance (ComSurv unit). I haven't learned multhithreading programming, I cannot help you debug into that code.

~~~edit~~~

One strange thing I found is that: the program didn't crash after the ini file was loaded. I stepped in to the loadsys procedure and found everything was ok. It crashed on application.run. I stepped into and found it crashed when WidgetSet.AppRun(@RunLoop);

So, it is clear SetLength works correctly. The problem is elsewhere. I guess it is multithreading issue.
« Last Edit: August 22, 2019, 09:54:54 am by Handoko »

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: SetLength() is not reliable?
« Reply #20 on: August 22, 2019, 11:30:21 am »
Multi-threading can complicate things quite a bit. I recently added a "simple" timer to my project to record and log data to a disk file every few seconds. Start and Stop buttons are supposed to pause and resume recording. Not surprisingly, every now and then I got a "File not open" error when pausing. This happens because there is no telling when exactly the timer (thread) invokes the recording, even if Timer.Enabled is set to False before closing the file in the Stop procedure. Application.ProcessMessages at key places and a RecordingActive (true/false) flag inside the thread solved the problem while keeping the interface responsive.

Just to illustrate how multi-threading can cause your program to behave in unexpected ways.
keep it simple

jamie

  • Hero Member
  • *****
  • Posts: 6077
Re: SetLength() is not reliable?
« Reply #21 on: August 22, 2019, 11:17:08 pm »
There are still memory leaks!

 Calling EXIT will call the FINALLY sections and then exit out of the whole procedure.

 Code after the "TRY... FINALLY.. END" does not get executed when you use EXIT earlier.

 The freeing of the Strs is taking place afterwards and thus this means it never reaches that location.

 I would guess multiple calls would accumulate memory over time. This you should see in the HeapTRC.
 

The only true wisdom is knowing you know nothing

senglit

  • Full Member
  • ***
  • Posts: 131
Re: SetLength() is not reliable?
« Reply #22 on: August 23, 2019, 03:12:50 am »
Now I believe it's not caused by setlength().
1. after i ommitted all of the create...block() procudures, the program is supposed to do nothing but to show the mainform.
2. I also ommitted for... do MyCom.start; Acturally all of the treads are not started, but only created.
3. I ommitted all of the lines in the ini file which would increase the DevCount and setlength() to MyDev[].
4. under conditions listed before, the program wouldn't crash!

Then, if I un-ommit even 1 line in ini file to call setlength() for 1 time, the program crash again. And it did not crashed while loading ini file (everything is fine in that process), but crushed at WidgetSet.AppRun(@RunLoop);

I suspect there is something in the package industrialStuff conflict against setlength(). But I'm not so sure, because I used that package in my last program and used setlength() too, nothing bad happened.
I use Win10 + Lazarus 2.2.0 + FPC 3.2.2. All 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 14157
  • Probably until I exterminate Putin.
Re: SetLength() is not reliable?
« Reply #23 on: August 23, 2019, 08:26:31 am »
Is the inifile opened for read/write? or only read? The latter may cause an EInOutError exception that you can catch and examine EInOutError.ErrorCode.
Pseudo code:
Code: Pascal  [Select][+][-]
  1. try
  2.   ...infile handling
  3. except
  4.   On E:EInOutError do ...
  5.   else
  6.      Raise // it it is something else
  7. end;
« Last Edit: August 23, 2019, 08:30:35 am by Thaddy »
Specialize a type, not a var.

julkas

  • Guest
« Last Edit: August 23, 2019, 09:20:20 am by julkas »

 

TinyPortal © 2005-2018