Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. I decided to use Delphi's own TDBGrid for one of my projects; I personally don't like installing a full 3rd party suite for a single component if I can just fix the built-in up...

    Long story short, I did exactly what @Lars Fosdal advised - just added the very same Unicode character to the TField's displayname (I think that's the property...?). No complaints from users, they all like it.

     

    I don't use the standard grid, but as TDBGrid descends from it I suppose it's very, very, very limited too. The only reason I don't break out of my comfort zone and look for a cheap alternative is that I managed to fix most of the inconveniences already. If I'd go back in time I'd slap myself a couple of times not even to start with it. It was just waaaaay too much effort 😞

     

    P.s.: my favorite "feature" is that the vertical progress bar had 3 positions: first record is active: topmost. Last record is active: downmost. Everything else inbetween - progress bar is at 50%. I seen no other component with this "design" yet.

    • Like 1

  2. 4 hours ago, dummzeuch said:

    Recent versions of Delphi (for a suitable definition of “recent”) come with a TZipFile class implemented in the unit System.Zip. This class has the really neat feature that it can not only read the contents of a ZIP file directly from a stream but also extract a file from that stream to a TBytes array, thus it does not require any file system access.

    E.g. imagine you have got a memory stream containing the contents of a ZIP archive. [read on in the blog post]

    Did TZipFile ever got a proper update? Although it's good to have something built in, https://github.com/ccy/delphi-zip adds LZMA and probably ZIP64 support. I myself forked that too to fix a memory leak and make it open some corrupted ZIP files too.

    I wonder if I can drop Zip2 in my projects and to use the built-in version again.

    • Like 1

  3. 11 hours ago, Fr0sT.Brutal said:

    Two controversial sentences, as for me. If UI is responsible, user can do some actions. If logic depends on receiving the control back, user is not supposed to do things that could change app's state while bg operation is running.

    The UI is a simple tabbed interface. One tab can be "blocked" (by disabling all buttons and actions when a BG operation starts; re-enabled when ends) but the others (and with that - the whole UI) must stay unblocked.

    Furthermore, a nice spinning loading indicator with a "Cancel" button looks and feels more "professional" than a frozen, whitened form what you only can close with the task manager.

     

    Edit: As I already took a shortcut with the While xx Do Application.ProcessMessages method and seeing where it lead to - I am already in the process of rewriting the whole "action" logic to pure event based. It's a nightmare. To any future reader: don't be like me. Write your code clean, so you have to write it only once 🙂


  4. Just now, FredS said:

    `WaitAsync` or something like that

    I'm completely new to System.Threading (I was told it exists today by one of my colleagues) and always used TThread descendants until now. I'll see what exactly this method does; thank you for the suggestion!

    2 minutes ago, FredS said:

    or just use a short timeout for `TTask.Wait` and call `Application.ProcessMessages`  while not terminated..

    The main purpose of this change is to get rid of the Application.ProcessMessages call in a loop, so this is not an option unfortunately.


  5. Yes, this will definitely get rid of the nullpointer but will still "swallow" all exceptions happening inside. During the past years I also grew to feel uncomfortable creating a local class without a Try...Finally block; however sometimes there's no way around.

     

    There is an other issue I forgot to mention - nested calls. Procedure1 calling procedure2, which starts a task and returns immediately. The issue is, Procedure1 will expect the data/action to be ready as it regains the program flow.

     

    So I guess there's no easy solution; I reap what I sow. One has to pay for his former mistakes 🙂

     

    Ps: I never really liked this implementation anyway.


  6. I was experimenting with this once but found that the merge when your app goes back online is not that easy if the data can be changed at multiple locations. Effectively you'll have to have a local database and keep your own transaction log (can be the raw SQL queries) of all the work which was done offline. When the connection is back up, replay that transaction log on the remote database. But if the database was changed by someone else on the remote location you'll have to automatically decide what to do with each failed query, and it will get annoying after a while. Furthermore, you already have a local database, which is only kept in-sync with the remote one... seems to be unnecessary.

    At the end I created my own datastore with classes and simply stored everything in a local JSON file. It was even easier to merge the modification sessions, as I had a full log of what happened and I did not want to change a value which I knew was deleted two weeks ago.

     

    If this is not an option for you, set up a local database with SQLite or alike... that will have the least overhead I can think of.

    You can also use a more advanced local database engine and use it's built-in replication to get the dirty work done.


  7. A long time ago I wrote an application where I was forced to push specific long-running operations into background threads to keep the UI responsive. Stupidly enough, each method wrapper kept the control away from the VCL thread and returned only when the background process finished. This was a quick and dirty solution so not much of the code had to be changed. For example:

    myclass := TMyClass.Create;
    // Setup myclass
    
    // Instead of myclass.DoWork, now it looks like this:
    WorkWithBackgroundThread(myclass);
    
    // Get the results out of myclass
    FreeAndNil(myclass);

    Where in WorkWithBackgroundThread lies the evil:

    workthread := TWorkThread.Create;
    // setup workthread
    workthread.Start;
    
    While Not workthread.Finished Do
     Begin
      Application.ProcessMessages;
      Sleep(10);
     End;

    This worked for a while, up until I updated a component the application is using and now it started to throw invalid pointer operations. Long story short, the execution pointer / counter jumps in-and-out of different methods called by window messages, corrupting some Strings in the process (in the debugger the value quickly changes to "inaccessible value") so yeah...

    I am thinking on re-writing the whole thing to be event driven, but unfortunately far too many logic already depend on not receiving the control back. Is there a way doing something like this in modern Delphi (10.2+) ? I noticed that now we have a System.Threading unit with some task implementations, but ITask.Wait also blocks the caller thread (and this unit does not have a high reputation as far as comments went).

     

    The other bad thing about these implementations is exceptions and freeing an object up:

    Var
     sl: TStringList;
     x: ITask;
    begin
     sl := TStringList.Create;
     Try
      Try
       x := TTask.Create(Procedure Begin
                                   Sleep(10000);
                                   sl.Add('eeeee');
                                   End);
       task.Start;
       // task.Wait; // will cause the UI to hang up
      Except
       On E:Exception Do ShowMessage(E.Message);
      End;
     Finally
      FreeAndNil(sl);
     End;

    Which will throw a nice and cozy nullpointer exception and is not getting caught in the except block... see my issue I suppose.

     

    So my question is: is there an easier salvation for me rather than the full refactor? I'd prefer not to use any 3rd party libraries to keep external dependency as low as possible.


  8. 3 minutes ago, idontknow said:

    I created the TwSocket from inside Thread.Execute, told him it shall use MultiThreading, and had a message loop running inside the TClientThread.

    Aren't those two are effectively the same thing? Afaik, .MultiThreaded := True only means the socket will use it's internal message pump.

    You easily can create a deadlock in any VCL application if you have two "While xxx Do Application.ProcessMessages" blocks running "simultaneously". Maybe this is the case here...?


  9. 2 hours ago, Kas Ob. said:

    Those packets !, i don't think belongs to ICS per se but most likely to OpenSSL, either some sort of renegotiating or heartbeat or something else..

    That was my initial thought when I started to read this thread. In cases like this I try to disable as much 3-rd party dependencies as I possibly can to see if the problem still occurs. SSL would be my first to go in this case.


  10. 6 minutes ago, Mike Torrettinni said:

    For now staying with SourceTree - it's like like Fork, but free.

    At work most of my colleagues prefer SourceTree. I find the UI sluggish and hard to complete simple tasks.

    I personally prefer VS Code's implementation (having it installed as the default text editor on all of my PCs might be a bias-factor, though).

    For me, Git handling is really easy, fast and easy to understand.

     

    As others already pointed out it's all a matter of taste. Some of my colleagues are still starting up Visual Studio for a simple pull, so... 🙂


  11. 36 minutes ago, dummzeuch said:

    Hold the mouse over the profile picture of the person you want to ignore. You will get additional information on him/her. On that panel there is a "Ignore User" button.

    There is that little beauty! I was not aware that we have a user fly-out menu until now:

     

    image.png.2cfa73b7a9c1759863545fd7b05374d5.png


  12. I have a project, with multiple source files added from other projects. For a while now I switched from TRTLCriticalSection to TMonitor as it requires no additional unit imports and should be more cross-platform (?) than the prior.

    Now; most of the said imported files are using TMonitors the usual way:

    TMonitor.Enter(Self);
    Try
     // Do stuff
    Finally
     TMonitor.Exit(Self);
    End;

    Works like a charm. I added a new VCL form and a private worker thread implementation. In this new unit, I can not use TMonitor.Enter as the compiler says the method does not exist!

     

    image.png.29675a49fbd2c97cb36ef52a4eee8a3b.png

     

    Ctrl-clicking on TMonitor in the working and the non-working source file both redirects me to the same TMonitor implementation; System.pas:817. Hovering the mouse shows the same tooltip - record, declared in System.pas.

     

    Which is even more strange, the project can not even be compiled if I leave the underlined TMonitor calls in this one source file so I suspect it's not only LSP. At the moment I changed TMonitors to TCriticalSections in this file and it is working fine this way.

     

    Has anyone seen this issue so far? Any tips how I can make it to work?

     


  13. 17 hours ago, Remy Lebeau said:

    An owned object can be destroyed at any time, even before its Owner is destroyed.  If the object is destroyed first, it will simply remove itself from its Owner so that that Owner does not try to destroy it again.

    I swear I had "random" access violations when an application was closed which was caused by freeing something owned by the form... then, when the form was closed it tried to free it again.

    Now, on Delphi 10.4 I simply can NOT reproduce the issue, no matter how hard I try.

     

    Checking the destructor code of TComponent:

    destructor TComponent.Destroy;
    begin
      Destroying;
      RemoveFreeNotifications;
      DestroyComponents;
      if FOwner <> nil then FOwner.RemoveComponent(Self); // THIS LINE
      FObservers.Free;
      inherited Destroy;
    end;

    Was THIS LINE always there? If I'm not mistaken the bug mentioned happened on Delphi 10 Seattle, before we made the upgrade... is it possible that it was implemented inbetween?

    If not, how exactly it can be reproduced?!

     

    Parents of mentioned components were frames, if it makes any difference


  14. 4 hours ago, Ian Branch said:

     

    
          //
      MyForm := TMyForm.Create(self);
      MyForm.sFormID := '3';    //  Pass a key value.
      MyForm.Show;
      //

     

    You are creating the form with the owner of your main form. When you close your main form therefore (as it owns your subform) it attempts to free it. But since it is already done in the onClose event (Action := caFree) it is trying to free a non-existing object resulting an access violation.

    Try to create your subform like

     

    TMyForm.Create(nil);

     

    Or don't use caFree in the onClose event.

     

    You should consider using

    MyForm := TMyForm.Create(nil);
    Try
     MyForm.sFormID := '3';
     MyForm.ShowModal;
    Finally
     MyForm.Free;
    End;

    if applicable.


  15. 15 minutes ago, PeterPanettone said:

    Internally, a batch file is being started by a CommandLine. Windows File Explorer (but also other shell-programs) can provide that CommandLine, e.g. by double-clicking on a batch file e.g. in Windows File Explorer.

    And here I though, that the CommandLine is internally using a ConHost process (Windows Console Host) which is provided by the operating system with AllocConsole for example. I must be wrong.

     

    image.png.3b5f260120b4bc393cce0b157511ed96.png

     

    16 minutes ago, PeterPanettone said:

    BTW, it would be interesting to inspect the source code of PatchTool.exe...

    In that, we definitely agree.


  16. 38 minutes ago, PeterPanettone said:

    Obviously you don't know what you are saying. This has nothing to do with UAC (although UAC is involved). I suggest that you re-read what I have written. If you don't understand any particular topic then please ask me.

    Obviously. I forgot that the one and only way to start a batch file is Windows Explorer.

    Why are we paying sysadmins, if the answer is always so easy...? :classic_unsure:


  17. There is no such thing as hardwired to File Explorer; it is just an application which requires elevated privileges due to it unzips files to the Program Files folder. I suggest you to take a look at how UAC works on Vista+ and how you can auto-elevate everything if it disturbs you that much.

    While I completely admit that this "patch tool" is utterly useless, don't blame Emba because of how UAC works.


  18. 4 hours ago, Fr0sT.Brutal said:

    Poor man's solution: Project group with all your 100 projects -> open in RAD -> build all -> go make yourself some tea

    While the idea clearly works, there's a huge flaw with it: I don't like tea.

    • Haha 2

  19. I'd simply re-throw exceptions in an understandable way; you now can even use RaiseOuterException to include the data from the first one.

    When I am working in a service application which should operate 24/7 without interruptions, I'm placing a Try ... Except in the worker thread only. It picks an item to process from the queue, if it fails, it logs why and places it back to the end of the queue. Once an item failed to process 3 times it is discarded.

     

    Specifications (even if given by the client) are only specifications. Our client keeps sending invalid XMLs for us to process, even though they created the validating XSD for that very document.

    So yes, expect bad data; no matter what. But depending on the needs - don't change values and/or swallow errors because the code looks... cleaner.

    22 hours ago, David Schwartz said:

    I suggested that all of these functions should be replaced with ones that don't throw exceptions, like StrToXxxDef(...), and that at least there should be Try...finally fencing around each method anyway just in case something unexpectedly throws an exception.

    I'd suggest TryStrToInt and it's counterparts, or simply Val. That way you know if/where the expected data is malformed and can Rase Exception.Create('The length of your shoes must be a number, not "' + s + '"');

×