Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. All,

     

    I have a really strange situation here and I'm running out of ideas on what differences to check. Long story short: I have an application to access data in a database (Connection -> DataSet -> DataSource -> DBGrid, using Zeos and VCL DBGrid descendant for some custom features). In one specific codepage setting it seemingly starts to corrupt memory on ONE PC only (that I know of, that is). Details can be read here.

    Now, given the complexity the issue might be Oracle-related, PC environment-related, component-related, moon phase related or simply I messed something up in my code. My issue is that I'm running out of ideas to pinpoint the culprit.

    I'm not asking how to fix the issue (I know it's not possible without seeing the code), I'm asking for diagnostic steps; basically how would you start to debug this?

     

    What I know:

    - OS, Oracle client locale doesn't seem to affect the behavior

    - It's not thread-related, confirmed by moving all logic to VCL

    - Oracle client and server version seems to be irrelevant, the issue appeared with 12 and 19, client and server respectively

    - It is not a display issue, datasets actually contain garbage

     

    What I think:

    - I doubt the issue is in the component as the same binary acts differently on one PC only

    - Because of the same reason I would say that my code is good, too

     

    What I will do next:

    - Simple test app. Only a connection, query and a DBGrid

    - On a PC where it seems to work I'll use said codepage to see if just by some miracle corruption only happens at a later stage

     

    Thank you all for your input!


  2. 2 hours ago, Daniel said:

    Thanks for the support - I am a step further.

    Actually OpenSSL handles all these algorithm-stuff by itself. It was just my code that was writing a superfluous zero-byte to a stream. And when it comes to cryptography, a single byte can destroy everything. 😉

    Oooooh, how many of sleepless nights I caused myself when due to encoding issues I changed some string routines to TBytes...

    I can feel the satisfaction of your "oh, damn it" moment when you realized what went wrong 🙂

     


  3. No need for a launcher, a single .EXE can do it - at least on Windows.

    Windows allows to rename the executable even if your application is running. So the steps are...

    - Download the updated .exe, and save it e.g. .exe.new

    - Rename the current application (Application.ExeName) to .exe.old

    - Rename the updated file .exe.new -> .exe

    - Launch the .exe and immediately quit

    - It's a good practice to wait for the previous process to actually die before doing any actual work, like reading settings up, etc.

    - Upon startup check and delete any .exe.old files

     

    I'm using this method and it works like a charm.

    • Like 6

  4. Hello,

     

    Not that long ago we managed to fix up the codebase enough so our large legacy application could be compiled to Win64. Since then this is the second time when something causes an AV on Win64 which worked for 15+ years on Win32.

     

    Pseudocode:

    Procedure TForm1.Button1Click(Sender: TObject);
    
     Procedure DoStuff(Var sl: TStringList);
     Begin
      If Not Assigned(sl) Then
        sl := TStringList.Create;
      sl.Text := 'Just add' + sLineBreak + 'some data';
     End;
    
    Var
     sl: TStringList;
    Begin
     Try
      If condition Then
        Exit;
    
      DoStuff(sl);
    
      [...]
    
     Finally
      sl.Free;
     End;
    End;

    There are two points where said code can fail:

    - sl wasn't initialized to nil before passing it to DoStuff. Win32 happily created the TStringList, Win64 didn't and therefore the next method doing anything with it threw an AV

    - Before even having a chance to initialize sl, we might break out of the method, but sl.Free is called anyway. On Win32 this was fine, Win64 threw an AV first chance

     

    As far as I'm aware Delphi is not initializing local variables and I also don't think we were that lucky for 15+ years that these variables were always reserved on a previously cleared memory area (therefore, pointing to nil).

    Before you say anything, I know that this is a coding issue and was corrected quickly, I'm interested why it behaves differently; maybe some compiler differences...?

    What can be the explanation?

     

    Cheers!


  5. 1 hour ago, Der schöne Günther said:
    • You cannot fine-grain access (public read access, protected write access)

    However I disagree with the opinion that properties are useless, but the above is driving me nuts, too. To be able to achieve this, I have a public, readonly ex. Status property, and a protected writeable InternalStatus property, which point to the same variable. It works, but I hate even to look at it.

    Maybe this could be solved if internally we refer to Self as an interface... idk.


  6. Security patches WOULD be important, if there's no firewall whatsoever between the machine and the Internet (WinXP 30 sec countdown, anyone?) OR if you are visiting shady websites OR if any machine on the network is already infected.

     

    I have an ESXi at home with VMs of all Windows Server versions from Win2k up so I can test the product compatibility. For none of these I have blocked Internet access and with only a router inbetween I had no issues (that I know of, anyway :)). in the past years.

    I'd say it's safe to have EOL OSes connected to the Internet if you know exactly what you are doing.


  7. As far as I am aware, SQLite was designed to be a small, "engineless", flatfile data storage which can be queried just like any RDBMS. It never meant to have all the functionality of those, it simply provided an alternate, standardized way to store, and quickly access information.

    Therefore, limitations. Quite few, and easily workaround-able, imo, though.

    • Like 1

  8. 13 minutes ago, Fr0sT.Brutal said:

    And don't forget to make it 64-bit otherwise rollover will bite very hard

    I started to use databases after the 32bit UNIX timestamp panic was already all over the Internet. Therefore I quickly learned to define all UNIX timestamps as UInt64s 🙂

    Nonetheless very true.

     

    From Wiki if someone is interested:

    Quote

    At 03:14:08 UTC on Tuesday, 19 January 2038, 32-bit versions of the Unix timestamp will cease to work, as it will overflow the largest value that can be held in a signed 32-bit number (7FFFFFFF16 or 2147483647). Before this moment, software using 32-bit time stamps will need to adopt a new convention for time stamps, and file formats using 32-bit time stamps will need to be changed to support larger time stamps or a different epoch. If unchanged, the next second will be incorrectly interpreted as 20:45:52 Friday 13 December 1901 UTC. This is referred to as the Year 2038 problem.

    Quote

    At 06:28:15 UTC on Sunday, 7 February 2106, Unix time will reach FFFFFFFF16 or 4294967295 seconds, which, for systems that hold the time on 32-bit unsigned integers, is the maximum attainable. For some of these systems, the next second will be incorrectly interpreted as 00:00:00 Thursday 1 January 1970 UTC. Other systems may experience an overflow error with unpredictable outcomes.

     


  9. 4 hours ago, Stano said:

    When I found SQLite and found how complicated it was to work with dates, I quickly rejected it.

    Use UNIX timestamp and convert in the application. SQLite will be more than happy to store that number for you.

    Lately I'm even storing dates as UNIX timestamps in local settings files too.

     

    It's really convenient.


  10. 2 hours ago, DavidJr. said:

     

    
      repeat
        if MsgWaitForMultipleObjects(0, Pointer(nil)^, FALSE, (TickTime - Present), QS_ALLINPUT) <> WAIT_OBJECT_0 then Break;
        Application.ProcessMessages;
        Present := (GetTickCount - Past);
      until ( Present >= LongInt(TickTime) );

     

    I wanted to save time before to convert blocking methods to background threads but still blocking the calling method while it runs... used a cycle like this. Once issues started to appear I spent months getting rid of this "shortcut". It became way more messy and way more complicated.

    Even if you don't have issues at the moment... remember, this cycle is the devil reincarnated.

    • Like 1

  11. 3 minutes ago, Ian Branch said:

    Bad news then!  Nothings been done about it since 10.3!  😞

    My comment was meant to be ironic. Sounded more ironic before I pressed send anyway.

     

    I'd rephrase "nothing" to "nothing in this matter". There has been a significant improvement in speed and accuracy of error insight, suggestions - at least in small to midsize projects. The IDE got more stable (once you disable LiveBindings and the welcome screen) and some convenience features were introduced (e.g. Ctrl+click on inherited). Hell, my IDE did not crash by itself since D10.1, not even at work with an endless amount of (even custom) components installed.

     

    Delphi is getting better in lots of ways. Alas, this still doesn't mean that the speed / willingness of Emba fixing bugs is not tragicomic.


  12. 8 hours ago, kaarigar said:

    Thanks - that is what I ended up doing. I should perhaps set up tje All Configuration as yiu suggest. But then the question what gets precedence - All Configuration or individual specific platform configuration.

    Individual settings will overwrite the one set on the parent. What I really don’t like from this perspective is that there is no “force inheritance”. You change a setting topmost, but if the same setting has a different value somewhere, it’s not going to be changed.

    • Like 2

  13. 13 minutes ago, Dalija Prasnikar said:

    Memory manager works fine in Sydney and detects memory leaks.

    Yes, but it's not pinpointing the line in the source code where the leaked object was created like as DeLeaker / FastMM full does. My main application which is about 1M LOC, you tell me a class name and I can tell where and why it was created. In older, legacy applications this can be a nightmare, especially if - like at work - everyone uses a TStringList instance for text manipulation, data storage or even as a parameter for a function call.

     

    I guess this is what @David Schwartz meant. In these occasions, having the complete callstack of a leaked object is a life saver.


  14. To be honest I don't see the reason why interfaces are required here. If the objects are freed up multiple times that clearly indicates a coding issue; using interfaces for lifecycle management in this case seems a bit like duct tape on a totaled car.

     

    I am also storing objects in .Data properties, using the OnDelete handler to manually free these up and set the .Data to nil. Never had a single issue with multiple release attempts or leaks (except if VCL styles are active but that's a different topic).

     

    Out of curiosity you can add a FreeNotification handler and put a breakpoint in that. That way you could examine what is freeing your objects up when you don't want them to, yet.

    • Thanks 1

  15. @clarke Since the D7 era I was using Delphi's own TClientSocket - TServerSocket. Had some quirks but it worked perfectly.

    Due to a bug in my receiver code (which originally I thought is in the component) I migrated to ICS about a year ago. A bit more complicated, but works perfectly as well.

     

    Depending on your needs you can check these out. ICS works fine on D11 (I'm unsure if TClientSocket - TServerSocket is still shipped, though), both has decent documentation.


  16. I ended up changing the quote detection as it misbehaved in several occasions. At the moment it looks like this:

     

    '''', '"':
    Begin
      // GetMatchingBracketEx does not support the same opening and closing characters. Therefore quotes
      // need special handling.
      // This quick and easy method only detects quotes in the same line.
    
      index := bufcoord.Char;
    
      If bufcoord.Char > 1 Then
      Begin
        // Check the current token BEFORE the current quote. If it's a String or a delimited identifier,
        // the currently selected is the closer. Go backwards to find the opener.
    
        Dec(bufcoord.Char);
        If Not SynEdit1.GetHighlighterAttriAtRowCol(bufcoord, token, attr) Or
          ((attr <> SynHighlight.StringAttribute) And
           (attr <> SynHighlight.DelimitedIdentifierAttri)) Or
          Not token.StartsWith(c) Then
          a := 0
        Else
        Begin
          a := Length(token);
          Dec(bufcoord.Char, a - 2);
        End;
      End
      Else
        a := 0;
    
      If (a = 0) And (bufcoord.Char < Length(SynEdit1.Lines[bufcoord.Line - 1])) Then
      Begin
        // Character before the current quote was not the correct token or opener quote was not found.
        // Attempt to do the same check with the character after, this time looking for a closer...
    
        bufcoord.Char := index + 1;
        If Not SynEdit1.GetHighlighterAttriAtRowCol(bufcoord, token, attr) Or
          ((attr <> SynHighlight.StringAttribute) And
           (attr <> SynHighlight.DelimitedIdentifierAttri)) Or
          Not Token.EndsWith(c) Then
          Exit
        Else
        Begin
          a := Length(token);
          Inc(bufcoord.Char, a - 2);
        End;
      End;
    
      tp := SynEdit1.RowColumnToPixels(SynEdit1.BufferToDisplayPos(bufcoord));
    End;

    This way I can correctly determine if the token is before or after the current character, which direction I should look for the closer. It works, with only 1 limitation: the token returned is only the part of the token, which is the current line - therefore highlighting quotes only work in one line as well.

    Is there a way to make GetHighlighterAttriAtRowCol return the full token (including line breaks) or an other method I can use?

×