Recent

Author Topic: Access Violation on Procedure Exit  (Read 4639 times)

therealhades

  • New member
  • *
  • Posts: 8
Access Violation on Procedure Exit
« on: May 29, 2024, 09:28:13 am »
Hello everyone,

i have the following problem and hope that somebody has an idea what i'm doing wrong.

I defined a record:
Code: Pascal  [Select][+][-]
  1. TStringVar = record
  2.     VarName: String;
  3.     Handle: LongWord;
  4.     Data: String;
  5. end;
  6.  

and a procedure to read a String Value from a PLC:
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ReadStringVar(var aVar: TStringVar);
  2. var
  3.   tmp: array of char;
  4.   ads: longint;
  5. begin
  6.   if aVar.Handle = 0 then
  7.     ads:= AdsSyncReadWriteReq(@AMS, ADSIGRP_SYM_HNDBYNAME, $0000, sizeof(aVar.Handle), @aVar.Handle, Length(aVar.VarName) + 1, @aVar.VarName[1])
  8.   else ads:= 0;
  9.   SetLength(tmp, 255);
  10.   if ads = 0 then
  11.     ads:= AdsSyncReadReq(@AMS, ADSIGRP_SYM_VALBYHND, aVar.Handle, Length(tmp), @tmp[1]);
  12.   if ads = 0 then aVar.Data:= PlcStringToString(tmp);
  13.   ShowMessage('in Procedure: ' + aVar.Data);
  14. end;
  15.  

I call this procedure in another procedure (btn[0] is of type TStringVar):
Code: Pascal  [Select][+][-]
  1. ReadStringVar(btn[0]);
  2. ShowMessage('after procedure: ' + btn[0].Data);
  3.  

Now i have the following Situation:
If i compile this code for Win32 or Win64 everything works fine. But if I compile this code for WinCE, only the ShowMessage inside the ReadStringVar procedure is called (with the correct Value from PLC) and then I got an Access Violation and the ShowMessage from the original procedure is not called.

Can anybody tell me what I'm doing wrong? As I said, this only happens with WinCE, on Win32 everything works like a charm.

Best regards
Bjoern

PS: Lazarus Version is 3.2, fpc is 3.2.2


« Last Edit: May 29, 2024, 09:31:02 am by therealhades »

Zvoni

  • Hero Member
  • *****
  • Posts: 2963
Re: Access Violation on Procedure Exit
« Reply #1 on: May 29, 2024, 09:38:00 am »
Line 11, last Parameter looks fishy to me.
shouldn't that be @tmp[0]?
tmp is dynamic array of char, and first char should be in Member 0
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

therealhades

  • New member
  • *
  • Posts: 8
Re: Access Violation on Procedure Exit
« Reply #2 on: May 29, 2024, 09:49:34 am »
You're right, it was [1] because i tried with Ansistring first.

I changed it to
Code: Pascal  [Select][+][-]
  1. ads:= AdsSyncReadReq(@AMS, ADSIGRP_SYM_VALBYHND, aVar.Handle, Length(tmp), @tmp[0]);
and got the same behavior (Access Vioalation after Procedure Exit)

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: Access Violation on Procedure Exit
« Reply #3 on: May 29, 2024, 09:58:24 am »
Hi
Line 7: you're using 'Length(aVar.VarName) + 1' & 'aVar.VarName[1]', when you haven't allocated *any* memory to 'aVar.VarName'  %)
Try to insert this line just before Line 7:
Code: Pascal  [Select][+][-]
  1. SetLength(aVar.VarName,255);
...and maybe skip the +1 part in 'Length(aVar.VarName) + 1', I dunno...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Zvoni

  • Hero Member
  • *****
  • Posts: 2963
Re: Access Violation on Procedure Exit
« Reply #4 on: May 29, 2024, 10:07:01 am »
Hi
Line 7: you're using 'Length(aVar.VarName) + 1' & 'aVar.VarName[1]', when you haven't allocated *any* memory to 'aVar.VarName'  %)
Try to insert this line just before Line 7:
Code: Pascal  [Select][+][-]
  1. SetLength(aVar.VarName,255);
...and maybe skip the +1 part in 'Length(aVar.VarName) + 1', I dunno...
Regards Benny
VarName is a string, so self-managed.
But you might be right.
Accessing external hardware is usually done with Pointer to chars, which need to be allocated
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

therealhades

  • New member
  • *
  • Posts: 8
Re: Access Violation on Procedure Exit
« Reply #5 on: May 29, 2024, 10:07:40 am »
I allocated the memory before the procedure call, sorry for not showing this.

Here is the complete procedure which calls the ReadString procedure:
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ReadButtonConfig;
  2. var
  3.   i, j: integer;
  4.   AdsError: LongInt;
  5.   tmp: string;
  6. begin
  7.  
  8.   AdsError:= AdsPortOpen();
  9.   //ShowMessage('AdsPort: ' + AdsError.ToString);
  10.  
  11.   if AMS.port <> 0 then begin
  12.     for i:= 0 to 7 do begin
  13.       j:= i + 1;
  14.       btn[i].VarName:= 'Config.Btn_Grp' + j.ToString;
  15.       btn[i].Handle:= 0;
  16.     end;
  17.   end;
  18.  
  19.   ReadStringVar(btn[0]);
  20.   ShowMessage('after procedure: ' + btn[0].Data);
  21.  
  22.   for i:= 1 to 7 do begin
  23.     ReadStringVar(btn[i]);
  24.   end;
  25.   btnGrp1.Caption:= btn[0].Data;
  26.   btnGrp2.Caption:= btn[1].Data;
  27.   btnGrp3.Caption:= btn[2].Data;
  28.   btnGrp4.Caption:= btn[3].Data;
  29.   btnGrp5.Caption:= btn[4].Data;
  30.   btnGrp6.Caption:= btn[5].Data;
  31.   btnGrp7.Caption:= btn[6].Data;
  32.   btnGrp8.Caption:= btn[7].Data;
  33. end;
  34.  

I think Line 19 is executed fine, because I got the MessageBox from the end of the procedure, but before Line 20 is executed I got the AccessViolation (only in WinCE, Win32 works fine).

The decalaration of btn:
Code: Pascal  [Select][+][-]
  1. var
  2.   frmMain: TfrmMain;
  3.   btn: array[0..7] of TStringVar;
  4.  

« Last Edit: May 29, 2024, 10:13:27 am by therealhades »

Thaddy

  • Hero Member
  • *****
  • Posts: 16937
  • Ceterum censeo Trump esse delendam
Re: Access Violation on Procedure Exit
« Reply #6 on: May 29, 2024, 10:11:57 am »
That record is wrong anyway. I would define it as:
Code: Pascal  [Select][+][-]
  1. TStringVar = record
  2.     VarName: String[255]; // to avoid it is a pointer type
  3.     Handle: LongWord;
  4.     Data: String;
  5. end;
This assumes the string is never longer than 255 bytes, because it is a ShortString.
It has no indirections, no pointers. All data is part of the record.

But misusing strings to catch data from lines is not a smart way anyway.
You should always catch that in binary format. Which means use a stream or a large enough buffer.
« Last Edit: May 29, 2024, 10:19:29 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

therealhades

  • New member
  • *
  • Posts: 8
Re: Access Violation on Procedure Exit
« Reply #7 on: May 29, 2024, 10:20:15 am »
I changed it to
Code: Pascal  [Select][+][-]
  1.   TStringVar = record
  2.     VarName: String[255];
  3.     Handle: LongWord;
  4.     Data: String;
  5.   end;
  6.  

Same effect. I changed the calling procedure to the following for a test:
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ReadButtonConfig;
  2. var
  3.   i, j: integer;
  4.   AdsError: LongInt;
  5.   tmp: string;
  6. begin
  7.  
  8.   AdsError:= AdsPortOpen();
  9.   //ShowMessage('AdsPort: ' + AdsError.ToString);
  10.  
  11.   if AMS.port <> 0 then begin
  12.     for i:= 0 to 7 do begin
  13.       j:= i + 1;
  14.       btn[i].VarName:= 'Config.Btn_Grp' + j.ToString;
  15.       btn[i].Handle:= 0;
  16.     end;
  17.   end;
  18.  
  19.   ReadStringVar(btn[0]);
  20.   ReadStringVar(btn[0]);
  21. end;
  22.  

The effect is that the first procedure call is executed and i get the MessageBox, then i get the AccessViolation and not the second MessageBox.

Thaddy

  • Hero Member
  • *****
  • Posts: 16937
  • Ceterum censeo Trump esse delendam
Re: Access Violation on Procedure Exit
« Reply #8 on: May 29, 2024, 10:27:48 am »
Really?
Code: Pascal  [Select][+][-]
  1. var
  2.   tmp: array of char;
Why not:
Code: Pascal  [Select][+][-]
  1. var
  2.   tmp: string[255] // and start your index at 1
Without seeing more code I can't reply with any other suggestions, because I don't have your PLC. But it transmits binary data, so your code should handle the data in the binary domain. NOT as string.

« Last Edit: May 29, 2024, 10:34:43 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Zvoni

  • Hero Member
  • *****
  • Posts: 2963
Re: Access Violation on Procedure Exit
« Reply #9 on: May 29, 2024, 10:28:54 am »
Single-step through your code, and report the exact line you get the AV
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Zvoni

  • Hero Member
  • *****
  • Posts: 2963
Re: Access Violation on Procedure Exit
« Reply #10 on: May 29, 2024, 10:37:21 am »
Wait a sec.....
https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/117520779.html&id=7570310429928352764
Quote
AdsSyncReadWriteReq

Writes data synchronously into an ADS server and receives data back from the ADS device.

LONG AdsSyncReadWriteReq(
     PAmsAddr  pAddr,
     ULONG  nIndexGroup,
     ULONG  nIndexOffset,
     ULONG  nReadLength,
     PVOID  pReadData,
     ULONG  nWriteLength,
     PVOID  pWriteData
);

Parameter

pAddr
[in] Structure with NetId and port number of the ADS server.

nIndexGroup
[in] Index Group.

nIndexOffset
[in] Index Offset.

nReadLength
[in] Length of the data, in bytes, returned by the ADS device.

pReadData
[out] Buffer with data returned by the ADS device.

nWriteLength
[in] Length of the data, in bytes, written to the ADS device.

pWriteData
[out] Buffer with data written to the ADS device.
4th Parameter: Your passing SizeOf(aVar.Handle), which is a Longword, which is how many bytes?
and in 5th Param you expect a handle back

https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/117518731.html&id=
Quote
AdsSyncReadReq

Reads data synchronously from an ADS server.

LONG AdsSyncReadReq(
     PAmsAddr  pAddr,
     ULONG  nIndexGroup,
     ULONG  nIndexOffset,
     ULONG  nLength,
     PVOID  pData);

Parameter

pAddr
[in] Structure with NetId and port number of the ADS server.

nIndexGroup
[in] Index Group.

nIndexOffset
[in] Index Offset.

nLength
[in] Length of the data in bytes.

pData
[out] Pointer to a data buffer that will receive the data.
3rd Parameter expects an offset, and you pass the handle
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

therealhades

  • New member
  • *
  • Posts: 8
Re: Access Violation on Procedure Exit
« Reply #11 on: May 29, 2024, 10:45:50 am »
Single-step through your code, and report the exact line you get the AV

Since the AV only happended with WinCE target I have to build gdb server for WinCE on X86 right? Because the Device is a X86 WinCE device.

Are there any X86 WinCE Binaries for gdb server?

Quote
3rd Parameter expects an offset, and you pass the handle
Yes, because in the example they use the handle as offset.

The Value is read correctly, because I get the Messagebox with the correct Value if I call the procedure.

therealhades

  • New member
  • *
  • Posts: 8
Re: Access Violation on Procedure Exit
« Reply #12 on: May 29, 2024, 01:01:03 pm »
Without seeing more code I can't reply with any other suggestions, because I don't have your PLC. But it transmits binary data, so your code should handle the data in the binary domain. NOT as string.

Here is the whole unit:
Code: Pascal  [Select][+][-]
  1. unit main;
  2.  
  3. {$mode objfpc}{$H+}
  4. //{$MODE Delphi}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, Buttons,
  10.   IniFiles,
  11.   {$ifdef windows}
  12.   {$ifndef wince}
  13.   tcadsdef, tcadsapi
  14.   {$endif}
  15.   {$endif}
  16.   {$ifdef wince}
  17.   tcadsdef_CE, tcadsapi_CE
  18.   {$endif}
  19.   ;
  20.  
  21. type
  22.  
  23.   TStringVar = record
  24.     VarName: String[255];
  25.     Handle: LongWord;
  26.     Data: String;
  27.   end;
  28.  
  29.   { TfrmMain }
  30.  
  31.   TfrmMain = class(TForm)
  32.     btnRes2: TButton;
  33.     btnRes4: TButton;
  34.     btnRes3: TButton;
  35.     btnBeenden: TButton;
  36.     btnRes1: TButton;
  37.     FlowPanel1: TFlowPanel;
  38.     lblDate: TLabel;
  39.     lblTime: TLabel;
  40.     lblController: TLabel;
  41.     Panel1: TPanel;
  42.     Panel2: TPanel;
  43.     Panel3: TPanel;
  44.     cycle1000ms: TTimer;
  45.     btnGrp1: TSpeedButton;
  46.     btnGrp2: TSpeedButton;
  47.     btnGrp3: TSpeedButton;
  48.     btnGrp4: TSpeedButton;
  49.     btnGrp5: TSpeedButton;
  50.     btnGrp6: TSpeedButton;
  51.     btnGrp7: TSpeedButton;
  52.     btnGrp8: TSpeedButton;
  53.     procedure btnBeendenClick(Sender: TObject);
  54.     procedure cycle1000msTimer(Sender: TObject);
  55.     procedure FormCreate(Sender: TObject);
  56.   private
  57.     AMS: TAmsAddr;
  58.     procedure EndProgram;
  59.     procedure StartProgram;
  60.     procedure ReadConfigFile;
  61.     procedure ReadGroupStatus;
  62.     procedure ReadButtonConfig;
  63.     procedure ReadStringVar(var aVar: TStringVar);
  64.     function PlcStringToString(APlcString: array of char): string;
  65.   public
  66.  
  67.   end;
  68.  
  69. var
  70.   frmMain: TfrmMain;
  71.   btn: array[0..7] of TStringVar;
  72.  
  73. implementation
  74.  
  75. {$R *.lfm}
  76.  
  77.  
  78. { TfrmMain }
  79.  
  80. procedure TfrmMain.btnBeendenClick(Sender: TObject);
  81. begin
  82.   EndProgram;
  83. end;
  84.  
  85. procedure TfrmMain.cycle1000msTimer(Sender: TObject);
  86. begin
  87.   lblDate.Caption:= FormatDateTime('dd.mm.yyyy', now);
  88.   lblTime.Caption:= FormatDateTime('hh:nn:ss', now);
  89. end;
  90.  
  91. procedure TfrmMain.FormCreate(Sender: TObject);
  92. begin
  93.   StartProgram;
  94. end;
  95.  
  96. procedure TfrmMain.EndProgram;
  97. var
  98.   i: integer;
  99. begin
  100.   for i:= 0 to 7 do begin
  101.      AdsSyncWriteReq( @AMS, ADSIGRP_RELEASE_SYMHND, $0000, sizeof( btn[i].Handle), @btn[i].Handle );
  102.   end;
  103.   AdsPortClose();
  104.   Application.Terminate;
  105. end;
  106.  
  107. procedure TfrmMain.StartProgram;
  108. begin
  109.   Width:= 800;
  110.   Height:= 480;
  111.   Left:= 0;
  112.   Top:= 0;
  113.   ReadConfigFile;
  114. end;
  115.  
  116. procedure TfrmMain.ReadConfigFile;
  117. var
  118.   ini: TIniFile;
  119.   sa: TStringArray;
  120.   s: string;
  121.   i: integer;
  122. begin
  123.   ini:= TIniFile.Create(Application.Location + 'Config.ini');
  124.   try
  125.     i:= ini.ReadInteger('ADS_Connection', 'Port', 0);
  126.     AMS.port:= i;
  127.     s:= ini.ReadString('General', 'ControllerName', '');
  128.     lblController.Caption:= s;
  129.     s:= ini.ReadString('ADS_Connection', 'NetID', '');
  130.     sa:= s.Split('.');
  131.     for i:= 0 to length(sa) - 1 do begin
  132.       AMS.netId.b[i]:= sa[i].ToInteger;
  133.     end;
  134.     ReadButtonConfig;
  135.   finally
  136.     FreeAndNil(ini);
  137.   end;
  138. end;
  139.  
  140. procedure TfrmMain.ReadGroupStatus;
  141. begin
  142.  
  143. end;
  144.  
  145. procedure TfrmMain.ReadButtonConfig;
  146. var
  147.   i, j: integer;
  148.   AdsError: LongInt;
  149. begin
  150.  
  151.   AdsError:= AdsPortOpen();
  152.   //ShowMessage('AdsPort: ' + AdsError.ToString);
  153.  
  154.   if AMS.port <> 0 then begin
  155.     for i:= 0 to 7 do begin
  156.       j:= i + 1;
  157.       btn[i].VarName:= 'Config.Btn_Grp' + j.ToString;
  158.       btn[i].Handle:= 0;
  159.     end;
  160.   end;
  161.  
  162.   ReadStringVar(btn[0]); // working, got Messagebox (last line of procedure (Line 202))
  163.   ShowMessage('test'); // not working, got AV
  164.  
  165.   //for i:= 1 to 7 do begin
  166.   //  ReadStringVar(btn[i]);
  167.   //end;
  168.   //btnGrp1.Caption:= btn[0].Data;
  169.   //btnGrp2.Caption:= btn[1].Data;
  170.   //btnGrp3.Caption:= btn[2].Data;
  171.   //btnGrp4.Caption:= btn[3].Data;
  172.   //btnGrp5.Caption:= btn[4].Data;
  173.   //btnGrp6.Caption:= btn[5].Data;
  174.   //btnGrp7.Caption:= btn[6].Data;
  175.   //btnGrp8.Caption:= btn[7].Data;
  176. end;
  177.  
  178. function TfrmMain.PlcStringToString(APlcString: array of char): string;
  179. var
  180.   i: integer;
  181. begin
  182.   Result:= '';
  183.   for i:= 0 to length(APlcString) do begin
  184.     if APlcString[i] <> Chr($00) then
  185.       Result:= Result + APlcString[i]
  186.     else exit;
  187.   end;
  188. end;
  189.  
  190. procedure TfrmMain.ReadStringVar(var aVar: TStringVar);
  191. var
  192.   tmp: array of char;
  193.   ads: longint;
  194. begin
  195.   if aVar.Handle = 0 then
  196.     ads:= AdsSyncReadWriteReq(@AMS, ADSIGRP_SYM_HNDBYNAME, $0000, sizeof(aVar.Handle), @aVar.Handle, Length(aVar.VarName) + 1, @aVar.VarName[1])
  197.   else ads:= 0;
  198.   SetLength(tmp, 255);
  199.   if ads = 0 then
  200.     ads:= AdsSyncReadReq(@AMS, ADSIGRP_SYM_VALBYHND, aVar.Handle, Length(tmp), @tmp[0]);
  201.   if ads = 0 then aVar.Data:= PlcStringToString(tmp);
  202.   ShowMessage('in Procedure: ' + aVar.Data);
  203. end;
  204.  
  205. end.
  206.  

the ADS functions are defined in the tcadsapi_ce (which is provided by PLC Vendor) unit as dll calls:
Code: Pascal  [Select][+][-]
  1. unit tcadsapi_CE;
  2.  
  3. {$MODE Delphi}
  4.  
  5. interface
  6. uses sysutils,LCLIntf, LCLType, LMessages, TcAdsDef_CE;
  7. {TcAdsDLL Prototypes}
  8. function AdsGetDllVersion():Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsGetDllVersion';
  9.  
  10. function AdsPortClose():Longint;stdcall; external 'TcAdsDllCe.dll' name 'AdsPortClose';
  11.  
  12. function AdsPortOpen():Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsPortOpen';
  13.  
  14. function AdsGetLocalAddress( pAddr:PAmsAddr ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsGetLocalAddress';
  15.  
  16. function AdsSyncWriteReq(    pAddr:PAmsAddr;
  17.                              indexGroup, indexOffset, length :Longword;
  18.                              pData:Pointer ):Longint; stdcall; external 'TcAdsDllCe.dll' name  'AdsSyncWriteReq';
  19.  
  20. function AdsSyncReadReq(     pAddr:PAmsAddr;
  21.                              indexGroup, indexOffset, length:Longword;
  22.                              pData:Pointer ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncReadReq';
  23.  
  24. function AdsSyncReadReqEx(   pAddr:PAmsAddr;
  25.                              indexGroup, indexOffset, length:Longword;
  26.                              pData:Pointer;
  27.                              pcbReturn :PLONGWORD ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncReadReqEx';
  28.  
  29.  
  30. function AdsSyncReadWriteReq(  pAddr:PAmsAddr;
  31.                                indexGroup, indexOffset, cbReadLength:Longword;
  32.                                pReadData:Pointer;
  33.                                cbWriteLength:Longword;
  34.                                pWriteData:Pointer ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncReadWriteReq';
  35.  
  36. function AdsSyncReadWriteReqEx( pAddr:PAmsAddr;
  37.                                 indexGroup, indexOffset, cbReadLength:Longword;
  38.                                 pReadData:Pointer;
  39.                                 cbWriteLength:Longword;
  40.                                 pWriteData:Pointer;
  41.                                 pcbReturn :PLONGWORD ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncReadWriteReqEx';
  42.  
  43.  
  44. function AdsSyncReadDeviceInfoReq( pAddr:PAmsAddr;
  45.                                    pDevName:PAnsiChar;
  46.                                    pVersion:PAdsVersion ):Longint;stdcall; external 'TcAdsDllCe.dll' name  'AdsSyncReadDeviceInfoReq';
  47.  
  48. function AdsSyncWriteControlReq( pAddr:PAmsAddr;
  49.                                  nAdsState, nDeviceState:Word;
  50.                                  nLength:Longword;
  51.                                  pData:Pointer ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncWriteControlReq';
  52.  
  53. function AdsSyncReadStateReq( pAddr:PAmsAddr;
  54.                               pAdsState, pDeviceState:PWORD ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncReadStateReq';
  55.  
  56. function AdsSyncAddDeviceNotificationReq( pAddr:PAmsAddr;
  57.                                           indexGroup, indexOffset:Longword;
  58.                                           pNoteAttrib:PAdsNotificationAttrib;
  59.                                           pNoteFunc:PAdsNotificationFuncEx;
  60.                                           hUser:Longword;
  61.                                           pNotification:PLONGWORD ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncAddDeviceNotificationReq';
  62.  
  63. function AdsSyncDelDeviceNotificationReq( pAddr:PAmsAddr;
  64.                                           hNotification:Longword ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncDelDeviceNotificationReq';
  65.  
  66. function AdsSyncSetTimeout( nMs:Longint ):Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsSyncSetTimeout';
  67.  
  68.  
  69.  
  70.  
  71. function AdsAmsRegisterRouterNotification(  pNoteFunc: PAmsRouterNotificationFuncEx ):Longint;  stdcall; external 'TcAdsDllCe.dll' name 'AdsAmsRegisterRouterNotification';
  72.  
  73. function AdsAmsUnRegisterRouterNotification ():Longint; stdcall; external 'TcAdsDllCe.dll' name 'AdsAmsUnRegisterRouterNotification';
  74.  

Thaddy

  • Hero Member
  • *****
  • Posts: 16937
  • Ceterum censeo Trump esse delendam
Re: Access Violation on Procedure Exit
« Reply #13 on: May 29, 2024, 03:07:47 pm »
- string[255] should be replaced with array[0..255] of char. (or byte) Essentially a PChar, but with a size and initialized memrory.
- tmp should be of type pchar and allocated with tmp:=allocmem(256);
- on exit of the method you should call freemem(tmp);

The issue is that the dll has no concept of any Pascal string type, as I already suspected.
They need to be PChar's or arra[0..length-1] of char /bytes (NO dynamic arrays) and they need to be initialized memory, hence allocmem.
« Last Edit: May 29, 2024, 03:13:21 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

therealhades

  • New member
  • *
  • Posts: 8
Re: Access Violation on Procedure Exit
« Reply #14 on: May 29, 2024, 04:23:08 pm »
- string[255] should be replaced with array[0..255] of char. (or byte) Essentially a PChar, but with a size and initialized memrory.
- tmp should be of type pchar and allocated with tmp:=allocmem(256);
- on exit of the method you should call freemem(tmp);

The issue is that the dll has no concept of any Pascal string type, as I already suspected.
They need to be PChar's or arra[0..length-1] of char /bytes (NO dynamic arrays) and they need to be initialized memory, hence allocmem.

Thanks Thaddy for your help and effort. I changed the procedure to this one to have a minimalistic version:
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ReadStringVar(var aVar: TStringVar);
  2. var
  3.   test: array[0..15] of char;
  4.   ads: longint;
  5.   tmp_handle: LongWord;
  6. begin
  7.  
  8.   // test with array of char instead string[255]
  9.   test[0]:= 'C';
  10.   test[1]:= 'o';
  11.   test[2]:= 'n';
  12.   test[3]:= 'f';
  13.   test[4]:= 'i';
  14.   test[5]:= 'g';
  15.   test[6]:= '.';
  16.   test[7]:= 'B';
  17.   test[8]:= 't';
  18.   test[9]:= 'n';
  19.   test[10]:= '_';
  20.   test[11]:= 'G';
  21.   test[12]:= 'r';
  22.   test[13]:= 'p';
  23.   test[14]:= '1';
  24.   test[15]:= Chr($00);
  25.  
  26.   tmp_handle:= aVar.Handle;;
  27.   if tmp_handle = 0 then
  28.     ads:= AdsSyncReadWriteReq(@AMS, ADSIGRP_SYM_HNDBYNAME, $0000, sizeof(tmp_handle), @tmp_handle, sizeof(test), @test[0])
  29.  
  30.   showMessage('Handle: ' + tmp_handle.ToString);
  31.   aVar.Handle:= tmp_handle;
  32. end;
  33.  

the procedure call is:
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ReadButtonConfig;
  2. var
  3.   i, j: integer;
  4.   AdsError: LongInt;
  5. begin
  6.  
  7.   AdsError:= AdsPortOpen();
  8.  
  9.   ReadStringVar(btn[0]);
  10.   ReadStringVar(btn[0]);
  11. end;
  12.  

The Result is the same. I got a Messagebox with the created Handle from the 1st Call and then the AV.

I tried other PLCs from the Vendor with this Code and the Result is:
  • CPU Atom E3815, OS WinCE (compiled with -T WinCE, -P i386) -> AV
  • CPU Atom E3815, OS Win10 1607 LTSB (compiled with -T Win32, -P i386) -> OK
  • CPU Arm Cortex A8, OS WinCE (compiled with -T WinCE, -P ARM) -> OK

So it seems that something with my environment is problematic. I installed all CrossCompilers via fpcupdeluxe, except the x86 WinCE combo, because this was not possible. (fpcupdeluxe said "No valid CPU/OS crosscompiler found"). Therefore i built the compiler manually with informations i found on the wiki/internet.

« Last Edit: May 29, 2024, 04:29:20 pm by therealhades »

 

TinyPortal © 2005-2018