Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. I solved this with the handler of TSynCompletionProposal.OnExecute (TableNamesSelector is a TSynCompletionProposal, SQLEditor is a TSynEdit) :

    Procedure TSQLConnectionFrame.TableNamesSelectorExecute(Kind: SynCompletionType; Sender: TObject; Var CurrentInput: String; Var x, y: Integer; Var CanExecute: Boolean);
    Var
     sa: TArray<String>;
    Begin
     If TableNamesSelector.ItemList.Count > 0 Then Exit;
    
     sa := SQLEditor.LineText.Substring(0, SQLEditor.CaretX - 1).Split([' ']);
     If Length(sa) > 0 Then sa := sa[Length(sa) - 1].Split(['.']);
     If Length(sa) < 2 Then TableNamesSelector.ItemList.Assign(SQLHighlight.TableNames) // No dot, offer table names immediately
       Else Begin
            CanExecute := False;
            If Length(sa) = 2 Then // Start a thread to collect field names of said table...
            End;
    End;

    You have to pass sa[0], sa[1], X and Y to the thread. sa[0] is the table name, sa[1] is the field name fragment which was already typed, x and y is the position where the proposal should pop back up.

    Once it finishes, you can:

     TableNamesSelector.ItemList.Assign(worker.FieldNames);
     TableNamesSelector.Execute(worker.FilterForText {passed to the thread as sa[1] from OnExecute}, worker.X, worker.Y);

    Also handle the OnClose event of the completionproposal:

    Procedure TSQLConnectionFrame.TableNamesSelectorClose(Sender: TObject);
    Begin
     TableNamesSelector.ItemList.Clear;
    End;


    The idea is that if the ItemList is empty the data still has to be collected and execution is disallowed. It will be popped up when the thread finishes.

    • Like 1
    • Thanks 1

  2. Long story short: I'd like to get the popular opinion / correct way to handle a situation in a TDataSet descendant - and DelphiPraxis does have the userbase for it.

    What do you think, does it make sense to be able to change the .SQL property (the SQL query to be executed) of a dataset component while it is open and contains a resultset? Keep in mind that this can be an INSERT, UPDATE, DELETE command too, not necessarily an other SELECT.

     

    The full discussion with pros and cons can be read here for those interested.


  3. 20 minutes ago, corneliusdavid said:

    If you want to write a server app or a web module, this is the best way to go as debugging a console app

    I started to write my applications as a single class some years ago and never turned back. Want to start from console? TMyApp.Create; From a Windows service? TMyApp.Create; From the Mt. Everest? TMyApp.Create;

     

    You'll have to refine logging for sure but this is the easiest way I ever experienced to write self-containing, easy to debug code.

     

    I even took this one step further by adding some code to the .dpr of my Delphi services: if you start with the /console parameter, it starts as a console app instead of a service.


  4. Thank you @Dalija Prasnikar, I learned some new things about window messages! Unfortunately though, none of the above applied to my case.

     

    After the comment of @Lars Fosdal I got rid of the TTimer and used the same dummy window to catch the now manually fired SetTimer messages too. I also included a counter which increases each time a window message is received (whether processed or not) to see how swarmed application is. With a timer of 1 second, 70 seconds of runtime I got 72 messages. 1 is UM_WORKERENDED, I count the extra 1 as an acceptable discrepancy due to the timer's accuracy (or something unprocessed).

    So no, it was not the window messages.

     

    The solution is so painfully trivial I'm almost afraid to share...

     

    In the TComponent my timer was set to 750 msec to update the elapsed time on the UI. As I'm using TStopWatch, I manually converted the .ElapsedMilliSeconds to some readable format... using dividing and substracting.

     

    First time the timer fired at 0,75 seconds, UI was updated with the rounded 0 seconds.

    Second time the timer fired at 1,5 seconds, UI was updated with the rounded 1 seconds.

    Third time the timer fired at 2,25 seconds, UI was updated with the rounded 2 seconds.

    Fourth time the timer fired at 3 seconds, UI was updated with the rounded 3 seconds.

    Fourth time the timer fired at 3,75 seconds, UI was updated with the rounded 3 seconds.

     

    Even if the timer component is spot on it can be seen that every 3 seconds, for 2 seconds the elapsed time does "not update"... thus the feeling of variable speed.

     

    After upping the sampling to 1000 msec instead of 750 and some small tweaks to the conversion everything seems to be working fine.

     

    Why it was working on the TPanel...? Because originally I forgot to change the default 1000 msec interval to 750...

     

    What a nice way to waste a day by debugging something, which is working as it should 🙂

    • Like 1

  5. Hello all,

     

    I have a progress indicator on a panel which is visible until a thread is running. On here is a timer which is updating the status of the thread every second (elapsed time, some data from inside the thread, etc.). The .OnTerminate event of said thread has a custom message sent to the panel so I can do some final touches plus freeing the thread object up:

     PostMessage(Self.Handle, UM_WORKERENDED, 0, 0);
    
    Procedure TProgressPanel.UMWorkerEnded(Var Msg: TMessage);
    Begin
     _worker.WaitFor;
     FreeAndNil(_worker);
    End;

    All was working fine until a recent refactoring, when I changed the panel to a TObject for modularity reasons (now said panel can display different content, so I had to move the progress indicator to it's own).

    Since a TObject has no handle I created a dummy window with AllocHwnd and sending my message on this dummy handle. Everything works like clockwork, but I realized that the status update timer became laggy. Sometimes it refreshes too quickly (from second 1 to 2 in ~750 msec) and later on delaying more than 1 second (from second 2 to second 3 in 1,25 sec). Elapsed time is measured with a TStopWatch so I doubt the issue is with the measurement.

     

    I am aware that the TTimer works via Window Messages - is it possible that adding a dummy window with AllocHwnd (which is picking up ALL window messages, not just the one specified) causes the code to simply be overwhelmed, not processing WM_TIMER in time?

     


  6. If you have Excel installed where the program will run you can use the COM API to interact with Excel files from your Delphi app I think. I personally never used any of these but I know it can send E-mails with Outlook so I suppose you can use Excel too.

    From there it's easy as a pie. Just do a loop, dataset.Append; dataset.FieldByName.AsString := excelcell; dataset.Post.

     

    @FPiette has my preferred answer too. Just export to CSV / TSV (which I personally prefer) and it's really easy to parse those with Delphi.


  7. Hello,

     

    On my development PC Delphi 10.4.2 stopped displaying the tooltip with the parameters and their types when a bracket was opened. E.g.:

     

    image.png.3f46c43352baff5c9c06af743bfef3db.png

     

    I compared the Code Insight settings with a PC where it is showing up properly and they seem to match. Currently I'm using these:

     

    image.thumb.png.837e5e085f1e78cd546114bbc533af25.png

     

    I remember that in the past unchecking something - reopening Delphi - re-checking something - reopening Delphi solved this (my hunch is on Code template completion... unsure though) but I cannot seem to get this tooltip back alive now.

     

    Is there something I still can try before dropping my registry entries and start to reconfigure the IDE from ground up?


  8. 9 minutes ago, Der schöne Günther said:

    Stupid question: Is it running inside a VM or directly on real hardware? When I installed it inside a Hyper-V machine, it lacked 3D acceleration and a lot of effects (transparency, animations, rounded corners) were missing.

    It is a VM running on ESXi, with tools installed. Transparency is visible even through RDP. In general, windows do not have rounded corners, just some special ones (like the Start thingie, dialogs in Settings, etc.)


  9. Actually no, compression is not performed in the project in which this class was used.

    Data packet is a JSON object which gets serialized and encrypted... size is usually between 80-140 bytes, which only increases after compression.

     

    The reason I didn't include a version in the header is the packet itself: if a new version adds a new field this easily can be detected during deserialization.

     

    I know the code is not completely universal. I kept it as simple as possible to do it's job in this context and nothing more - keeping the logic easy to read and understand. If your packets are usually larger than 64k, it's enough to change TPacketLength = UInt64; for example, the rest of the code should take care about this automatically.

     

    P.s.: I just read your first answer again and realized that I'm indeed copying an Integer in a TPacketLength which is definitely not right! I'll fix this and move the size check from my program to this class so I can reupload. I should also mention the size limit in the "Keep in mind" section I assume 🙂

     

    P.s.p.s.: There was a network loss between just the other day and about 90k of buffer was received by the server upon reestablishment containing a number of ~140 byte packets. The code worked flawlessly, separated and processed each of these without errors. So yeah, I'm happy 🙂

    • Like 2

  10. 2 minutes ago, Lars Fosdal said:

    A couple of observations:
    1. TPacketLength = Word;
    The first thing you write to the stream is the Packet length as a word.

    TBytes can be larger than 64K, so there are some safeguards missing.

     

    2. If you are transmitting to a different byte order system, the PacketLength needs to be marshalled/demarshalled.

    Appending the length is included in the class method TBinaryPacket.AppendSize(Var inData: TBytes);

    As for the size limit I do have a check but it's in the sending method of my client application. You are absolutely right, it makes more sense here though!

      // If the buffer exceeds the maximum length allowed, raise an error as it can not be sent!
      If Length(buf) > TPacketLength.MaxValue Then Raise ETCPPortError.Create('Buffer overflow, cannot send ' + Length(buf).ToString + ' bytes!');

    I personally have no experience with different byte order systems... You mean some systems (OSes..?) store a word like LLHH and an other like HHLL? I always thought that it's language dependent.

    In this case yes, the code is not ready for it yet. It can be achieved by adding one more byte to the beginning... 0 if sender is a LLHH system, 1 if sender is a HHLL system. The receiving part then can decide if we need to switch the order upon reading a packet out.

     

    Thank you for this input! Although I used this code to send and receive data from Windows to Windows it's always good to know that there are cases which I have to consider before using it in a future cross-platform project.


  11. Hello all,

     

    I've seen discussions about how to properly receive binary packets through the network. I even had my share of mistakes with my first implementation so I decided to create a class to do the heavylifting: receiving data chunks and assembling the packets. Feel free to use, improve and build upon.

     

    Keep in mind that

    1, Packet is referred to the data sent by the client, not TCP packets!

    2, The code is NOT thread safe!

    3, Memory allocation is inefficient and can be improved. In my case (really low amount of packets) it's not an issue but the internal data storage easily can be replaced with a stream for example

    uBinaryPacket.pas

    • Like 2

  12. Be careful, the above code is NOT thread safe. If you have multiple threads using the same logger you'll end up crashing said thread due to unhandled exception in the exception handler or simply corrupting your log file.

    It's good for single threaded applications, though.

     

    Edit: Having a second look I'd vote against this method in general. If you know you will dump everything in a file no matter what it's better to write to that file and read it back all up if you want to display it in the UI. This way you are simply wasting resources, especially if your log file grows large.

    Also, appending to a memo has it's performance penalty which is extremely painful if items are arriving rapidly.

    • Like 1

  13. Got the same result as @Kas Ob. when building a JSON object and using my custom hasher:

    procedure TForm1.FormCreate(Sender: TObject);
    Var
     s256: TAESHA256Hasher;
     tb: TBytes;
     json: TJSONObject;
    begin
     s256 := TAESHA256Hasher.Create;
     Try
      json := TJSONObject(TJSONObject.ParseJSONValue(Memo1.Text));
      SetLength(tb, json.EstimatedByteSize);
      SetLength(tb, json.ToBytes(tb, 0));
    
      tb := s256.HashOf(tb);
    
      ShowMessage(TNetEncoding.Base64.EncodeBytesToString(tb));
     Finally
      FreeAndNil(s256);
     End;
    end;

    Ps: I know I'm leaking json.

    Quote

     

    [Window Title]
    Project2

    [Content]
    1xBL0CLO3vOTbdszL/I7OZF3hoUbR9sElb9UYOimgR8=

    [OK]

     

    So either the example is wrong or they are not parsing the document as we think (I also tried removing the opening and closing braces so only the body is processed - no luck).


  14. I just want to make sure my logic is correct here. I built a really simple test, see .dpr and the generated .map file attached. I used this method to extract method addresses.

     

    Stack trace was as following:

    $0000000000429A84
    $0000000000425327
    $0000000000425C1B
    $000000000040A5C6
    $000000000040A601
    $0000000000429BE5
    $0000000000429C0D
    $0000000000429C2D
    $0000000000429C82
    $00007FFD09E97034
    $00007FFD0A7A2651

     

    Now, from each address I substracted image base ($400000) and $1000, which resulted the following relative addresses:

     

    $28A84
    $24327
    $24C1B
    $95C6
    $9601
    $28BE5
    $28C0D
    $28C2D
    $28C82
    $7FFD09A96034
    $7FFD0A3A1651

     

    In the map file "Address - Publics by name" section I looked for the address which is the closest but below each relative address. This gives me the method names.

    In the map file "Line numbers for xxx" section I searched for the exact relative address. This gives me the line numbers, if any.

     

    Based on the above approach my reconstructed stack trace is the following:

     

    StackTrace.GetExceptionStackInfo ( StackTrace:44 )
    System.SysUtils.Exception.RaisingException ( ??? )
    System.SysUtils.ExceptHandler ( System.SysUtils:23574 )
    ??? ( System.pas:22018 )
    ??? ( System.pas:22108 )
    Project1.Three ( Project1:14 )
    Project1.Two ( Project1:19 )
    Project1.One ( Project1:24 )
    Project1.Project1 ( ??? )
    ???
    ???

     

    It resembles what went on, but I have some missing information which I was unable to find.

    I have two questions... is this the way to reconstruct the call stack from a map file? And if yes, how I can find the missing information?

    Project1.zip

×