Jump to content

Alberto Paganini

Members
  • Content Count

    42
  • Joined

  • Last visited

Everything posted by Alberto Paganini

  1. Alberto Paganini

    tlsv1 alert protocol version

    Hello, I have a problem with a Win32 application using Indy. All of a sudden it throws me the following error message: "Error connecting with SSL. error 1409442E:SSL routine:ssl3_read_bytes:tlsv1 alert protocol version" In the beginning, I supposed it had something to do with an obsolete version I manage in my source. The following is the only place in my source where I set the SSL version and it supports the version 1.2 function TConnectionClass.GetToken: Boolean; var LJSONObject: TJSONObject; LJSONValue: TJSONValue; {$IFDEF VER230} LJSONPair: TJSONPair; {$ENDIF} LJSONToken: TJSONValue; IdHTTP: TIdHTTP; IdSSL: TIdSSLIOHandlerSocketOpenSSL; IdLogFile: TIdLogFile; sList: TStringList; begin Result := False; FToken := ''; IdHTTP := TIdHTTP.Create(nil); try IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoForceEncodeParams]; IdHTTP.HandleRedirects := True; IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP); IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; IdHTTP.IOHandler := IdSSL; IdLogFile := TIdLogFile.Create(IdHTTP); IdLogFile.Filename := 'c:\' + ChangeFileExt(ExtractFileName(ParamStr(0)), '.log'); IdLogFile.Active := True; IdHTTP.Intercept := IdLogFile; IdHTTP.Request.Accept := 'application/json'; IdHTTP.Request.CustomHeaders.Values['X-Application'] := cBetfair_AppKey; IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded'; sList := TStringList.Create; try sList.Add('username=' + FUserID); sList.Add('password=' + FPassword); LJSONValue := TJSONObject.ParseJSONValue(IdHTTP.Post(URL_LOGIN, sList)); try if LJSONValue is TJSONObject then begin LJSONObject := TJSONObject(LJSONValue); {$IFDEF VER230} LJSONPair := LJSONObject.Get('token'); if LJSONPair <> nil then LJSONToken := LJSONPair.JsonValue else LJSONToken := nil; {$ELSE} LJSONToken := LJSONObject.Values['token']; {$ENDIF} if LJSONToken <> nil then FToken := LJSONToken.Value; end; finally LJSONValue.Free; end; finally sList.Free; end; finally IdHTTP.Free; end; Result := (FToken <> ''); end; I also tried to amend the options into IdSSL.SSLOptions.SSLVersions := [sslvTLSv1_2]; but the error is still the same Then I updated the SSL .dll files in my system32 folder by downloading the latest version of libeay32.dll and ssleay32.dll from here https://indy.fulgan.com/SSL/ and replace the old files with the new ones. I have double-checked and now the .dll have Version file 1.0.2.21 and Version 1.0.2u Despite these attempts, the application still throws me the same exception. Is there anything else I should change? Many thanks Alberto
  2. Alberto Paganini

    Transform string into TDateTime

    I would like to replace JsonStringToDateTime with JsonStringToDateTime2 in my code because it is significantly faster. Here below a small example program MyTest; {$APPTYPE CONSOLE} {$R *.res} uses madExcept, madLinkDisAsm, madListHardware, madListProcesses, madListModules, System.SysUtils, Soap.XSBuiltIns; const gNullDate: TDateTime = 2 + 365; {*****************************************************************************************} function JsonStringToDateTime(S: widestring): TDateTime; {*****************************************************************************************} begin result := gNullDate; with TXSDateTime.Create() do begin try XSToNative(S); result := AsDateTime; finally Free; end; end; end; {*****************************************************************************************} function JsonStringToDateTime2(S: widestring): TDateTime; {*****************************************************************************************} var Dummy: string; begin try Result := EncodeDate(StrToInt(Copy(S, 1, 4)), StrToInt(Copy(S, 6, 2)), StrToInt(Copy(S, 9, 2))); Result := EncodeTime(StrToInt(Copy(S, 12, 2)), StrToInt(Copy(S, 15, 2)), StrToInt(Copy(S, 18, 2)), 0); except result := gNullDate; end; end; var TimeResult: TDateTime; a: TDateTime; b: TDateTime; I: Integer; hh, mm, ss, ms: Word; begin a := Now; for i := 0 to 5000 do begin TimeResult := JsonStringToDateTime('2021-02-11T02:25:00.000Z'); end; b := Now - a; DecodeTime(b, Hh, mm, ss, ms); Writeln('JsonStringToDateTime sec:' + IntToStr(ss) + ' msec:+' + IntToStr(ms)); a := Now; for i := 0 to 5000 do begin TimeResult := JsonStringToDateTime2('2021-02-11T02:25:00.000Z'); end; b := Now - a; DecodeTime(b, Hh, mm, ss, ms); Writeln('JsonStringToDateTime2 sec:' + IntToStr(ss) + ' msec:+' + IntToStr(ms)); Readln; end. Can you see any drawbacks in JsonStringToDateTime2 ? Many thanks Alberto
  3. Alberto Paganini

    Transform string into TDateTime

    One important part of the application I am working on is to retrieve data from a service provider as often as possible. The function JsonStringToDateTime is called tens of thousands of times from several processes in a few minutes in order to parse the data provided therefore, the faster the function the more times the application can retrieve data. This is one of the most "popular" functions but there are others similar and called as much as this one and I will look into these too. I understand this is just a quick fix, the next step would be to refactor the logic of the application in order to retrieve data and parse it in a better way (maybe parse only the necessary data) but that would require more time and I would like to see some results in the short run. However, for the records here the three versions with the usual quick test. program SoapTest; {$APPTYPE CONSOLE} {$R *.res} uses madExcept, madLinkDisAsm, madListHardware, madListProcesses, madListModules, System.SysUtils, Soap.XSBuiltIns; const gOutOfScopelDate: TDateTime = 2 + 365; {*****************************************************************************************} function JsonStringToDateTime(S: widestring): TDateTime; {*****************************************************************************************} begin result := gOutOfScopelDate; with TXSDateTime.Create() do begin try XSToNative(S); result := AsDateTime; finally Free; end; end; end; {*****************************************************************************************} function JsonStringToDateTime2(S: widestring): TDateTime; {*****************************************************************************************} begin try Result := EncodeDate(StrToInt(Copy(S, 1, 4)), StrToInt(Copy(S, 6, 2)), StrToInt(Copy(S, 9, 2))); Result := Result + EncodeTime(StrToInt(Copy(S, 12, 2)), StrToInt(Copy(S, 15, 2)), StrToInt(Copy(S, 18, 2)), 0); except result := gOutOfScopelDate; end; end; {*****************************************************************************************} function JsonStringToDateTime3(Src: widestring): TDateTime; {*****************************************************************************************} function CharToDigit(aChar: Char): Integer; inline; begin Result := Ord(aChar) - Ord('0'); end; var S: string; pSrc, pDest: PChar; begin SetLength(S, 8); pSrc := Pointer(Src); pDest := Pointer(S); try //date pDest^ := pSrc^; (pDest + 1)^ := (pSrc + 1)^; (pDest + 2)^ := (pSrc + 2)^; (pDest + 3)^ := (pSrc + 3)^; (pDest + 4)^ := (pSrc + 5)^; (pDest + 5)^ := (pSrc + 6)^; (pDest + 6)^ := (pSrc + 8)^; (pDest + 7)^ := (pSrc + 9)^; {(*} Result := EncodeDate( // year CharToDigit(S[1]) * 1000 + CharToDigit(S[2]) * 100 + CharToDigit(S[3]) * 10 +CharToDigit(S[4]), CharToDigit(S[5]) * 10 +CharToDigit(S[6]), //month CharToDigit(S[7]) * 10 +CharToDigit(S[8])); //date {*)} //time pDest^ := (pSrc + 11)^; (pDest + 1)^ := (pSrc + 12)^; (pDest + 2)^ := (pSrc + 14)^; (pDest + 3)^ := (pSrc + 15)^; (pDest + 4)^ := (pSrc + 17)^; (pDest + 5)^ := (pSrc + 18)^; {(*} Result:=Result+EncodeTime( CharToDigit(S[1]) * 10 +CharToDigit(S[2]), //hh CharToDigit(S[3]) * 10 +CharToDigit(S[4]), //mm CharToDigit(S[5]) * 10 +CharToDigit(S[6]), //ss 0//ms ); {*)} except result := gOutOfScopelDate; end; end; const aTimes = 5000; var TimeResult: TDateTime; a: TDateTime; b: TDateTime; I: Integer; hh, mm, ss, ms: Word; begin a := Now; for i := 0 to aTimes do begin TimeResult := JsonStringToDateTime('2021-02-11T02:25:00.000Z'); end; b := Now - a; DecodeTime(b, Hh, mm, ss, ms); Writeln('JsonStringToDateTime sec:' + IntToStr(ss) + ' msec:' + IntToStr(ms)); a := Now; for i := 0 to aTimes do begin TimeResult := JsonStringToDateTime2('2023-03-13T02:25:00.000Z'); end; b := Now - a; DecodeTime(b, Hh, mm, ss, ms); Writeln('JsonStringToDateTime2 sec:' + IntToStr(ss) + ' msec:' + IntToStr(ms)); a := Now; for i := 0 to aTimes do begin TimeResult := JsonStringToDateTime3('2023-03-13T02:25:00.000Z'); end; b := Now - a; DecodeTime(b, Hh, mm, ss, ms); Writeln('JsonStringToDateTime3 sec:' + IntToStr(ss) + ' msec:' + IntToStr(ms)); Readln; end. In all honesty, I didn't think that replacing Copy would make a big difference, obviously, I was wrong. I was under the impression that StrToInt could be replaced but I didn't know how to do. The improvement that the changes suggested by @Fr0sT.Brutal make is very big! Thank you. I hope I have understood all the suggestions. Many thanks Alberto
  4. Alberto Paganini

    Transform string into TDateTime

    I was not able to think of anything better in order to transform parts of a string into numbers and then put everything into a TDateTime. Is there an alternative to that, considering that performance is important here?
  5. Alberto Paganini

    Transform string into TDateTime

    @emailx45 That is technically correct, there is no "null date" in Delphi. The software I am working on should receive dates >01/01/1901 only. If something goes wrong the procedure assigns 01/01/1901 to Result. Somewhere else the application checks Result and if it is is 01/01/1901 treats this accordingly. I should have renamed it something like gOutOfScopeDate
  6. Alberto Paganini

    Transform string into TDateTime

    Yes, sorry I forgot to remove it. Any particular reason for that? I am not trying to be funny here. I just want to understand why this is better. Yes, not a problem in my case.
  7. Alberto Paganini

    tlsv1 alert protocol version

    Ok, I have found out the final issues. It turned out that Windows 7 does not enable TLS 1.2 by default. You have to do it manually by following this process https://support.site24x7.com/portal/en/kb/articles/how-to-check-if-tls-1-2-is-enabled At this stage, the function above started working. Next, I had to add one extra line in a third party code every time TIdSSLIOHandlerSocketOpenSSL is created. The extra line is sslIOHandler.SSLOptions.SSLVersions := [sslvTLSv1_2]; After that, the application started working as before. @Remy LebeauThank you for your help Alberto
  8. Alberto Paganini

    tlsv1 alert protocol version

    It seems I am making progress with this. I have amended the class in a way the application loads the .DLLs residing in the application folder. This is achieved with IdSSLOpenSSLHeaders.IdOpenSSLSetLibPath(). (Thanks to Remi for this post https://stackoverflow.com/questions/13269169/how-to-use-a-dll-outside-of-the-system-path) IdSSLOpenSSL.OpenSSLVersion; confirms now "OpenSSL 1.0.2q 20 Nov 2018" is loaded and this collides with the .DLLs version in the application folder. As far as I can see from the https://www.openssl.org/news/openssl-1.0.2-notes.html the OpenSSL 1.0.2 supports TLS 1.2 for a long time (Jan 2015). Despite that my application still throws "tlsv1 alert protocol version" Is there anything else I should be aware of ? The amended source code is here above if anybody may be interested. Many thanks Alberto function TConnectionClass.GetToken: Boolean; var LJSONObject: TJSONObject; LJSONValue: TJSONValue; {$IFDEF VER230} LJSONPair: TJSONPair; {$ENDIF} LJSONToken: TJSONValue; IdHTTP: TIdHTTP; IdSSL: TIdSSLIOHandlerSocketOpenSSL; IdLogFile: TIdLogFile; sList: TStringList; Path:string; OpenSSLVersion:string; begin begin Result := False; FToken := ''; Path:=ExtractFilePath(ParamStr(0)); IdSSLOpenSSLHeaders.IdOpenSSLSetLibPath(Path); OpenSSLVersion:= IdSSLOpenSSL.OpenSSLVersion; IdHTTP := TIdHTTP.Create(nil); try IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoForceEncodeParams]; IdHTTP.HandleRedirects := True; IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP); IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; IdHTTP.IOHandler := IdSSL; IdLogFile := TIdLogFile.Create(IdHTTP); IdLogFile.Filename := 'c:\' + ChangeFileExt(ExtractFileName(ParamStr(0)), '.log'); IdLogFile.Active := True; IdHTTP.Intercept := IdLogFile; IdHTTP.Request.Accept := 'application/json'; IdHTTP.Request.CustomHeaders.Values['X-Application'] := cBetfair_AppKey; IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded'; sList := TStringList.Create; try sList.Add('username=' + FUserID); sList.Add('password=' + FPassword); LJSONValue := TJSONObject.ParseJSONValue(IdHTTP.Post(URL_LOGIN, sList)); try if LJSONValue is TJSONObject then begin LJSONObject := TJSONObject(LJSONValue); {$IFDEF VER230} LJSONPair := LJSONObject.Get('token'); if LJSONPair <> nil then LJSONToken := LJSONPair.JsonValue else LJSONToken := nil; {$ELSE} LJSONToken := LJSONObject.Values['token']; {$ENDIF} if LJSONToken <> nil then FToken := LJSONToken.Value; end; finally LJSONValue.Free; end; finally sList.Free; end; finally IdHTTP.Free; end; Result := (FToken <> ''); end;
  9. Alberto Paganini

    tlsv1 alert protocol version

    @Remy Lebeau Yes, this is the issue. I have just found out from the provider that "From 1st December only TLS 1.2 and above will be supported". I renamed libeay32.dll and ssleay32.dll in the system32 folder, downloaded the two .DLLs from Github, placed them in the application folder but the error is still the same. The version I have reported is the one found in the properties of the .DLL files. However, the OpenSSLVersion function reports OpenSSL 1.0.0d 8 Feb 2011, and that explains why the .exe is not working any longer. Does it mean my application uses other SSL .DLLs on my hard disk? (yes, I have a few installed by other third-party software. Each of them resides in the relevant application folder) How can I tell my application that the correct .DLL resides in the same folder of the application itself? I don't want to delete the other .DLLs in order not to stop the other applications to work. Many thanks. Alberto
  10. I use FastMM4 to check memory leaks and I noticed the report file " MyApplicatoin+MemoryManager_EventLog.txt" is not deleted automatically once I have amended the source and I run my application again. This way if there are still memory leaks they are added to the existing report and this confuses me. Is there an option in FastMM4 to achieve that I am not aware of or do I have to rely on DeleteFile(...) when I run the application ? Many thanks Alberto
  11. Alberto Paganini

    Delete FastMM4 MemoryManager_EventLog.txt

    Thank you David, this did the trick !
  12. Alberto Paganini

    Delete FastMM4 MemoryManager_EventLog.txt

    I did and created this test: program Project3; {$APPTYPE CONSOLE} {$R *.res} uses FastMM4, system.Classes, System.SysUtils; var t:tstringlist; begin t:=TStringList.Create; end. I ran it a number of times and the new reports are appended to the old ones. Obviously there must be something wrong in my FastMM4 settings. I will look into that.
  13. Alberto Paganini

    Varialbe set to nil

    I am just investigating on how this works. Having the following code: program Project3; {$APPTYPE CONSOLE} {$R *.res} uses FastMM4, system.Classes, System.SysUtils; type MyInterface2 = interface ['{8E7BC71F-55EE-4345-8A5B-42C433BE9EBA}'] procedure print; end; MyInterface1 = interface ['{607A66A6-D68C-4980-9FB1-83B325EE9A91}'] procedure SetInterface2(aInterface: MyInterface2); end; TMyClass1 = class(TInterfacedObject, MyInterface1) private RefToInterFace2: MyInterface2; public procedure SetInterface2(aInterface: MyInterface2); end; TMyClass2 = class(TInterfacedObject, MyInterface2) constructor Create(int: MyInterface1); reintroduce; procedure print; end; { TMyClass1 } procedure TMyClass2.print; begin Writeln('TMyClass2.print'); Sleep(1500); end; { TMyClass2 } procedure TMyClass1.SetInterface2(aInterface: MyInterface2); begin RefToInterFace2 := aInterface; RefToInterFace2.print; end; constructor TMyClass2.Create(int: MyInterface1); begin inherited Create; int.SetInterface2(Self); end; var aMyClass1: MyInterface1; aMyClass2: MyInterface2; begin aMyClass1 := TMyClass1.Create; aMyClass2 := TMyClass2.Create(aMyClass1); end. How does TMyClass1 know there is RefToInterFace2 to be set to nil in order to prevent an AV error? Where is RefToInterFace2:=nil invoked from? I mean, TMyClass1 ancestor does not know about RefToInterFace2 and RefToInterFace2 is not set to nil anywhere in TMyClass1 Many thanks Alberto
  14. Alberto Paganini

    Varialbe set to nil

    @Stefan Glienke I have amended the example according to your advice. program Project3; {$APPTYPE CONSOLE} {$R *.res} uses FastMM4, system.Classes, System.SysUtils, Spring; type MyInterface2 = interface ['{8E7BC71F-55EE-4345-8A5B-42C433BE9EBA}'] procedure print; end; MyInterface1 = interface ['{607A66A6-D68C-4980-9FB1-83B325EE9A91}'] procedure SetInterface2(const aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass1 = class(TInterfacedObject, MyInterface1) private //RefToInterFace2: Pointer{MyInterface2}; RefToInterFace2: Weak<MyInterface2>; public procedure SetInterface2(const aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass2 = class(TInterfacedObject, MyInterface2) private // Ref: MyInterface1; ref: Weak<MyInterface1>; public constructor Create(int: MyInterface1); reintroduce; destructor Destroy; override; // this can be deleted procedure print; end; { TMyClass1 } procedure TMyClass2.print; begin Writeln('TMyClass2.print'); Sleep(1500); end; { TMyClass2 } procedure TMyClass1.DoSomethingWithInterface2; begin if RefToInterFace2 <> nil then begin // MyInterface2(RefToInterFace2).print; RefToInterFace2.Target.print; end; end; procedure TMyClass1.SetInterface2(const aInterface: MyInterface2); begin // RefToInterFace2 := Pointer(aInterface); RefToInterFace2.Target := aInterface; end; constructor TMyClass2.Create(int: MyInterface1); begin inherited Create; if int <> nil then begin Ref.Create(int); Ref.Target.SetInterface2(Self); end; end; destructor TMyClass2.Destroy; begin // if Ref <> nil then // MyInterface1(Ref).SetInterface2(nil); inherited Destroy; end; var aMyClass1: MyInterface1; aMyClass2: MyInterface2; begin aMyClass1 := TMyClass1.Create; aMyClass2 := TMyClass2.Create(aMyClass1); aMyClass1.DoSomethingWithInterface2; end. Is that acceptable? In which case the pointer approach proposed by Remi may cause AV errors? Many thanks Alberto
  15. Alberto Paganini

    Varialbe set to nil

    Hi Remi, I had to amend your code with the following one in order to make the example compile by DX2 procedure TMyClass1.SetInterface2(const aInterface: MyInterface2); begin RefToInterFace2 := Pointer(aInterface); end; The rest is all spot on! I suppose that the advantage to use the second solution is you can call any method of MyInterface1 throughout TMyClass2 by using Ref while in the first one you can do it only in TMyClass2.Create as MyInterface1 is not stored in a variable. Thank you very much for taking the time. Alberto
  16. Alberto Paganini

    Varialbe set to nil

    But If I store the interface in a local variable in TMyClass2, Ref in my example, I have a leak instead. It seems the RTL cannot have access to it. The only way to release the variable is to use a method and call this method from the main begin..end. Here below the minimum example. program Project3; {$APPTYPE CONSOLE} {$R *.res} uses FastMM4, system.Classes, System.SysUtils; type MyInterface2 = interface ['{8E7BC71F-55EE-4345-8A5B-42C433BE9EBA}'] procedure print; end; MyInterface1 = interface ['{607A66A6-D68C-4980-9FB1-83B325EE9A91}'] procedure SetInterface2(aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass1 = class(TInterfacedObject, MyInterface1) private RefToInterFace2: MyInterface2; public procedure SetInterface2(aInterface: MyInterface2); procedure DoSomethingWithInterface2; destructor Destroy;override; end; TMyClass2 = class(TInterfacedObject, MyInterface2) private Ref: MyInterface1; public constructor Create(int: MyInterface1); reintroduce; procedure print; end; { TMyClass1 } procedure TMyClass2.print; begin Writeln('TMyClass2.print'); Sleep(1500); end; { TMyClass2 } destructor TMyClass1.Destroy; begin SetInterface2(nil);// this isn not executed inherited; end; procedure TMyClass1.DoSomethingWithInterface2; begin RefToInterFace2.print; end; procedure TMyClass1.SetInterface2(aInterface: MyInterface2); begin RefToInterFace2 := aInterface; end; constructor TMyClass2.Create(int: MyInterface1); begin inherited Create; Ref := int; Ref.SetInterface2(Self); end; var aMyClass1: MyInterface1; aMyClass2: MyInterface2; begin aMyClass1 := TMyClass1.Create; aMyClass2 := TMyClass2.Create(aMyClass1); aMyClass1.DoSomethingWithInterface2; aMyClass1.SetInterface2(nil);// this releases the variable end. Is this the only way to release the pointer in the variable or is there a better way? If I add a destructor that is not executed. Many thanks Alberto
  17. I have one more memory leak to fix. Here is the report from FastMM4 --------------------------------2020/6/23 14:34:20-------------------------------- A memory block has been leaked. The size is: 116 This block was allocated by thread 0x157C, and the stack trace (return addresses) at the time was: 404DA6 [System.pas][System][@GetMem$qqri][3454] 408BBB [System.pas][System][@NewUnicodeString$qqri][19030] 408DEC [System.pas][System][@UStrFromPWCharLen$qqrr20System.UnicodeStringpbi][19697] 40A0C9 [System.pas][System][@UStrCopy$qqrx20System.UnicodeStringii][24873] E5CAD3 [Data.DBXPlatform.pas][Data.DBXPlatform][Dbxplatform.TDBXStringBuffer.ToString$qqrv][697] E8E8BC [Data.DBXJSON.pas][Data.DBXJSON][Dbxjson.TJSONString.Value$qqrv][1541] E9297C [uBetFair.pas][uBetFair][TBetFairApi.GetToken$qqrv][222] E92E90 [uTokenBL.pas][uTokenBL][TTokenBL.GetToken$qqrv][30] E5B593 [Main.pas][Main][TMainForm.FormActivate$qqrp14System.TObject][402] 557ED9 [Vcl.Forms][Forms.TCustomForm.Activate$qqrv] 558F99 [Vcl.Forms][Forms.TCustomForm.CMActivate$qqrr27Winapi.Messages.TWMNoParams] 4B4930 [Vcl.Controls.pas][Vcl.Controls][Controls.TControl.WndProc$qqrr24Winapi.Messages.TMessage][7204] 75C3C431 [Unknown function at gapfnScSendMessage] 75C3C3EE [Unknown function at gapfnScSendMessage] 75C3C380 [Unknown function at gapfnScSendMessage] The block is currently used for an object of class: UnicodeString The allocation number is: 60573 Current memory dump of 256 bytes starting at pointer address 7F2C7580: B0 04 02 00 01 00 00 00 2C 00 00 00 53 00 31 00 45 00 41 00 66 00 64 00 33 00 33 00 51 00 61 00 57 00 35 00 77 00 36 00 53 00 49 00 62 00 2F 00 69 00 4B 00 4F 00 6E 00 6F 00 32 00 74 00 47 00 36 00 74 00 63 00 36 00 4F 00 51 00 69 00 45 00 74 00 73 00 6C 00 6E 00 70 00 45 00 57 00 45 00 77 00 3D 00 00 00 31 8D A4 0F 80 80 80 80 80 80 80 80 80 80 80 80 80 80 00 00 00 00 21 80 2C 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E1 0C 01 00 A6 4D 40 00 0F 6E 40 00 7E 74 40 00 2B 97 DB 00 6D 80 DC 00 9B D1 CD 00 7A 3A D5 00 68 39 CD 00 D4 39 CD 00 62 8F DC 00 69 91 DC 00 D7 FE B8 00 06 68 D4 00 1E FD B8 00 37 AC DD 00 7C 15 00 00 7C 15 00 00 C2 4D 40 00 2D 6E 40 00 C9 74 40 00 F1 EF D4 00 73 6E 40 00 1F D2 CD 00 CA D1 CD 00 BE 3A D5 00 73 6E 40 00 86 39 CD 00 ° . . . . . . . , . . . S . 1 . E . A . f . d . 3 . 3 . Q . a . W . 5 . w . 6 . S . I . b . / . i . K . O . n . o . 2 . t . G . 6 . t . c . 6 . O . Q . i . E . t . s . l . n . p . E . W . E . w . = . . . 1 � ¤ . € € € € € € € € € € € € € € . . . . ! € , � . . . . . . . . . . . . . . . . á . . . ¦ M @ . . n @ . ~ t @ . + — Û . m € Ü . › Ñ Í . z : Õ . h 9 Í . Ô 9 Í . b � Ü . i ‘ Ü . × þ ¸ . . h Ô . . ý ¸ . 7 ¬ Ý . | . . . | . . . Â M @ . - n @ . É t @ . ñ ï Ô . s n @ . . Ò Í . Ê Ñ Í . ¾ : Õ . s n @ . † 9 Í . The culprit seems to be TBetFairApi.GetToken and here is the code: function TBetFairApi.GetToken: Boolean; var LJSONObject: TJSONObject; LJSONValue: TJSONValue; s:string; begin Result := False; FidHTTP := TidHTTP.Create(nil); FidHTTP.HTTPOptions := FidHTTP.HTTPOptions - [hoForceEncodeParams]; FidHTTP.Intercept := TIdLogFile.Create(FidHTTP); s:=ExtractFileName(ParamStr((0))); s:=Copy(s,1,Pos('.',s)-1); TIdLogFile(FidHTTP.Intercept).Filename := 'c:\' + s + '.log'; TIdLogFile(FidHTTP.Intercept).Active := true; FList := TStringList.Create; FIdSSL := TIdSSLIOHandlerSocketOpenSSL.Create; FIdSSL.SSLOptions.Method:=sslvTLSv1_2; FidHTTP.Request.Accept := 'application/json'; FidHTTP.Request.CustomHeaders.Clear; FidHTTP.Request.CustomHeaders.AddValue('X-Application', cBetfair_AppKey); FList.Clear; FList.Add('username=' + FUserID); FList.Add('password=' + FPassword); FidHTTP.Request.ContentType := 'application/x-www-form-urlencoded'; FidHTTP.HandleRedirects := true; {$IFDEF VER230} FidHTTP.IOHandler := FIdSSL; {$ENDIF} LJSONObject := TJSONObject.ParseJSONValue(FidHTTP.Post(URL_LOGIN, FList)) as TJSONObject; try if Assigned(LJSONObject) then begin LJSONValue := LJSONObject.{$IFDEF VER230}Get('token').JsonValue{$ELSE}Values['token']{$ENDIF}; if Assigned(LJSONValue) then begin FToken := LJSONValue.Value; Result := True; end; end; finally FreeAndNil(LJSONObject); end; FreeAndNil(FidHTTP); {$IFDEF VER230} FreeAndNil(FIdSSL); {$ENDIF} FreeAndNil(FList); end; There are 4 .Creates: FidHTTP := TidHTTP.Create(nil); FList := TStringList.Create; FIdSSL := TIdSSLIOHandlerSocketOpenSSL.Create; FidHTTP.Intercept := TIdLogFile.Create(FidHTTP); The first three are freed at the end of the procedure and TIdLogFile.Create(FidHTTP) should be freed automatically when FidHTTP is freed. I cannot see any memory leak here but obviously I am wrong. Can you see anything I cannot? Alberto
  18. Alberto Paganini

    One more memory leak and FastMM4

    Sure, here it is in attach. I just wanted to avoid to attach a minimal example every time. Test3.zip
  19. Alberto Paganini

    One more memory leak and FastMM4

    Ok, I have also implemented QueryInterface, AfterConstruction, BeforeDestruction and NewInstance in my DataModule In this case, there are no leaks in both cases, with our without .AsSingleton. At this stage, I started implementing the other classes, one by one and .AsSingleton in my datamodule started making a difference when I arrived at the class TBetFairAPINGHorseFunctionSimulationBL TBetFairAPINGHorseFunctionSimulationBL = class(TInterfacedObject, ILBS_BetfairAPINGFunctionsBL) private FDM: IMainDMTEST; public constructor Create(aDM: IMainDMTEST); reintroduce; destructor Destroy; override; end; constructor TBetFairAPINGHorseFunctionSimulationBL.Create(aDM: IMainDMTEST); begin inherited create; FDM := aDM; end; destructor TBetFairAPINGHorseFunctionSimulationBL.Destroy; begin FDM := nil; inherited; end; I have added TBetFairAPINGHorseFunctionSimulationBL.Destroy and set FDM=nil in there but this destructor is never executed, don't know why as this class inherits from TInterfacedObject? As the last attempt, I have added a line at the end of the program to try to release the IMainDMTest at the very end of the application but the leak is still there. var FScalpingBL: IScalpingBL; Test: IMainDMTEST; begin RegisterTypes(GlobalContainer); Test := GlobalContainer.Resolve<IMainDMTEST>; FScalpingBL := globalcontainer.Resolve<IScalpingBL>; Test := nil; end. If I remove .AsSingleton when I resolve the interface then I have no more leaks but I have to keep .AsSingleton because this class holds/manages data and several classes have access to it.
  20. Alberto Paganini

    One more memory leak and FastMM4

    Sorry I didn't explain properly. _AddRef and _Release are implemented in my example. It was the other procedures from Jeroen's example that have been left out as you advised. If I create the object as per your example above then there is no leak. I suppose the issue is in the way I resolve the interface in the Spring container.
  21. Alberto Paganini

    One more memory leak and FastMM4

    I looked into TInterfacedObject again and implemented _AddRef and _Release in the same way, leaving out the remaining procedures, the example attached to this post is the updated minimum example. Unfortunately, I still have the same leaks. I started that thread. Now I know why I cannot have access to it any longer. Test2.zip
  22. Alberto Paganini

    One more memory leak and FastMM4

    I have managed to make a minimal example that leaks. I will attach it here. I have found this article on how to make a TDataModule reference counting written by Jeroen Pluimers https://wiert.me/2009/08/10/delphi-using-fastmm4-part-2-tdatamodule-descendants-exposing-interfaces-or-the-introduction-of-a-tinterfaceddatamodule/ and tried to implement it in MainDMSimulation in the example. Despite that, the example is still leaking. I think is I am still doing something wrong when it comes to making TDataModule reference counting or in the way I resolve the interface in the Spring4D container (or maybe both). Test.zip
  23. Hello Everybody, I am looking for the equivalent of the Excel function LINEST in Delphi but I cannot find it. Do you know any library has it? Many thanks Alberto
  24. Alberto Paganini

    One more memory leak and FastMM4

    TBetFairApi.GetToken works perfectly now, thank you very much! Now I have to debug the rest because, you are right, there is another leak elsewhere. I will work on a minimal example. Thank you again, everybody. I have learnt a few new things !
  25. Alberto Paganini

    One more memory leak and FastMM4

    I did more investigation on the leaking instruction and found out that LJSONValue.Value; is UnicodeString. As FToken was a string I changed FToken into a UnicodeString but this didn't change anything. I don't know if this can help.
×