Jump to content

aehimself

Members
  • Content Count

    1090
  • Joined

  • Last visited

  • Days Won

    23

Posts posted by aehimself


  1. 41 minutes ago, Fr0sT.Brutal said:

    When function encounters it, it replaces slash to current DateSeparator. This way date format becomes independent from date separator change so user can just modify date separator to get desired format instead of changing whole date format.

    I don't know if I follow you here. Does it mean that I must never use FormatSettings.ShortDateFormat as it is, only FormatSettings.ShortDateFormat.Replace('/', FormatSettings.DateSeparator)?

     

    The reason I'm asking is that 3rd-party controls (like DevExpress) started to show (and save) dates differently just because of compiling under D11. That makes me think that either the above statement is incorrect, or all 3rd-party controls handle this incorrectly.

    To correct this I had to manually correct FormatSettings.ShortDateFormat but that immediately breaks all logic which relies on TFormatSettings.Create and StrToDate - which our tool is using a lot.

     

    54 minutes ago, Fr0sT.Brutal said:

    Regarding the separator itself, probably it couldn't be retrieved from the system (as https://learn.microsoft.com/en-us/windows/win32/intl/locale-sdate says, this constant is deprecated) so it gets value of slash.

    The reason Delphi 10.4.2 could not get it is because Embarcadero didn't provide a buffer big enough. As both versions are calling the same WinApi function, changing the buffer to array[0..1] retrieving will fail on D11 aswell; calling RaiseLastOSError will reveal the reason:

    image.png.66ad55a8e820d3460c36f5cfa655ce41.png

     

    Maybe I should have been more precise. I personally don't care about the date separator alone, only the malformation of ShortDateFormat because of it, which seem to affect appearance and logic.


  2. Hello,

     

    I am in the process of verifying if we can update our environment to use Delphi 11.2 instead of the current 10.4.2. However compilation was successful, even the very basic tests failed because of an incorrect date format detected by Delphi.

     

    Reproduction is really easy, just execute a single line:

    TFormatSettings.Create(1038);

    Result on Delphi 11.2:

    image.thumb.png.b6c2debd0fc4d5aa201220371e7ea388.png

     

    On 10.4.2:

    image.thumb.png.0ccc1e78f6b2e6f68da82ffd9c99b50e.png

     

    So 11.2 correctly detects the date separator as "." in the locale, however the very next line will call this method, which will replace all separators with a forward slash instead:

      procedure FixDateSeparator(var DateFormat: string);
      var
        P: PChar;
        InsideLiteral: Boolean;
      begin
        InsideLiteral := False;
        P := PChar(DateFormat);
        if P = nil then
          Exit;
        while P^ <> #0 do
        begin
          if P^ = '''' then
            InsideLiteral := not InsideLiteral;
          if (P^ = Separator) and (not InsideLiteral) then
            P^ := '/';
          Inc(P);
        end;
      end;

    So because 10.4.2 could not detect the separator, it returns the correct date format. 11.2 detects the separator, ruining the date format instead. The WinAPi definition is the same, buffer is different in the implementation:

    function GetLocaleInfo; external kernel32 name 'GetLocaleInfoW';
    
    // Delphi 10.4.2 MSWindows implementation
    function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
    var
      Buffer: array[0..1] of Char;
    begin
      if GetLocaleInfo(Locale, LocaleType, Buffer, 2) > 0 then
        Result := Buffer[0] else
        Result := Default;
    end;
    
    // Delphi 11.2 MSWindows implementation
    function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
    var
      Buffer: array[0..2] of Char;
    begin
      if GetLocaleInfo(Locale, LocaleType, Buffer, 3) > 0 then
        Result := Buffer[0] else
        Result := Default;
    end;

    I can not wrap my head around this. I believe it should be a faulty FixDateSeparator implementation, as it makes no sense to change the correct separators to incorrect ones based on the selected locale.

     

    Is this an issue in Delphi and I should raise a ticket or maybe I'm missing something...?


  3. MyFileSourceSize := MyFileStreamSource.Size;

    Is unnecessary, MyFileSourceSize.Size - MyFileSourceSize.Position will always return the unprocessed bytes.

     

      MyFileStreamSource.Read(MyBuffer[0], MyBufferSize); // read and put on the next position...

    Make sure you also check if .Read returns MyBufferSize. Otherwise you will have "rouge" data what you will write back to the destination, effectively corrupting the output.

     

    Also, the pseudocode above won't handle data, which is interrupted by the end of a buffer.


  4. 9 minutes ago, robertjohns said:

    It is 32 bit and the file size is 3GB

    a 32 bit process can allocate a total of 2 GBs of RAM (or 3 if it's large address aware). Loading 3 GBs of data in the memory would use that up by itself, leaving no room for anything else (like the TMemoryStream class or any other variables). So it is expected.

    As @PeterBelow advised, build a 64 bit executable, or use FileStreams instead of loading everything in the memory.


  5. As a general rule of thumb, never call Application.ProcessMessages by yourself. Let your thread "ping" back with updates from time to time with TThread.Synchronize, you can even have a TTimer in your VCL thread to poll updates from the Thread via public properties. Just make sure you are using some king of locking (TCriticalSection, TMonitor, mutexes, events, etc.) when reading from and writing to these variables.


  6. I discovered a trick. If you put SelStart before a link and set SelLength to 1, RichEdit will select the FULL link text... like

    Quote

    HYPERLINK "https://en.delphipraxis.net"Delphi Praxis

    The good news in this is that we can optimize the checking cycle to use this feature:

      Self.SelStart := Integer.MaxValue;
      max := Self.SelStart;
    
      a := 0;
      While a < max Do
      Begin
        Self.SelStart := a;
        Self.SelLength := 1;
    
        If Self.SelAttributes.Color = clWindowtext Then
          Self.SelAttributes.Color := TStyleManager.ActiveStyle.GetStyleFontColor(sfEditBoxTextNormal);
    
        a := Self.SelStart + Self.SelLength;
      End;

    Instead of 3000+ msec, the cycle now finishes in 500!

     

    Still not ideal, but a lot more bearable 🙂


  7. 1 hour ago, robertjohns said:

    How can I find all instances of a string in a file , replace all instance of the string and save the copy of the file in Delphi

    TFile.WriteAllText('OutFileName.txt', TFile.ReadAllText('FileName.txt').Replace('Replace from', 'Replace To'));


  8. Ok, I'm getting baffled. Time is lost in the space-time continuum.

     

    I added a ton of measuring to the code, looks like this now:

    Procedure TRichEdit.StreamIn(Var Msg: TMessage);
    Var
     a, b: Integer;
     ro: Boolean;
     sw, fsw: TStopWatch;
     needcolor: Boolean;
     sscount, slcount, clcount, gscount, nccount: Integer;
     ssdelay, sldelay, cldelay, gsdelay, lddelay, ulddelay, loading, rodelay, moddelay, ncdelay: Int64;
     s: String;
    Begin
     fsw := TStopWatch.StartNew;
    
     sscount := 0;
     ssdelay := 0;
     slcount := 0;
     sldelay := 0;
     clcount := 0;
     cldelay := 0;
     gscount := 0;
     gsdelay := 0;
     lddelay := 0;
     ulddelay := 0;
     rodelay := 0;
     moddelay := 0;
     nccount := 0;
     ncdelay := 0;
    
     sw := TStopWatch.StartNew;
     Self.LockDrawing;
     lddelay := sw.ElapsedMilliseconds;
     Try
      ro := Self.ReadOnly;
      Try
       sw := TStopWatch.StartNew;
       Self.ReadOnly := False;
       Inc(rodelay, sw.ElapsedMilliseconds);
    
       sw := TStopWatch.StartNew;
       inherited;
       loading := sw.ElapsedMilliseconds;
      Finally
       sw := TStopWatch.StartNew;
       Self.ReadOnly := ro;
       Inc(rodelay, sw.ElapsedMilliseconds);
      End;
    
      Inc(sscount);
      sw := TStopWatch.StartNew;
      Self.SelStart := Integer.MaxValue;
      Inc(ssdelay, sw.ElapsedMilliseconds);
    
      Inc(gscount);
      sw := TStopWatch.StartNew;
      b := Self.SelStart;
      Inc(gsdelay, sw.ElapsedMilliseconds);
    
      For a := 0 To b Do
       Begin
        Inc(sscount);
        sw := TStopWatch.StartNew;
        Self.SelStart := a;
        Inc(ssdelay, sw.ElapsedMilliseconds);
    
        Inc(gscount);
        sw := TStopWatch.StartNew;
        Try
         If Self.SelStart <> a Then Continue;
        Finally
         Inc(gsdelay, sw.ElapsedMilliseconds);
        End;
    
        Inc(slcount);
        sw := TStopWatch.StartNew;
        Self.SelLength := 1;
        Inc(sldelay, sw.ElapsedMilliseconds);
    
        Inc(nccount);
        sw := TStopWatch.StartNew;
        needcolor := Self.SelAttributes.Color = clWindowtext;
        Inc(ncdelay, sw.ElapsedMilliseconds);
    
        If needcolor Then
        Begin
         Inc(clcount);
         sw := TStopWatch.StartNew;
         Self.SelAttributes.Color := TStyleManager.ActiveStyle.GetStyleFontColor(sfEditBoxTextNormal);
         Inc(cldelay, sw.ElapsedMilliseconds);
        End;
       End;
    
      Inc(sscount);
      sw := TStopWatch.StartNew;
      Self.SelStart := 0;
      Inc(ssdelay, sw.ElapsedMilliseconds);
    
      sw := TStopWatch.StartNew;
      Self.Modified := False;
      moddelay := sw.ElapsedMilliseconds;
     Finally
      sw := TStopWatch.StartNew;
      Self.UnlockDrawing;
      ulddelay := sw.ElapsedMilliseconds;
     End;
    
     sw := TStopWatch.StartNew;
     s := 'SelStart count: ' + sscount.ToString + ', total time: ' + ssdelay.ToString + ' ms' + sLineBreak +
                 'SelLength count: ' + slcount.ToString + ', total time: ' + sldelay.ToString + ' ms' + sLineBreak +
                 'NeedColor count: ' + nccount.ToString + ', total time: ' + ncdelay.ToString + ' ms' + sLineBreak +
                 'Coloring count: ' + clcount.ToString + ', total time: ' + cldelay.ToString + ' ms' + sLineBreak +
                 'GetSelStart count: ' + gscount.ToString + ', total time: ' + gsdelay.ToString + ' ms' + sLineBreak +
                 'Locking: ' + lddelay.ToString + ' ms, unlocking: ' + ulddelay.ToString + ' ms, loading: ' + loading.ToString + ' ms, read-only: ' + rodelay.ToString + ' ms, modified: ' + moddelay.ToString + ' ms' + sLineBreak +
                 'Full cycle: ' + fsw.ElapsedMilliseconds.ToString + ' ms' + sLineBreak +
                 'Building message: ' + sw.ElapsedMilliseconds.ToString + ' ms';
     ShowMessage(s);
    End;

    Loading a 7 kb RTF file with no coloring, only links and a bulleted list results:

    Quote

    SelStart count: 2140, total time: 730 ms
    SelLength count: 703, total time: 816 ms
    NeedColor count: 703, total time: 0 ms
    Coloring count: 85, total time: 5 ms
    GetSelStart count: 2139, total time: 0 ms
    Locking: 0 ms, unlocking: 0 ms, loading: 6 ms, read-only: 0 ms, modified: 0 ms
    Full cycle: 3247 ms
    Building message: 0 ms

    Setting SelStarts took 730 ms, setting SelLengths took 816 ms, a total of 1546 ms.  So how the full cycle is 3247 ms, which is more than double...?

     

    Loading the same with no VCL styles active makes it even more visible:

    Quote

     

    SelStart count: 2140, total time: 11 ms
    SelLength count: 703, total time: 111 ms
    NeedColor count: 703, total time: 0 ms
    Coloring count: 85, total time: 0 ms
    GetSelStart count: 2139, total time: 0 ms
    Locking: 0 ms, unlocking: 0 ms, loading: 5 ms, read-only: 0 ms, modified: 0 ms
    Full cycle: 1847 ms
    Building message: 0 ms

     

    What I did not measure yet...?

     

    I know TStopWatch is not the most precise system, but it can not be missing this much...


  9. 1 hour ago, PeterBelow said:

    Have you tried to just set DefAttributes.Color  before calling the inherited StreamIn method? SelAttributes.ConsistentAttributes may also be of use if you really need to manually correct the font color.

    Yes, and unfortunately loading the stream overwrites this setting; it has no effect in the new document being loaded.

    My issue with ConsistentAttributes is that it needs a selection - and for that I need to reposition the cursor again, most probably ending up at the same slowdown.

     

    I'm attempting to experiment with EM_GETTEXTRANGE but unfortunately it doesn't seem to care about formatting at all, just returns as many characters as I desire.


  10. First of all, do a WriteLn('d.exe -b '+e1.Text+'d.exe -a '+e2.Text+'d.exe -c'+e3.Text+'d.exe -s'+e4.Text+'-d PATH_OF_DEVICE_A') and ensure that the command executes successfully.

    Next, make sure ExtractFilePath(application.ExeName) + 'bin\' folder exists (I suppose this is the "working directory".

    Finally, make sure that all the external files you reference in the command line are using ABSOLUTE paths, otherwise they must be present in the working directory.

     

    As @FPiette mentioned, neither GetDosOutput or TMemoAppendStream exists in Delphi by default. While we can guess what they do, we cannot guess their implementation. It's possible that your call is correct, only these implementations are buggy.


  11. 15 hours ago, direktor05 said:

    Then I get to "animals":[{"id".... now here gets complicated. How do I parse further to get ID and name? Parse Json further or parse Json Array? Can someone help with some example code please?

    Let's say you have a TJSONObject variable which holds the inmost object only (id and name) named innerjson:

    if innerjson.GetValue('id') <> null then fid := (innerjson.GetValue('id') As TJSONNumber).AsLargeInt;
    if innerjson.GetValue('name') <> null then fname := (innerjson.GetValue('name') As TJSONString).Value;

    if you are sure that these will always be present, you can discard the nullcheck.


  12. I really do suspect that this is a bug. The color of uncolored text is clWindowText, it's just RichEdit is rendering it in a wrong color.

    I'm not good with StyleHooks to actually fix it... I did find a workaround and however it works, it's painfully slow:

      TMyRichEdit = Class(Vcl.ComCtrls.TRichEdit)
      strict private
        Procedure StreamIn(Var Msg: TMessage); Message EM_STREAMIN;
      End;
    
    Procedure TMyRichEdit.StreamIn(Var Msg: TMessage);
    Var
      a: Integer;
    Begin
      inherited;
    
      Self.LockDrawing;
      Try
        Self.SelStart := Integer.MaxValue;
    
        For a := 0 To Self.SelStart Do
        Begin
          Self.SelStart := a;
          If Self.SelStart <> a Then
            Continue;
    
          Self.SelLength := 1;
          If Self.SelAttributes.Color = clWindowtext Then
            Self.SelAttributes.Color := TStyleManager.ActiveStyle.GetStyleFontColor(sfEditBoxTextNormal);
        End;
    
        Self.SelStart := 0;
      Finally
        Self.UnlockDrawing;
      End;
    End;

    The amount of .SelStart is causing the slowdown. Does anyone know how I can extract the start and end of consecutive blocks, where all formatting is the same? That should speed things up a LOT.

     

    I'll look into how exactly RichEdit is setting the properties of SelAttributes - maybe I can extract the attribute at a specific location without having to move the cursor, that way the only SelStart - SelLength would be at blocks what I actually have to change.


  13. Are you in control of both the data and the header? If yes, open the file for writing in your preferred method (AssignFile, TFiles, TFileStream) and write the header, then the data.

     

    If you get a file from an external source and you need to add a header, the easiest solution is a stream. Write your header and then copy the data from said file, loaded in a TFileStream.

     

     If you receive one header files and need to append data, all 3 methods (AssignFile, TFile, TFileStream) can append binary data to it.


  14. I never checked, but isn’t the Delphi localization file a regular resource DLL, without code?

     

    If this is the case (and your source is not IFDEF-ing resource stings) the same file can be used by 32 and 64 bit applications. Afaik a new DLL is only required if there is ANY code (including self-extracting, like UPX).


  15. 49 minutes ago, Fr0sT.Brutal said:

    Generics are with us since D2009 and you unlikely want to support something older as it will require too much manual job.

     

    Impressive project! Glad to see my stack trace unit 🙂 you'll probably want to update its code from https://github.com/Fr0sT-Brutal/Delphi_StackTraces/blob/master/Ice.Debug.pas since it has an improvement of excluding non-relevant entries and better comments

    There are lots of people who dislike Generics and would like to avoid having them in their projects. Using a standard TList and casting pointers can be an easy solution. In theory this whole thing could be D7 compatible if I'd invest enough free time in it. We'll see.

     

    So the author of said stack trace unit was you? 🙂 I remember having it for ages without any sign of where / who it came from so I can give appropriate credit. There were also some cyrillic characters in it which threw me totally off. As it seems it grew a lot, and since the new version is on GitHub it's easier just to remove it from mine.

    I don't like redundancy.


  16. The whole thing started with this topic. Due to lack of possibilities, I wrote my own update mechanism which noes not rely on any advanced stuff but still should be versatile enough.

     

    Since the initial version TAEUpdater got some improvements and things are looking great. Some more stuff was implemented like hash-based verification, messages, E-tag caching, separate internal, development and production channels, ability to downgrade to a previous version, etc. I'm running it in my main application for a while and it seems to do the job correctly.


    Since not all of us might update from an unauthenticated web server, now 3 different file providers are shipped: HTTP, flatfile and custom. HTTP uses Delphi's TNetHTTPClient, flatfile reads the files from a local disk and custom has all necessary events exposed via events. I also got rid of some personal dependencies (like compressing the update file or using System.Zip2) and now everything is handed to the user for the solution to be more... generic. I still use my own version discovery and comparison method to be able to determine which published one is "newer" but that is going to be my next step. As a workaround, you can call .LoadUpdateFile, check the new version in each ActualProduct.Files and call .Update manually.

     

    What it needs:

    - An update file containing the product, all its files and versions. You can build this with TAEUpdateFile.SaveToStream in AE.Updater.UpdateFile.pas

    - Update packages, which can be zipped or encrypted, on FTP, HTTP, custom protocol - doesn't matter as events should be in place for performing all these actions

     

    Plans for the future:

    - Getting rid of more dependencies, maybe it'll become a standalone updater package with no extras

    - By accomplishing the above the code will get closer and closer to be truly cross-platform (atm it's WIndows only)

    - Get rid of modern stuff (like generics) and implement proper versioning conditionals so the package can be compiled and installed on anything other than D11.2

    - Really, really basic documentation on how to use...

     

    Disclaimer: further changes are on their way. I'll possibly add / remove events / splitting the component in multiple subcomponents which might make existing .DFMs failing to be streamed. Also, publish your new versions on the Internal channel if I might break the core functionality 😞

     

    All words as one - I just wanted to give the existing Delphi community a chance to implement auto-updating feature to their applications without having to pay for the component or the backend. Feel free to check it out. And suggest ways of improvement of course 🙂

    • Like 4
    • Thanks 1
×