Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Everything posted by aehimself

  1. aehimself

    Interface question

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

    NetCom7 clients chat implementation

    @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.
  3. aehimself

    SynEdit bracket highlight

    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!
  4. aehimself

    SynEdit bracket highlight

    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?
  5. 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.
  6. aehimself

    SynEdit bracket highlight

    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 πŸ™‚
  7. aehimself

    SynEdit bracket highlight

    @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!
  8. 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.
  9. aehimself

    enable/disable the internet connection?

    I don't know which one is funnier: the reply, or that an MSFT marked it as an answer πŸ˜„
  10. 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?
  11. aehimself

    GDI object leak and overflow when TImageList is on a frame

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

    GDI object leak and overflow when TImageList is on a frame

    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!
  13. aehimself

    GDI object leak and overflow when TImageList is on a frame

    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 :)
  14. aehimself

    GDI object leak and overflow when TImageList is on a frame

    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!
  15. aehimself

    GDI object leak and overflow when TImageList is on a frame

    The issue is, about 600 frames are opened and closed before the issue detectibly appears. My sanity will give up first than to count each and every TImageList and track their lifetime. Also, any Delphi object leak would be reported by DeLeaker upon application closure. I spent more than a year to get that list clean, so no more unfreed imagelists / TBitmaps πŸ™‚
  16. aehimself

    GDI object leak and overflow when TImageList is on a frame

    Yes, it is. My first idea was that the skinning of TcxPageControl caused this (we recently applied some DevExpress skins based on user input) as it was the most recent change with comctrls involvement. My issue is that the ImageList is simply dropped on the innermost frame design-time. It should be created and freed up automatically. If there would be a bug in the Delphi wrapper, it would be visible in my test case, too. The real code contains a cximageList (DevExpress version) but symptoms are exactly the same if I replace it with a simply TImageList. This is why I didn't mention in until now. I doubt it will make any difference, though.
  17. aehimself

    GDI object leak and overflow when TImageList is on a frame

    I already introduced my boss to D11 when it was released and I personally was the one who said I don't see a reason why we would need to upgrade. Good thing is that I asked him to doublecheck our licensing and we should still be eligible. Will be a funny talk, though πŸ™‚ My personal OCD still hasn't calmed down yet. How come a completely unused TImageList can cause a GDI object leak? Or I'm just looking at the completely wrong direction. That is unfortunately a completely valid option, too.
  18. aehimself

    GDI object leak and overflow when TImageList is on a frame

    There is a combobox, which is normally pulling those images from the ImageList. I have a hunch that if I get rid of the imagelist and push those bitmaps in resources the issue is solved... it just bugs me that I don't know what is causing the mayhem now. Of course. I tried to replicate everything as close to the real thing as possible. Btw, creating the frames from constructor means inherited... by embedded I meant to add the innermost frame to an outer one from the designer. I suspect the VCL is working correctly, this is why creating and destroying my test frame 100k + times makes no difference. I'm suspecting something funky going on here... like the Z-order TTreeNode leak with styles active or some 3rd party component interference. My knowledge is severely limited in this area though, that's why I need starting points. I'll do a quick search to see if GetDC is called in our custom components / frame code somewhere, but I doubt. The complexity the ancient code is written is pretty basic... data storage is a TStringList 78% of the times πŸ™‚
  19. aehimself

    GDI object leak and overflow when TImageList is on a frame

    It is the installation default, which is 10000 decimal. It seems irrelevant though, as if even an abandoned ImageList is on the innermost frame (all code commented out, not used at all, no images in it) will cause the application to have 7500+ GDI objects in about 20 minutes. If I completely remove the ImageList, GDI object count stays top 2500.
  20. aehimself

    Pos, SplitString

    Var firstword = String(Memo1.Text).Trim.Substring(0, String(Memo1.Text).Trim.IndexOf(' ')); Ineffective, but will yield you results πŸ™‚ But as I mentioned, this will work only in some cases. I'd recommend against using such a solution anywhere near production.
  21. aehimself

    Patch for Delphi 11 Alexandria available

    Seems... you are perfect, too? πŸ™‚
  22. aehimself

    Pos, SplitString

    This won't work, you have to parse at least a little bit. E.g.: -- This is a very good query /* Just for extra complexity */ SELECT * FROM MyTable is a valid SQL query. Zeos has a SmartOpen property, so you can call .Open even on queries with no resultset returned - although I never tested this. My personal solution is: Function IsOpenQuery(Const inSQL: String): Boolean; Var fword: String; tokenizer: TZTokenizer; token: TZToken; Begin tokenizer := TZTokenizer.Create; fword := ''; Try For token In tokenizer.TokenizeBuffer(inSQL, [toSkipUnknown, toSkipWhitespaces, toSkipComments, toSkipEOF, toUnifyNumbers]) Do If token.TokenType = ttWord Then Begin fword := Copy(token.P, 0, token.L).ToLower; Break; End; Finally FreeAndNil(tokenizer); End; Result := (fword = 'select') Or (fword = 'show') { Or fword.IsEmpty }; End; It uses Zeos's tokenizer for parsing so I don't have to worry about comments and stuff. Although it works, it is still vulnerable to the fact that (probably) there are a lot more statements than SELECT and SHOW, which will return a resultset.
  23. aehimself

    enable/disable the internet connection?

    https://en.wikipedia.org/wiki/Stockholm_syndrome
  24. aehimself

    enable/disable the internet connection?

    My thoughts exactly. I saw videos Windows rebooting itself on some live streams and conferences but never happened to me, not even once; and I was testing it from the first Insider build. I also have to admit that Microsoft did a great job around updates (less downtime, torrent-like downloading from Internet and neighboring PCs, active hour detection, etc); whoever says otherwise install Vista or 7 vanilla and try to patch it up to current. I have a feeling that lots of absolute-negative opinions of Windows 10 would flip if only the logo would be Apple instead of Microsoft.
  25. aehimself

    Delphi 11.0 has a different form Caption offset than Delphi 10.4

    Form 652. That's a pretty extended hello world collection!
Γ—