Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. On 9/4/2019 at 10:53 PM, Remy Lebeau said:

    Random crashes like this usually tend to imply that there is possible memory corruption at play.  What does Processor_analyze() actually do internally (if you have its source code), and are you sure you have declared it correctly (if importing it from a DLL)?

    This.

    I remember having a similar issue a couple of years ago calling .DLL exported functions when I switched from D7 to D10.2. The issue was memory allocation for a string (* SizeOf(Char) was not included) which lead to a nice and beautiful memory corruption. The application always crashed several functions later so it was a pain in the butt to find it.

    Anyways, memory corruption would indeed be my guess in this case, too.


  2. We did something like this in a commercial application: it only handles numbers up to 9999 but in multiple languages if I'm not mistaken.

    To be completely honest, it's quicker just to write your own from scratch which will satisfy your needs rather than to look for one. The code I was talking about is about 50-100 lines in Delphi...

     

    Due to it's commercial nature I'll not be able to share sniplets, but it's basically these rules in lots of "if" statements 🙂

    • Like 1

  3. 1 minute ago, Angus Robertson said:

    Not looked for a long time, but TService used to use old APIs and was never updated. 

     

    Main problem with long term support is RTL changes in new versions of Delphi, not often, but breaks new compilers.

    Agreed, this is a possibility in most cases; I just don't see a reasonable chance when it comes to Windows Services.

    Even if Embarcadero would decide to re-write the logic, they'd probably keep the event handlers and the class name so it stays backwards compatible... I hope 🙂 if not, most of us will be in a big trouble 🙂


  4. When my applications stop processing Windows messages it's always a very long processing in the VCL thread. Like reconnecting to a database, refreshing a huge dataset or I simply messed something up and my code got in an endless loop 🙂

    This is when correct logging can save your life: I'm usually logging when an action starts and when it ends. When the application freezes just check which action started, which did not report back as finished.

    • Like 1

  5. Hello,

     

    I will be honest I don't understand the question correctly, but if you'd like to identify if the received string is a JSON or an XML, you can use this:

     

    Uses System.JSON;

    [...]

    Var
     x: TJSONValue;
    begin
     x := TJSONObject.ParseJSONValue('this is the received file');
     If x = nil Then Begin
                     // File is NOT JSON -> it is XML (or other webserver generated error page)
                     End
       Else Begin
            // File is JSON, values can now be accessed by object "x"
            End;
    
    end;

    In the x = nil branch you could make an XML-to-object parsing to confirm if it's XML and simply stop processing if not. I never worked with XMLs from my codes so I have no knowledge on how to do that, though 😞


  6. 49 minutes ago, microtronx said:

    Has someone used SvCom in the past; are the components still supported?

    I personally never used SvCom but even if it's unsupported I would not worry about it. The way Windows handles services (service control messages) did not and could not change as it would break backwards compatibility (you wouldn't be able to install / start a service on Windows 2003, only on 2019 - never seen that, unless it was an API dependency in the business logic). A component to create a service application mostly contains a code to handle these SCMs, which are the same since Windows 2000.


  7. I wrote all of my Windows service applications based on Delphi's TService and I did not find any showstoppers which made me want to change it. It's robust, small and gets the job done quite well.

    A small tip though. Since service applications are hard to debug, I started to implement the whole business logic as a simple Class which is being created and destroyed based on what is happening with the service.

     

    Change the .DPR like this:

     If Not FindCmdLineSwitch('console', True) Then Begin
                                                    If Not Application.DelayInitialize Or Application.Installing Then Application.Initialize;
                                                    Application.CreateForm(TService1, Service1);
                                                    Application.Run;
                                                    End
       Else StartWithConsole(TMyServiceClass);

    ...where StartWithConsole is just a small procedure which creates a console window with AllocConsole, creates the service class, redirects the logging output to StdOut and handles the console handlers (like Ctrl-C, etc).

    This way there's only one executable, which can be started as a command line application for easy debugging and as a Windows Service as well.

     

    P.s.: don't forget about creating a custom message pump, some components rely on Windows Messages to work properly!


  8. On 9/12/2019 at 1:31 PM, David Heffernan said:

    Don't think that's true. That's the point of the broadcast message. The shell updates its environment and uses that when creating new processes. 

    I just checked my old codes and yes - they do take effect after the broadcast was sent. Must have mistaken it with setting it somewhere else in Windows...?


  9. 5 hours ago, Der schöne Günther said:

    Are you sure F8 is the right hotkey? I think in your case, it should be Shift+F8 (run until return).

    Also, TStringList is from Delphis default RTL. Be sure to have "With Debug DCU files" checked in your project or you cannot have breakpoints in there.

    Shift-F8 will return at the end of the calling method, in this case the OnClick handler. It will NOT put you inside the Except block in any case.

    As for TStringList, I just chose something random, you can even put

    Raise Exception.Create('hello world');

    instead. Also, I believe that when you try to access an uninitialized object it does not matter where it comes from, as it does not exist...

     

    I am afraid that @Fr0sT.Brutal will be right, and this - too - is going to be a limitation of the IDE.

     

    Too bad 🙂


  10. Based on experience I am against this type of data storage for several reasons:

    - Some database engines can not search in BLOB fields. Now it might not be an issue in the beginning but it makes investigating data corruption / debugging processing issues a living nightmare

    - Downloading blob fields are slow, no matter what. Especially over the network. In our production databases I can select hundreds of thousands of rows with 150+ columns but only a fraction of that 2 fields: an ID and a BLOB storing an XML

    - Database engines are robust, they were built for having columns. While it can be hard / intimidating to see the relations at first time, they have no issues handling a larger number of columns

    - Unless the DB engine has built-in JSON support, you'll not be able to query and compare a specific value on database side, you'll always have to download and compare from code, increasing memory, network and CPU usage of your program. Considering a well-fed metal (our slang for a server with enough resource + overhead) running the database, this will always be significantly slower than letting the DB engine to take care of these

     

    The only reasonable case when storing a JSON / XML in a database I can see is if you receive that from an external source (like a REST server or a 3rd party sending your software some information) and you want to keep those chunks for auditing purposes. Sooner or later you'll face one of the issues I mentioned, though 🙂

     

    We did the same about 8 years ago (only with XMLs) and now we are looking into opportunities to undo it...

    • Like 2

  11. A quick and dirty way of adding this component in design time is to switch the platform back to Win32, drop the component and then switch back (even completely remove the Win32 platform).

    I usually have to do this with my 64bit applications using database access components.


  12. Hello,

     

    I was just wondering if this is "normal" or just an anomaly on my side. Consider the following code:

    Quote

    procedure TForm1.Button1Click(Sender: TObject);
    Var
     sl: TStringList;
    begin
     Try
      sl.Add('Hello');
     Except
      On E:Exception Do Begin
                        ShowMessage(E.Message);
                        End;
     End;
    end;

    Set a breakpoint on sl.Add and execute. When the execution stops, press F8. However the Except block executes, the Delphi debugger does not continue the step-by-step debugging. I know, you can put a breakpoint on On E:Exception but it would be a lot nicer to see how the exception is flowing (especially through 5-10 units until it reaches the final handler).

     

    Is there a way to achieve this or it's just a limitation of the debugger?

     

    Cheers!


  13. 14 hours ago, Mike Torrettinni said:

    Don't you need more info than just simple boolean variable? Like:

    - new version number

    - change log

    - maybe that new version is available but maintenance has expired for this customer

    - download link or link for change log

     

     

    Hello,

     

    Of course, this information is included in the update file and (except for the download link) are published by the thread object as properties. For simplicity's sake I decided not to include them, but here you go 🙂


  14. On 6/12/2019 at 10:55 PM, Schokohase said:

    @Gary Mugford 

     

    You should start a new thread and leave a link to that new thread here

    A thread with local variables was always my solution as well. When my application starts it launches a background thread which downloads the update definition file and notifies the main form to show the "Updates are available". Everything until here is not locking the UI.

    When the update form is shown and the user clicked "Yes" the main form calls a .Update method in the thread which downloads the update file, verifies it etc. and returns a simple bool if it was successful or not.

    This way no global variables were defined, no complex types were passed between threads and most of the work was done without locking the UI.

     

    Threads are a good thing.


  15. I am using Delphi 10.2.3 and wow, I wish I knew about this before I started my first project... now I have to go back and correct them all 😄

    I can confirm that s.ToJSON is producing the exact same output as it was originally requested:

     

    s.ToString with the replacements produces "\"value\\with\n{strange}chars/\""

    s.ToJSON produces "\"value\\with\r\n{strange}chars\/\""

     

    Lesson learned. Do not try to reinvent the wheel, just realize if I messed up the implementation.

    (I wonder why it has a .ToString if it is not producing a correct syntax though...)


  16. I used the system menu to allow users to change the VCL style of my application. The code looked like this:

    Procedure TMyApplicationMainForm.FormShow(Sender: TObject);
    Var
     sysmenu: THandle;
     a: Integer;
     flags: Cardinal;
    Begin
     sysmenu := GetSystemMenu(Self.Handle, False);
     AppendMenu(sysmenu, MF_SEPARATOR, Word(-1), '');
     For a := Low(_validstyles) To High(_validstyles) Do
      Begin
       flags := MF_STRING;
       If TStyleManager.ActiveStyle.Name = _validstyles[a] Then flags := flags Or MF_CHECKED;
       AppendMenu(sysmenu, flags, WM_USER + 1 + a, PWideChar(_validstyles[a]));
      End;
    End;

    Instead of Self.Handle I suppose you can supply a different form handle returned by FindWindow. This solution will send a WM_USER message to the form when the appended item is clicked but the foreign one has to process that correctly...

     

    I guess you can append to the system menu of an other application, but I doubt that it will ever work correctly. At least not this way 😞


  17. On 6/7/2019 at 11:49 AM, Jacek Laskowski said:

    Does Delphi have a function in RTL that will encode the text to a form consistent with the JSON value?

     

    For example, from string:

     

    "value\with
    {strange}chars/"

     

    to:

     

    "\"value\\with\r\n{strange}chars\/\""

     

    It is possible?

    Although Delphi has a native JSON implementation (System.JSON unit) it is not perfect. It seems to be good in parsing, but assembly needs a bit of help. I use it in most of my latest codes though.

    To achieve what you'd like:

    procedure TForm1.Button1Click(Sender: TObject);
    const
     KEK = '"value\with' + sLineBreak + '{strange}chars/"';
    Var
     s: TJSONString;
    begin
     s := TJSONString.Create(KEK.Replace('\', '\\').Replace(#9, '\t').Replace(sLineBreak, '\n').Replace(#10, '\n').Replace(#13, '\n'));
     Try
      Edit1.Text := s.ToString;
     Finally
      FreeAndNil(s);
     End;
    end;

    Without the loads of .Replace statements it tends to generate invalid JSONs. So far this chain always fit my needs.
     

    It needs a little bit of time to get used to it, but it's lightweight and fast.

     

    Edit: I don't think you actually have to escape forward slashes in JSON string literals, they seem to pass validation without them. I did not read the whitesheet though, so I could be wrong. Just add .Replace('/', '\/') in the chain to do that.


  18. I always used this code, which works fine. The only issue is if the other application is expecting an input (even a keypress to finish) because ReadFile will wait endlessly as far as I recall. It is easy to finetune it though.

    function GetDosOutput(CommandLine: string; var ReturnString: String; var ReturnCode: Cardinal): Boolean;
    const
      LOGON_WITH_PROFILE = $00000001;
    var
      SA: TSecurityAttributes;
      SI: TStartupInfo;
      PI: TProcessInformation;
      StdOutPipeRead, StdOutPipeWrite: THandle;
      WasOK: Boolean;
      Buffer: array[0..255] of AnsiChar;
      BytesRead: Cardinal;
      WorkDir: string;
      Handle: Boolean;
    begin
      Handle := False;
      ReturnCode := 255;
      Result := False;
      ReturnString := '';
      SA.nLength := SizeOf(SA);
      SA.bInheritHandle := True;
      SA.lpSecurityDescriptor := nil;
      CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
      Try
       With SI Do Begin
                  FillChar(SI, SizeOf(SI), 0);
                  cb := SizeOf(SI);
                  dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
                  wShowWindow := SW_HIDE;
                  //wShowWindow := SW_MINIMIZE;
                  hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
                  hStdOutput := StdOutPipeWrite;
                  hStdError := StdOutPipeWrite;
                  End;
       WorkDir := '.';
       Handle := CreateProcess(nil, PChar(CommandLine), nil, nil, True, CREATE_NEW_PROCESS_GROUP or CREATE_NEW_CONSOLE, nil, PChar(WorkDir), SI, PI);
       CloseHandle(StdOutPipeWrite);
       If Handle Then Try
                       Repeat
                        WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
                        If BytesRead > 0 Then Begin
                                              Buffer[BytesRead] := #0;
                                              ReturnString := ReturnString + Buffer;
                                              End;
                       Until Not WasOK Or (BytesRead = 0);
                       WaitForSingleObject(PI.hProcess, INFINITE);
                       GetExitCodeProcess(PI.hProcess, ReturnCode);
                       While (Length(ReturnString)<>0) And ((ReturnString[Length(ReturnString)]=#10) Or (ReturnString[Length(ReturnString)]=#13)) Do
                        Delete(ReturnString,Length(ReturnString),1);
                       Result := True;
                      Finally
                       CloseHandle(PI.hThread);
                       CloseHandle(PI.hProcess);
                      End;
      Finally
       CloseHandle(StdOutPipeRead);
      End;
    end;

     

    • Thanks 2

  19. Hello guys,

     

    I am developing a lightweight, customized SQL client. The main form only has a PageControl, all business logic reside on a frame. When the user chooses a connection, a new tabsheet is created, and a new instance of the said frame is placed upon it. Simple, and works like a charm.

    To please some people I decided to allow VLC styling of the application but this introduced a really strange issue. If the popup menu resides on the frame and has an image list assigned, it seems like that the menu is not drawn properly:

     

    01-Styled_no_text.png.be97a4ab869673b43875ec74cddb2ad5.png

     

    If I change the Style back to the system default (or simply unassign the imagelist component from the TPopupMenu), everything works perfectly:

     

    02-Not_styled_text.png.fa6c675e798938d50a82492be3176688.png03-Styled_noicons_text.png.8ff65d0099189eb398894202d0bfa993.png

     

    Menu has no owner draw set. What I tried so far:

    - Add a new menu item during runtime to force a refresh of the menu

    - Unassign and reassign the popup menu during runtime from the DBGrid component
    - Unassign and reassign the image list during runtime

    - Set all ImageIndexes to -1, even removed all menu items during design time

    - Added Vcl.Styles to the uses clause of the frame

    - Changing Style during runtime has no effect. If I change back to System default, popup menu start being drawn correctly

    - Behavior is the same on additional modal forms, menu is not drawn correctly if image list is assigned

     

    In the mean time, the popup menu on the main form works perfectly. Which is even more strange, if I assign the main forms popup menu to the DB grid during runtime, it is drawn correctly. I feel like this will be something to do with the menus not being a child of the main form, or like a special hook I'm missing (like how you are forcing styling on a SynEdit component) but I could be far from the truth.

    I'm running out of ideas. Did anyone face this issue already? Any tips which I could try to make this to work?

     

    Thanks all!

    image.png

    image.png

×