-
Content Count
1030 -
Joined
-
Last visited
-
Days Won
22
Posts posted by aehimself
-
-
@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.
-
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.
-
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!
-
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".
-
*** Screams in JavaScript ***
-
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!
-
4 hours ago, Remy Lebeau said:when server-side pushes
Aren't these supposed to be services (like Apple's or Google's notification service) or websockets...?
-
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.
-
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!
-
@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!
-
A while ago we managed to create one like this. You'll also find an installable component version in the comments.
- 1
-
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.
-
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 🙂
-
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?
-
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: -
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.
- 2
-
9 hours ago, stacker_liew said:BTW, how to handle when the form is resized while the processing is doing?
You either manually set the position on the form's OnResize event or set the proper anchors after creating the components.
-
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.
-
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;
-
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:
-
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!
-
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 🙂
-
Checking the TCP state with NetStat is a great idea, I don't know why I didn't think about this! Will do the check, thanks!
-
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!
Custom component ParentBackground property resets to false
in Delphi IDE and APIs
Posted
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:
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!