Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. Hello,

     

    We have a custom component derived from Delphi's standard TPanel. What I realized is that the ParentBackground property resets to False each time the form is opened in the Delphi IDE (no saving is required, it automatically updates the DFM).

    There are no mentions / changes made to this property in the component's code and the situation does not change even if I add this published property:

        property ParentBackground stored true default True;

    I found an old topic about OldCreateOrder behaving the same way, but the proposed solution (remove the ParentBackground = False from the DFM with a text editor) only works until the form is reopened again - then it will automatically reset to False and update the DFM immediately without saving.

    I'll keep digging and investigating but if anyone has experience with this please do not hesitate to share the solution 🙂

     

    Thanks!


  2. I solved this issue by changing my icons to Material Designs Webfont and providing them to my application using Icon Fonts Image list.

    When the style changes I just update the .FontColor property of the image list to TStyleManager.ActiveStyle.GetStyleFontColor(sfWindowTextNormal). This way I can be certain that whatever is selected, my toolbar and popup menu icons are always visible.

     

    As an extra, if you are fine with the monochrome look, you can spare the fee of the designer.


  3. Hello,

     

    I just finished rewriting a method which now only lists font names useable by TSynEdit. The theory is easy; while enumerating Screen.Fonts I'm passing them to the exact same check as TSynEdit does (SynDWrite.pas, IsFontMonospacedAndValid). If it returns true, I add it to the list.

     

    The issue is, there is a CheckOSError call in said method which break the execution of the program way too many times if it is run from the IDE. The current implementation is as follows:

    function IsFontMonospacedAndValid(Font: TFont): Boolean;
    var
      LogFont: TLogFont;
      DWFont: IDWriteFont;
    begin
      try
        Assert(GetObject(Font.Handle, SizeOf(TLogFont), @LogFont) <> 0);
        CheckOSError(TSynDWrite.GDIInterop.CreateFontFromLOGFONT(LogFont, DWFont));
        Result := (DWFont as IDWriteFont1).IsMonospacedFont;
        if (FontFamilyName(DWFont) <> Font.Name) and (fsBold in Font.Style) then
          Font.Style := Font.Style - [fsBold];
      except
        Exit(False);
      end;
    end;

    I can put the EOSError exception type as a globally ignored one in Tools / Options but understandably it is not an ideal solution. I also could copy and paste this check, removing the CheckOSError but if I do I have to keep an eye on the official implementation to make sure to update my version if the original changes.

     

    Is there a compiler directive which will ignore a specific exception type within a block of code?

     

    Thanks!


  4. Hello,

     

    Due to various reasons I'm experimenting with Delphi's TRichEdit component. I looked into the demo and quickly learned the basics but I didn't find any way to insert a picture.

    My searches lead to two solutions:

    - LINK. With a RichOle.pas file create an object from the image and insert it into the RichEdit using OLE. This method worked, but it embeds it instead of inserting (image is not shown, only an icon and the file name. Picture opens correctly when double-clicked)

    - LINK. Load the bitmap and convert it into rich edit compatible code. Insert this code using EM_STREAMIN. This method did absolutely nothing

     

    Another way (which probably works) is to copy the picture to the clipboard and paste it's contents into the RichEdit but this just feels way too hacky.

     

    The question is, how to insert a picture in a TRichEdit the most elegant way? I'm attempting on Delphi 10.4.1 / 10.4.2 / 11.1.

     

    Thanks!


  5. 15 hours ago, Remy Lebeau said:

    Makes me wonder now if I should add a NATKeepAlive property to TIdHTTP, similar to what TIdFTP has. I've opened a ticket for that: https://github.com/IndySockets/Indy/issues/413

    Honestly, I don't know if it would make any sense. It's your decision at the end. Would have made my implementation easier (and error-free the first try 🙂) but our implementation is clearly wrong here. In normal operation the HTTP protocol should work in "bursts": request something, get an answer, repeat until all done.

     

    If someone else needs a TCP-level keep-alive in HTTP instead of fixing the real issue (like in my case) these 3 lines of extra code seems very well deserved. Plus the solution is now publicly accessible in this thread.


  6. I decided to implement both. Upon enabling the TCP keepalive if a handle is allocated SetKeepAliveValues is called but there is now an OnSocketAllocated handler which checks if the keepalive was enabled and if yes, calls SetKeepAliveValues.

    This way the exception disappeared; your guess was right, there was no handle allocated after the DoRequest call.

     

    As I could not reproduce the freezing of the application I can not confirm whether that disappears or not... guess time will tell sooner or later.

     

    Thank you!


  7. @Remy Lebeau Just yesterday a new issue was reported which is in direct connection with the solution in this thread.

     

    The code is fairly simple:

    procedure TCustomActionCallerThread.Execute;
    begin
      V_CONNECTION.IndyHttpClient.Socket.Binding.SetKeepAliveValues(True, FTCPKeepAlive, FTCPKeepAlive);
      Try
        try
          V_CONNECTION.DoRequest(FActionName, FRequestXml, FResult);
        except
          on E: Exception do
            FResultException := Exception(AcquireExceptionObject);
        end;
      Finally
        V_CONNECTION.IndyHTTPClient.Socket.Binding.SetKeepAliveValues(False, 0, 0);
      End;
    end;

    The DoRequest (where the HTTP communication actually takes place) is quick (less than a second) but the code in the finally block (to disable the keepalive) throws an exception: Socker Error # 10038  Socket operation on non-socket.

    To make things more interesting, if the application is built with Delphi 10.4.1, it completely freezes. 10.4.2 only throws the exception but the operation finishes successfully.

     

    Do you have an idea why this error might appear? We use HTTP keepalive, so the socket should still exist after the DoRequest call. Is there a check which I can use as a condition to prevent this from happening?

    Also, do you happen to know in any difference between the Indy versions in 10.4.1 and 10.4.2? I can not really find a logical explanation in the behavior difference.

     

    Thank you!


  8. 11 minutes ago, tinyBigGAMES said:

    - which virus scanner in use? (see if you can exclude C:\Users\xxx\Downloads\GVExamples\, most likely the scanner is chewing on it). 

    Windows Defender. I know what sites to visit and what to download / execute 🙂

    11 minutes ago, tinyBigGAMES said:

    - what version of windows (must be at least win10, 64 bits)

    Spot on. Win10 Pro x64.

    12 minutes ago, tinyBigGAMES said:

    - do have OpenGL 3+ drivers? (make sure they are up to date)

    No idea. I have an on-board Intel 620 and an additional Radeon R5 M430. The last time I used my PC for gaming was when I quit WoW about 10 years ago so I'm kinda rusty in these things 🙂 Intel's driver is 30.0.101.1960, Radeon is at 27.20.20904.4000 if it makes any sense.

     

    When I first started your demo there was a moment of DOS prompt and then nothing. No worries if my laptop cannot run it, it's not a workhorse. I just got a tidy bit worried, questioning my life decisions and uploading the executable on VirusTotal.

    Handle your exceptions if you can, please 🙂


  9. This code seems to work on a fully patched 10.4.2:

    procedure TForm2.FormCreate(Sender: TObject);
    Var
     x: Int64;
    begin
     x := Int64.MaxValue;
    
     If x <> Test Then Raise Exception.Create('Fail');
    end;
    
    function TForm2.Test: Int64;
    begin
      Result := Int64.MaxValue;
    end;

    Checked with TFileStream too:

    procedure TForm2.FormCreate(Sender: TObject);
    Var
     fs: TFileStream;
     tb, r: TBytes;
     a: Int64;
     b: Integer;
    begin
     SetLength(tb, 1024 * 1024); // 1 MByte
     For a := Low(tb) To High(tb) Do
      tb[a] := a Mod 255;
    
     fs := TFileStream.Create('C:\Users\xxx\test.tmp', fmCreate);
     Try
      For a := 0 To 4 * 1024 Do
       fs.Write(tb, Length(tb));
     finally
      fs.Free;
     End;
    
     SetLength(r, Length(tb));
     fs := TFileStream.Create('C:\Users\xxx\test.tmp', fmOpenRead);
     Try
      Repeat
       b := fs.Read(r, Length(r));
       If b > 0 Then If (b <> Length(tb)) Or Not CompareMem(@r[0], @tb[0], Length(tb)) Then Raise Exception.Create('Read error, read ' + b.ToString + ' bytes');
      Until b = 0;
     finally
      fs.Free;
     End;
    end;

    Runs and works properly.

     

    Am I missing something?


  10. Faulting application name: GVExamples.exe, version: 0.0.0.0, time stamp: 0x62940977
    Faulting module name: KERNELBASE.dll, version: 10.0.19041.1706, time stamp: 0x458acb5b
    Exception code: 0xc06d007e
    Fault offset: 0x0000000000034fd9
    Faulting process id: 0x2bd8
    Faulting application start time: 0x01d8752529d9b09b
    Faulting application path: C:\Users\xxx\Downloads\GVExamples\GVExamples.exe
    Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll
    Report Id: ac240234-13c0-439c-88d1-1d9bdbdcb991
    Faulting package full name: 
    Faulting package-relative application ID: 


  11. I wanted to do this for a really long time so I started to extract the core improvements of Delphi's TDBGrid into a separate component. This includes:

    - New public BeginUpdate / EndUpdate methods, which can disconnect the dataset and keep the previous image on the component. This is useful if you are doing opens / posts / anything in a background thread but don't want to show emptiness until

    - Automatic and manual fitting of columns which considers the column and the content width but won't let a column be wider than half it's size

    - Vertical scrollbar now works properly, not only 3 positions and is not visible when not needed

    - Grid properly handles mouse wheel scrolling

    - Content is shown as the scrollbar is dragged (not only updating when the mouse is released)

    - If there is no connected dataset or it is not active, the two empty cells won't be shown

    - Every second row has a slightly different background, out-of-focus selection is now drawn with a separate shade of grey, so you can see that the grid is not in focus. This considers VCL styles.

    - If TitleClick or TitleHotTrack is enabled, the cursor changes to a hand instead of the pointers

     

    It might get more updates later on when I see what code can be generally used from my heavily customized one.

     

    Feel free to grab it / check how things were done. Tried to put comments everywhere.

    • Like 2

  12. Something like this:

    Var
      pbar: TProgressBar;
      lbl: TLabel;
      a: Integer;
    Begin
      TProgressBarInStatusBar.CreateIn(StatusBar1, pbar, lbl);
      
      For a := 0 To 1000 Do
      Begin
        pbar.Position := a Div 10;
        lbl.Caption := 'Working ' + a.ToString + '...';
        Application.ProcessMessages; // Don't do this. It's just pseudocode.
        Sleep(500);
      End;
    End;

     


  13. I create my progress bars in the first panel of the status bar, with a label on it to show some meaningful information on the progress... like "30 of 999 items processed". Yes, it won't work if you resize the panel, needs some adjustments if you want to create it in the 3rd, but this is the code I use:

    Unit uProgressBarInStatusBar;
    
    Interface
    
    Uses Vcl.ComCtrls, Vcl.StdCtrls;
    
    Type
     TProgressBarInStatusBar = Class
     public
      Class Procedure CreateIn(Const inStatusBarPanel: TStatusPanel; Var outProgressBar: TProgressBar; Var outLabel: TLabel);
     End;
    
    Implementation
    
    Uses Vcl.Controls, System.Classes;
    
    Class Procedure TProgressBarInStatusBar.CreateIn(Const inStatusBarPanel: TStatusPanel; Var outProgressBar: TProgressBar; Var outLabel: TLabel);
    Var
     statusbar: TStatusBar;
    Begin
     statusbar := inStatusBarPanel.Collection.Owner As TStatusBar;
    
     outProgressBar := TProgressBar.Create(statusbar);
     outProgressBar.Parent := statusbar;
     outProgressBar.Top := 2;
     outProgressBar.Left := 1;
     outProgressBar.Width := inStatusBarPanel.Width - 3;
     outProgressBar.Height := statusbar.ClientHeight - 3;
    
     outLabel := TLabel.Create(outProgressBar);
     outLabel.Parent := outProgressBar;
     outLabel.Align := alClient;
     outLabel.AutoSize := False;
     outLabel.Alignment := taCenter;
    End;
    
    End.

    The small sacrifice of having it in the first panel makes up to it with no custom drawings / hacks needed at all. And it looks good enough:

     

    image.png.a052b18560be4c685eedc70179a3f2ae.png


  14. Hello,

     

    We have an application built on Delphi 10.4.1 / 10.4.2 which is communicating with a server using the Indy TidHttp component. It works perfectly, but there is one particular call when the result can arrive in 1-1,5 hours... and this is where things get strange. The request is sent with idHttp.Post (using a stream as an outgoing and an incoming data buffer) and if the reply arrives in 30 minutes, all is fine. Somewhere between 30 minutes and one hour, the underlying WinSock .select never returns. Data is sent out, received by the server, processed and the data is sent out - but never received by the client. Using WireShark it can be seen that the moment the server sends the reply, the client issues a TCP retransmission... maybe it thinks that the data was lost but was unable to use the channel while waiting for data? Then the reply arrives, channel gets free and it sends the retransmission but discards the data received? These are just guesses, I'm not very familiar with this low-level functionality of WinSock.

    Oh, one more thing... this issue is NOT present if the server and the client is on the same machine; connecting to localhost makes a difference.

     

    Stack trace where the application stops is as follows:

    :772729dc ntdll.ZwWaitForSingleObject + 0xc
    :74417555 ; C:\WINDOWS\SysWOW64\mswsock.dll
    :751c5f1e WS2_32.select + 0xce
    IdStackWindows.TIdSocketListWindows.FDSelect(???,???,nil,???)
    IdStackWindows.TIdSocketListWindows.SelectRead(-2)
    IdSocketHandle.TIdSocketHandle.Select(???)
    IdSocketHandle.CheckIsReadable(???)
    IdSocketHandle.TIdSocketHandle.Readable(-2)
    IdIOHandlerStack.TIdIOHandlerStack.Readable(???)
    IdIOHandler.TIdIOHandler.ReadFromSource(True,-2,False)
    IdIOHandler.TIdIOHandler.ReadLn(#$A,-1,16384,TIdASCIIEncoding($1A138AD4) as IIdTextEncoding)
    IdIOHandler.TIdIOHandler.ReadLn(nil)
    IdHTTP.TIdCustomHTTP.InternalReadLn
    IdHTTP.TIdCustomHTTP.DoRequest(???,'http://10.0.2.53:12345/BIN',$2E6D75A0,$2E6D73A0,(...))
    IdHTTP.TIdCustomHTTP.Post('http://10.0.2.53:12345/BIN',$14E0F58,$2E6D73A0)

    Before you say anything, I know this is a bad design. We shouldn't wait on long lasting operations but to poll for it on HTTP. What I'd like to know is what happens and why it happens so I can get an insight if patching the mess worth it or just jump straight to refactoring.

     

    Cheers!
     

×