Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by aehimself

  1. 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!
  2. aehimself

    Ignore exception type from code

    @Steven Kamradt An important rule of writing maintainable software is that you are not changing the code in any external components. Doing that will come to bite you in the back later on; especially if you try to update said component.
  3. aehimself

    Changing TToolButton image and using transparency

    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.
  4. I'd doublecheck every registry operation and make sure every .OpenKey has a .CloseKey, and every TRegistry.Create has a .Free in the Finally block. I had my share of issues when I wanted to reuse the same TRegistry object within a method to perform different writes. I ended up creating it and freeing it up after each "task".
  5. *** Screams in JavaScript ***
  6. aehimself

    Update framework question

    Hello, I have a pretty basic update mechanism in my applications which gets the job done - but it has it's limitations. It would be nice to have "dev" and "stable" channels, messages to be shown to the users (e.g. if there's a yet unfixed bug with a workaround), going back to a previous version, delta updates only, etc. So I started to rework the thing as it is but I am simply stuck on the design stage... I can not agree with myself on how it should be done properly. My base theory is one file in one update archive, the application determines what it needs to update and download only those archives to minimize network traffic. - Put a static JSON with all version information, this gets refreshed each time a version is deployed? This is how the current system works: easy to implement but... - Just put the ready archive with the new version in the folder and have a service explore the changes and rebuild the static JSON accordingly? This option sounds the best to avoid any lingering files / entries (e.g. archive is placed but changed were not placed in the DB or vice versa) but where the changelog is coming from in this case? - Store everything in a database and use a PHP script to query and assemble the reply JSON? My PHP knowledge is really limited, so I'd prefer not to have this option. Although, a dynamic list is crucial to minimize traffic (why to download version information for different products, or changelogs for versions below the current one?) So my question is... are there any readily available update platforms for Delphi (server and client side too) which I can simply implement in my applications and forget about this matter? I'm also open to suggestions on how the thing should work, on all possible levels: - Backend. Is a database really needed or overkill? - Static vs dynamic update definitions. Dynamic is better from many perspectives but does it really worth the extra effort? - Protocol. Should I really stick to HTTP, or is there a better / create my own? - How the information is translated and sent, including how the application should know if a new file is added to the distribution? Cheers!
  7. aehimself

    Insert picture in TRichEdit from code

    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!
  8. 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(???,'',$2E6D75A0,$2E6D73A0,(...)) IdHTTP.TIdCustomHTTP.Post('',$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!
  9. Aren't these supposed to be services (like Apple's or Google's notification service) or websockets...?
  10. 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.
  11. 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!
  12. @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!
  13. aehimself

    panel transparent

    A while ago we managed to create one like this. You'll also find an installable component version in the comments.
  14. aehimself

    GameVision Toolkit

    Nice fix from a standard Windows application error 🙂 Where can I report more and how? 🙂 One demo locks up the application, 3 other causes it to crash completely.
  15. aehimself

    GameVision Toolkit

    Windows Defender. I know what sites to visit and what to download / execute 🙂 Spot on. Win10 Pro x64. 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, 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 🙂
  16. aehimself

    Warning: serious bug in XE10.4.2/DCC64

    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?
  17. aehimself

    GameVision Toolkit

    Faulting application name: GVExamples.exe, version:, 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:
  18. aehimself

    Problem with column names in dbgrid

    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.
  19. aehimself

    Does ProgressBar Inside StatusBar Still Working?

    You either manually set the position on the form's OnResize event or set the proper anchors after creating the components.
  20. aehimself

    Problem with column names in dbgrid

    TDBGrid is doing a really crappy job in sizing it's columns and however I didn't meet the issue you describe, I simply consider it to this "feature". Have a look at this snipplet for a possible fix; I started mine based on this too.
  21. aehimself

    Does ProgressBar Inside StatusBar Still Working?

    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;
  22. aehimself

    Does ProgressBar Inside StatusBar Still Working?

    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:
  23. aehimself

    Overloads in implementation

    Hello, We just met a strange issue and was wondering if anyone can explain why it is happening. The below code throws a stack overflow, as the compiler doesn't make a difference between TDateTime and Integer and keeps calling the first method: program Project1; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.DateUtils; function GetDay(dt: TDateTime): string; Overload; begin Result := GetDay(DayOfTheWeek(dt)); end; function GetDay(i: Integer): string; Overload; const LDays: array[1..7] of string = ('H', 'K', 'S', 'C', 'P', 'S', 'V'); begin Result := LDays[I]; end; begin WriteLn(GetDay(Today)); end. It works perfectly if you turn it to a dummy class and publish these methods as class functions: Type x = Class class function GetDay(dt: TDateTime): string; Overload; class function GetDay(i: Integer): string; Overload; End; It also works if you push these two methods in a separate unit with proper Interface section: unit Unit1; interface function GetDay(dt: TDateTime): string; overload; function GetDay(i: Integer): string; overload; implementation I guess it'll have something to do on how overloads are interpreted in the implementation area...? Delphi 10.4, 10.4.2 and 11 are producing the same symptom.
  24. I can confirm that wrapping the long lasting call in a Try...Finally block and enabling / disabling the KeepAlive function via SetKeepAliveValues solves the problem. Thank you, Remy!
  25. There are two connections between the client and the server at this state, both show up as ESTABLISHED. Damn, I really wanted to pass this on to the NW guys 🙂