-
Content Count
1090 -
Joined
-
Last visited
-
Days Won
23
Everything posted by aehimself
-
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:
-
Updating a JSON file with THTTPClient
aehimself replied to Bob Baudewyns's topic in Network, Cloud and Web
Maybe you think of FTP, but HTTP does not work this way. You need a receiver script / application on your web server which will receive and save that file. -
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.
-
Posting large binary files with Indy using IdMultipartFormData, Do I need wait?
aehimself replied to DavidJr.'s topic in Indy
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. -
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.
-
Good news then! This is not an issue brought by the new version!
-
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.
-
I can not imagine myself sitting down, debugging in head instead of news with my morning coffee. Respect for you there.
-
As far as I know you might be able to copy the relevant lines from within the .dproj file but there's no way I'm familiar with to push the "child" settings up. This is why I'm always setting all information at the topmost level (All configuration) even if it's a Win32 build only.
-
While I agree this is a really annoying issue... may I ask why you decided to print a unit...?
-
Interface question
aehimself replied to David Schwartz's topic in Algorithms, Data Structures and Class Design
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. -
Interface question
aehimself replied to David Schwartz's topic in Algorithms, Data Structures and Class Design
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. -
@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.
- 17 replies
-
- chat.
- client .server
-
(and 1 more)
Tagged with:
-
Hello, I found and modified a code to highlight matching brackets in SynEdit in a Delphi IDE style (no color highlights, but a square around the characters. All is working fine except this one scenario: Enter this pattern and place the cursor in the middle of the inner brackets: Now, press backspace: And now, enter an opening bracket again: The rectangle around the first bracket doesn't get cleared and I can not figure out why. What is even more strange, if you put all in one line all works just fine: This image was taken after deleting and reentering the inner starting bracket. The code I have at the moment is the following: // This whole method was copied from https://stackoverflow.com/questions/18487553/synedit-onpainttransientdemo and modified // to draw rectangles instead of coloring. Procedure TEditorFrame.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType); Procedure PaintText(Const inPoint: TPoint; Const inText: String); Begin Case TransientType Of ttAfter: Begin // Clear brush and pen has font color: rectangle will be drawn SynEdit1.Canvas.Brush.Style := bsClear; SynEdit1.Canvas.Pen.Color := SynEdit1.Font.Color; End; ttBefore: Begin // Solid brush and pen has background color: Rectangle will be cleared SynEdit1.Canvas.Brush.Style := bsSolid; SynEdit1.Canvas.Pen.Color := SynEdit1.Color; End; End; SynEdit1.Canvas.Rectangle(inPoint.X, inPoint.Y, inPoint.X + SynEdit1.Canvas.TextWidth(inText), inPoint.Y + SynEdit1.Canvas.TextHeight(inText)); SynEdit1.Canvas.TextOut(inPoint.X, inPoint.Y, inText); End; Const OCSYMBOLS: Array[0..7] Of Char = ('(', ')', '{', '}', '[', ']', '<', '>'); Var bufcoord: TBufferCoord; tp: TPoint; a: Integer; c: Char; Begin bufcoord := SynEdit1.CaretXY; a := SynEdit1.RowColToCharIndex(bufcoord); If (a > 0) And (a <= SynEdit1.Text.Length) Then Begin // First, take the character BEHIND the cursor and check if it's an opener or a closer c := SynEdit1.Text[a]; If Not TArray.BinarySearch<Char>(OCSYMBOLS, c, a) Then c := #0; End Else c := #0; If c = #0 Then Begin // Variable a might have been overwritten by TArray.BinarySearch so let's initialize it again a := SynEdit1.RowColToCharIndex(bufcoord); If (a < SynEdit1.Text.Length) Then Begin // If the character behind the cursor wasn't an opener or a closer, // take the character AFTER the cursor and check the same c := SynEdit1.Text[a + 1]; If Not TArray.BinarySearch<Char>(OCSYMBOLS, c, a) Then c := #0; End; If c = #0 Then Exit; End Else bufcoord.Char := bufcoord.Char - 1; // Originally this was a FOR cycle but it's a waste to go through all if only one iteration actually performs something. // During the binary search we already got the index and if it's an opener or a closer. Simply use those previously saved // values SynEdit1.Canvas.Font.Assign(SynEdit1.Font); tp := SynEdit1.RowColumnToPixels(SynEdit1.BufferToDisplayPos(bufcoord)); PaintText(tp, c); bufcoord := SynEdit1.GetMatchingBracketEx(bufcoord); If (bufcoord.Char = 0) Or (bufcoord.Line = 0) Then Exit; tp := SynEdit1.RowColumnToPixels(SynEdit1.BufferToDisplayPos(bufcoord)); If tp.X <= SynEdit1.GutterWidth Then Exit; // We need to paint the opposite symbol now: if first one was an opener then a closer and vice versa. // Instead of an If statement we can simply use a XOr 1, as it will turn 1 -> 2 and 2 -> 1, 3 -> 4 and // 4 -> 3... giving the exact opposite symbol in the pair that we need PaintText(tp, OCSYMBOLS[a XOr 1]); End; I'm using TurboPack SynEdit on D10.4.2. If the code doesn't compile just use SynEdit1.Gutter.Width instead, I think nothing else had to be changed. Can someone please check and give some hints on why things go south? š Thanks!
-
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?
-
How to Prevent Multiple Instance Application Running.
aehimself replied to amit's topic in Cross-platform
I can not understand why you have to have (free or paid) components for everything; unless true multiplatform is supported. You can simply use WinApi: handle := CreateMutex(nil, True, 'MyApplicationUniqueMutex'); Try If (handle = 0) Or (GetLastError = ERROR_ALREADY_EXISTS) Then Begin ShowMessage('Only one instance can be running at a time!'); handle := FindWindow('TMyApplicationMainForm', nil); If IsWindow(handle) Then Begin ShowWindow(handle, SW_RESTORE And SW_SHOW); SetForegroundWindow(handle); SetFocus(handle); End; handle := 0; Exit; End; // Stuff from DPR... like Application.Run etc Finally If handle <> 0 Then CloseHandle(handle); End; End result is the same and you have smaller resource footprint. -
Unfortunately GetMatchingBracketEx does not support quotes - to be precise opener and closer pairs which are the same character. I wrote a quick handler for quotes, all is working like a charm: // This whole method was copied from https://stackoverflow.com/questions/18487553/synedit-onpainttransientdemo and modified // to draw rectangles instead of coloring. A few bugs were fixed and it was greatly simplified, too. Oh and while I figured // out what it is doind exactly, I placed some comments for easier understanding :) Procedure TEditorFrame.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType); Procedure PaintText(Const inPoint: TPoint; Const inText: String); Begin Case TransientType Of ttAfter: Begin // Clear brush and pen has font color: rectangle will be drawn SynEdit1.Canvas.Brush.Style := bsClear; SynEdit1.Canvas.Pen.Color := SynEdit1.Font.Color; End; ttBefore: Begin // Solid brush and pen has background color: Rectangle will be cleared SynEdit1.Canvas.Brush.Style := bsSolid; SynEdit1.Canvas.Pen.Color := SynEdit1.Color; End; End; SynEdit1.Canvas.Rectangle(inPoint.X, inPoint.Y, inPoint.X + SynEdit1.Canvas.TextWidth(inText), inPoint.Y + SynEdit1.Canvas.TextHeight(inText)); SynEdit1.Canvas.TextOut(inPoint.X, inPoint.Y, inText); End; Const OCSYMBOLS = '()[]{}<>''"'; Var bufcoord: TBufferCoord; tp: TPoint; index, a: Integer; c: Char; Begin c := #0; bufcoord := SynEdit1.CaretXY; If (bufcoord.Char = 0) Or (bufcoord.Line = 0) Then Exit; If bufcoord.Char > 1 Then Begin // Take the character BEHIND the cursor and check if it's a character we need to highlight c := SynEdit1.Lines[bufcoord.Line - 1][bufcoord.Char - 1]; index := OCSYMBOLS.IndexOf(c); End Else index := -1; If index = -1 Then If bufcoord.Char > Length(SynEdit1.Lines[bufcoord.Line - 1]) Then Exit Else Begin // The character behind the cursor wasn't something we need to highlight. // Take the character AFTER the cursor and check the same c := SynEdit1.Lines[bufcoord.Line - 1][bufcoord.Char]; index := OCSYMBOLS.IndexOf(c); If index = -1 Then Exit; End Else bufcoord.Char := bufcoord.Char - 1; // Originally this was a FOR cycle but it's a waste to go through all if only one iteration actually performs something. // During the search we already got the character which requires highlighting. Simply use that to save some cycles SynEdit1.Canvas.Font.Assign(SynEdit1.Font); tp := SynEdit1.RowColumnToPixels(SynEdit1.BufferToDisplayPos(bufcoord)); PaintText(tp, c); Case c Of '''', '"': 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; a := index - 1; // Try to find the previous quote of the same type While a > 0 Do Begin If SynEdit1.Lines[bufcoord.Line - 1][a] = c Then Break; Dec(a); End; If a = 0 Then Begin // No previous quote was found. Reset the position and try to find the next one a := index + 1; While a < Length(SynEdit1.Lines[bufcoord.Line - 1]) Do Begin If SynEdit1.Lines[bufcoord.Line - 1][a] = c Then Break; Inc(a); End; If a > Length(SynEdit1.Lines[bufcoord.Line - 1]) Then Exit; End; bufcoord.Char := a; tp := SynEdit1.RowColumnToPixels(SynEdit1.BufferToDisplayPos(bufcoord)); End; Else Begin bufcoord := SynEdit1.GetMatchingBracketEx(bufcoord, OCSYMBOLS); If (bufcoord.Char = 0) Or (bufcoord.Line = 0) Then Exit; tp := SynEdit1.RowColumnToPixels(SynEdit1.BufferToDisplayPos(bufcoord)); If tp.X <= SynEdit1.GutterWidth Then Exit; // We need to paint the opposite symbol now: if first one was an opener then a closer and vice versa. // Instead of an If statement we can simply use a XOr 1, as it will turn 1 -> 2 and 2 -> 1, 3 -> 4 and // 4 -> 3... giving the exact opposite symbol in the pair that we need c := OCSYMBOLS.Chars[index XOr 1]; End; End; PaintText(tp, c); End; Feel free to use it if you like; any comment or improvement is welcome š
-
@pyscripter The patch worked, it highlight correctly now! I saw that the request to be able to provide matching characters were also implemented so I changed the array to a string - this way no sorting is required and I can simply pass it to GetMatchingBracketEx. I'll take a look into the efficiency you mentioned. Thank you!
-
Delphiās TZipFile working on a stream
aehimself replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
Kind of necroing a thread, I'm aware but I was just informed: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/What's_New#RTL:_TZipFile Still doesn't support LZMA and still fails on slightly corrupted archives 7Zip extracts happily, though. -
I don't know which one is funnier: the reply, or that an MSFT marked it as an answer š
-
Hello, Our legacy application mainly consist of frames which are created and displayed runtime when needed. Several parts of these frames are the same, so these functionalities were created on a smaller frame which was then embedded to the main frame. So basically we have a form with a panel, we create a frame in that panel and the constructor creates an other frame, which is on the frame we are creating. The subfame has an imagelist on it. Recently our automatic test started to fail after creating and destroying ~600 frames because of "invalid parameter" when the DFM streaming was creating the bitmap to add it to the imagelist. When executing the same test from the Delphi 10.4.2 IDE, actually 2 error messages appear, one after the other, but at the same place: parameter incorrect and out of system resources. I managed to find out that the errors start appearing when the GDI object count reaches 9999 and a new frame is about to be created. If I clear all bitmaps from the imagelist I get an "Invalid imagelist" error when GDI object count reaches the limit. If I remove the ImageList from the frame, the test finishes successfully, without GDI object count raising above 2500. No matter how hard I try I can not reproduce the issue in a fresh appication even if I model the imagelist-on-a-frame-on-a-frame-in-a-panel-on-a-form layout. GDI object count simply stays at 42. Now, this is the first time I heard that there is such a thing as GDI objects and that there is a limit on it, so my experience is converging towards zero. We have DeLeaker purchased and is showing no memory leaks. There are tons and tons of GDI objects during runtime, all from the same source: Do you guys have any tips on how to find out what is causing this leak?
-
GDI object leak and overflow when TImageList is on a frame
aehimself replied to aehimself's topic in VCL
Well, yes and no. My repro was correct - in a way as it should have been done. In real world it could be like Repeat CreateInnerMostFrameWithImageList; Until False; and just wait until I run out of GDI objects. There were no leaks, just too many created at once due to a design issue. -
GDI object leak and overflow when TImageList is on a frame
aehimself replied to aehimself's topic in VCL
There was a small trick I was unaware of. The innermost frame (which contained the ImageList) was not created once, but at least 10-20 times. Then, based on some criteria only one of them was shown (it's a really bad practice in my opinion, but it is how it is). Since the ImageList contains 10-11 images, basically what I wanted to say is... 1 main frame easily created 100-200 GDI objects (or more) just for this one ImageList. To add salt to the wound, some of these main frames are not actually closed when they should be to improve loading time upon the next time they should be shown. Moving the ImageList to a separate location kept the amount of GDI objects below 2500 at all times and the test finished without any issues. At the end of the day, @Fr0sT.Brutal was right - there was no leak, there were just way too many created which would have been freed up normally once the application (frame) closes. Legacy code is the best. You can learn so much just from the design issues your predecessors made š Actually, without irony this time. I had no idea that "GDI object" as a thing exists and can make your program to crash! -
GDI object leak and overflow when TImageList is on a frame
aehimself replied to aehimself's topic in VCL
Yep, in my own projects me too. Unfortunately this application was written some 20+ years ago and contains several thousand frames and hundreds of custom components⦠it will take a while to check and correct all these small design mistakes :) -
GDI object leak and overflow when TImageList is on a frame
aehimself replied to aehimself's topic in VCL
That is true, and definitely could apply here (no memory / GDI leak upon application closure) but we are talking about auto-created components here. The imagelist is on the innermost frame, and that frame is embedded design time on the parent frame. Iāll check the whole chain tomorrow though - if just one is created runtime it can cause this. Thanks!