Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. For a long time I am using a custom settings class in most of my applications to store it's data. They all have a .AsJSON property to export / import everything, which is automatically called by .Load and .Save. It's working perfectly. The only drawback is that I had to write the getter and setter methods for each instance, even if it's only a sub-class of the main settings. And it was getting tiring.

    I started to experiment with RTTI to make the class detect it's own properties in the getter and setter so I finally can forget about these in general situations. Exporting works like a charm; I only could not properly handle TLists but I wrote a custom list wrapper for the rescue.

     

    The importing (loading from JSON) part caused some headache, which I can't seem to solve. One part is left, if the property / field is an array type. The inner helper method looks like this:

    Function TApplicationSettings.JSONToValue(inJSON: TJSONValue; inTypeInfo: PTypeInfo): TValue;
    Var
     enum: TJSONArray.TEnumerator;
     valarray: TArray<TValue>;
     a: Integer;
    Begin
     Result := TValue.Empty;
     Case inTypeInfo.Kind Of
      [...]
      tkArray, tkDynArray:
      Begin
       enum := TJSONArray(inJSON).GetEnumerator;
       SetLength(valarray, TJSONArray(inJSON).Count);
       If Assigned(enum) Then Try
        a := 0;
        While enum.MoveNext Do
        Begin
         valarray[a] := JSONToValue(enum.Current, inTypeInfo);
         Inc(a);
        End;
       Finally
        enum.Free;
       End;
       Result := TValue.FromArray(inTypeInfo, valarray);
      End;
     End;
    End;

    ..and the outer helper:

    Procedure TApplicationSettings.JSONToRttiMember(inJSON: TJSONValue; inRttiMember: TRttiMember);
    Var
     val: TValue;
     pi: PTypeInfo;
     ppi: PPropInfo;
    Begin
     If Not Assigned(inJSON) Then Exit;
     ppi := GetPropInfo(Self, inRttiMember.Name);
     If Not Assigned(ppi) Then Exit;
     pi := ppi^.PropType^;
     val := JSONToValue(inJSON, pi);
     If val.IsEmpty Then Exit;
     If inRttiMember Is TRttiProperty Then (inRttiMember As TRttiProperty).SetValue(Self, val)
       Else
     If inRttiMember Is TRttiField Then (inRttiMember As TRttiField).SetValue(Self, val);
    End;

    This most possibly where my mistake is, as GetPropInfo always returns nil. Before this solution I passed the (PropertyType / FieldType).TypeKind as a parameter to the inner helper, but in that case I won't have access to PTypeInfo, which is needed for the TValue.FromArray.

    So the question is; how can I extract the PTypeInfo of an RTTI field / property? Because the code above doesn't, that's for sure 🙂


  2. Holy mess, I skipped only one day with my gadget addiction and I came back to see my E-mail inbox exploded! As it was mentioned countless times, you must get a stack trace of where the error happens. Without that most probably we are all looking at the wrong place. Far from ideal, but SendMessage works in this scenario. I doubt that Win7 - Win10 will be an issue either; I am testing most of my code with everything from Windows 2000 and up and so far only new features (which does not exist in earlier editions) caused headache. And just because it's a framework, don't be afraid to touch it! At work we have a similar setup - part of our legacy app's code is used as a base for many other applications. I started to sort out some basic leaks and you can not imagine what was buried deep below. A framework is the nothing but code, which can contain bugs even if thousands of other applications are using it.

    If you are absolutely sure you can not get a stack trace, at least pollute your methods with debug messages using a new logger with proper synchronization. When the AV occures, just look which method started and did not finish. Alas, using proper synchronization in your new debug logger might solve the synchronization issue (if it is a synchronization issue after all...) with those tiny-tiny delays. So yeah, reinventing the wheel is not a smart thing to do, if we have MadExcept and similar.


  3. Read of address with a low number means a nullpointer exception; the code is attempting to access an already freed / uninitialized object. Did you manage to catch where the AV is raised or you just see it in the logs?

    I was suspecting that the thread's "stat" variable is overwritten and therefore relocated; but it's unlikely because

    1, it would show a random address, not 1.

    2, thanks to @Anders Melander I remembered that SendMessage does not return until the message is processed. So unless "stat" can be written from the outside...

     


  4. fmCreate, according to Delphi help = Create a file with the given name. If a file with the given name exists, override the existing file and open it in write mode.

    I don't use FileStreams that often, but won't you effectively null the contents...?

     

    I would go the old-school way; however I have no idea if it would work:

    Var
     f: File;
    Begin
     If Not FileExists('myfile') Then Exit(False);
     {$I-}
     AssignFile(f, 'myfile');
     Append(f);
     Result := IOResult <> 0;
     If Not Result Then CloseFile(f);
    End;

     


  5. Sometimes we had this when using License Server activation and installing from an .ISO image. I personally never faced this issue, but my colleague succeeded by starting the network installer, importing the .slip file and then using the .ISO installer.

    And I thought these should be the same...? 😐


  6. 33 minutes ago, Mike Torrettinni said:

    Yes, of course, make sense. Unless you use 'open in new window' option for each resource you want monitored live data.

    Yes, makes sense; did not think of this. What you can do is to have a TFrame with basic functionality (placeholder for the monitor, invisible panel, etc) and add a "pop out" button there.

    When you click it, you simply create a TForm and move the current TFrame on that new form. Just, don't forget to free the tabsheet 🙂

     

    I was also experimenting with auto-docking tabsheets a while ago. When you drag them out they turned to forms automatically, and vice versa. Unfortunately though the performance was so bad (flickering and lagging) that I abandoned the idea. Most probably I was doing something wrong; you can look in this direction too instead of a pop-out button.

     

    Edit: Seems pretty easy, I don't know what I messed up before.

     

     

    Edit-edit: It works, but the flickering is still there when dragging around the "child" window; even if DoubleBuffered is on.

    If you don't want to watch the video: Set DockSite of the PageControl to True. Set DragKind of the "child" form to dkDock and in the OnClose event set the Action to caFree. Then, create your "child" forms like this:

    Var
     f: TForm2;
    begin
     f := TForm2.Create(Application);
     f.ManualDock(PageControl1);
     f.Show;
    End;

     


  7. Talking from an end-user perspective I'd go insane if one application would open tens of forms. I have one screen only and it will be polluted within seconds.

    What I would do is to have only one form, with a list on the left and a PageControl on the right. Instead of forms, create a new TabSheet for the monitors. When there is an alert, you can change the ImageIndex property to change the icon of the tab sheet; signaling the user that attention is needed.

     

    As for the "messages" I would put an invisible panel inside every tab sheet, on top of everything. It would contain only a TMemo and a button to dismiss. This way if multiple alerts are generated without interaction, you can append it to the memo instead of having 3-4 popup windows for a single monitor.

    • Like 1

  8. 20 minutes ago, Fr0sT.Brutal said:

    I'm pretty sure local network servers send periodic packets to each of network peer. Maybe when someone invokes list of network machines.

    Now this applies only to Windows as I'm not working with Linux machines, but the Windows Computer Browser service is basically collecting a bunch of broadcasted information and then others connect to it via TCP (source). If the activity-based startup is locked to an IP it will not be triggered by broadcast messages; the rest of the communication will be between other PCs and the current directory master.

     

    Alas, this only applies for standard services. My program attempting to ping this IP to see if it's alive or not will trigger the power on for sure.

    • Thanks 1

  9. 11 hours ago, M-Brig said:

    Does anyone know what type of FTP client File Explorer uses? This may help us figure out the problem. The FTP from the command prompt and Power shell's FTP client do not work on these devices.

    If File Explorer is the standard Windows file management application which pops up when you click on My Computer / This PC; and you just type ftp://host_or_ip_of_device it's hardwired in the shell for sure. I am also certain that Microsoft did not maintain this Client as they saw the future in WebDav; not FTP; but to be honest it's irrelevant in your case. You said your only issue is that your device is inserting a null character at the end of each file upon listing, which you save to a file. I'm not sure if Delphi is handling #0 characters in strings since Unicode; but you can try:

    Var
     sl: TStringList;
     a: Integer;
    Begin
     sl := TStringList.Create;
     Try
      sl.Delimiter := #0;
      sl.LoadFromFile('MyList.lst');
      For a := 0 To sl.Count - 1 Do
       // Do something with the file at sl[a]
     Finally
      sl.Free;
     End;
    End;

    Or...

    Uses System.IOUtils;
    
    Var
     sa: TArray<String>;
     fname: String;
    Begin
     sa := TFile.ReadAllText('MyList.lst').Split([#0]);
     For fname In sa Do
      // Do something with the file in fname
    End;

    and then using any FTP client component you issue a download request.

     

    Edit:

     

    It does.

    Var
     sa: TArray<String>;
     fname: String;
    Begin
     sa := String('Hello' + #0 + 'World' + #0 + 'Zero char' + #0 + 'separation').Split([#0]);
     For fname In sa Do
      ShowMessage(fname);
    End;

    works, popping up one segment at a time; so it should do the trick.


  10. By the definition from MSDN (https://docs.microsoft.com/en-us/windows/win32/api/exdisp/nn-exdisp-ishellwindows)

     

    "IShellWindows interface

    Provides access to the collection of open Shell windows."

    [...]

    "A Shell window is a window that has been registered by calling IShellWindows::Register or IShellWindows::RegisterPending."

     

    Which means that if I want to, I can register any application as a shell window and you will see it in your collection. Maybe this is what you mean "hidden" as it is an instance of a shell window, which is NOT an Explorer process...?

     

    Edit:

     

    I suppose you already read this:

    "If the type is VT_UI4, the value of index is interpreted as a member of ShellWindowTypeConstants"

    ...which leads you here:

     

    "SWC_EXPLORER An Windows Explorer (Explorer.exe) window.

    SWC_BROWSER An Internet Explorer (Iexplore.exe) browser window.

    SWC_3RDPARTY A non-Microsoft browser window.

    SWC_CALLBACK A creation callback window.

    SWC_DESKTOP Windows Vista and later. The Windows desktop."

     

    So I suppose you are handling these cases in your code...?

     


  11. Just now, Tntman said:

    You mean to upload delphi code on arduino or just send commands from delphi app that is on PC or mobile ?

    Sending commands only. I don't have any specific projects in mind; I just see the opportunities of a connection between a Delphi app and an Arduino.

    For the time being I'd just be interested in a hello-world type setup. Make an Arduino light an LED when you check a checbox on a Delphi form. I'll make my way from there, when I have anything specific in mind 🙂


  12. Nice! I also played around with Arduino boards (this is how I fixed my smoking Christmas tree lights on Christmas eve) and I love them a lot. I even minified a project to a single ATMega chip for extra efficiency.

    But there was one thing I was always wondered but never took the time to dig myself into... can I talk to this board with Delphi...?

     

    I don't want you to share mission critical parts, but can you give some hints on how you did it? Are you using standard Serial to USB and text commands? Or a custom interface?


  13. While we were working from home the office building decided that it's a good time to test the electric breakers. This resulted someone having to travel on-site and turning all PCs on one-by-one on the whole floor.

    I started to experiment; my plan is to write a simple service application which is discovering PCs in a pre-defined range and provides a web interface where you can log on with your domain credentials and choose which PC you would like to wake up with WOL. If I get the green light from management I think this is going to be our solution.

    I'll have to test though, if a WOL broadcast packet actually works out-of-subnet or not; or what configuration is required on the network devices as I have no personal experience with this.


  14. 1 hour ago, limelect said:

    I never said just one there can be many explorer.

    But my software chooses the first in list.

    And that is an issue, if you ask me. To be honest I lost track a long time ago, but for a while the Windows Taskbar and the Desktop was hosted by an instance of the same explorer.exe process what you launched when you clicked on "My computer"; maybe Windows is using it for even more undocumented things (like specific background processes; which would answer why they just come and go). If your application requires to interact with an Explorer window, you should let your users to choose which one. But then again - this is how I think.

    4 hours ago, limelect said:

    But a hidden process does not have any name just a number.

    Wrong. A process has multiple granted properties; like an executable NAME and a PID for example. All processes have that, having visible windows or not, being service applications or not, being launched under a svchost process or not. Depending on how you enumerate those, you get different properties available. I personally never used ShellWindows as I always went for EnumWindows or FindWindow what @Fr0sT.Brutal advised to you already. It's a bit more low level, but opens up tons of opportunities.

    2 hours ago, limelect said:

    Maybe you should download my software and see what it dose.

    There are a lot of people using it and it is quite useful for us.

    Just unzip and use. No installation. you can always delete.

    And this part; I must totally have to agree with @David Heffernan. If someone anonymous on the Internet would tell you "Download my app, it's useful, you always can delete"... would you trust him/her?

    At this point noone really understands why you are doing what you are doing so they are unable to give you alternatives. Without an understandable code snipplet (e.g. I have no damn clue what "Do not forget Dispatch := ShellWindows.Item(i); is nil" means and I think I'm not alone) or a clear goal set... it just sounds like an other million dollar idea what SW developers are usually getting.

    • Like 2

  15.  

    10 hours ago, Alexander Sviridenkov said:

    @aehimself 95% of real world HTMLs cannot be parsed by XML parser. Also you cannot correctly modify HTML without calculating CSS styles. F.e. in this case <p class="p1">This is text</p>   <p class="p1">This is text2</p>  spaces between p can be removed, but in other case <p class="p2">This is text</p>   <p class="p2">This is text2</p> they should be preserved.

    I quickly realized. It threw a nullpointer exception anyway because of embedded JavaScript. And XML does not have that 🙂

     

    9 hours ago, Fr0sT.Brutal said:

    Btw, what you need XML minification for? As far as I'm aware you can rarely see plain XML in the wild; most of formats suppose Zipped XML (docx, fb2.zip). DB engines often use compression internally as well, HTTP protocol allows deflate compression.

    Our overgrown legacy system is talking to an other, overgrown legacy system through a custom protocol, transporting the XMLs in a beautified form. Now the issue is, that sometimes these two systems start to get misaligned because either of a 3rd overgrown legacy system, a bug in their software or a bug in ours. So after fixing a bug these information packets should be re-processed or re-sent. And as the protocol was designed you always need a reference to an earlier packet as well... quite complicated, resulting us having to save all communication between our software instances and their system. Originally, someone decided to keep the XMLs in a BLOB field in a table. Unfortunately the code is so big and disorganized (20+ years old) that changing the saving scheme would need a re-writing half our application plus the web interface. We know it should be done but we also know that it never will be done as the clients do not pay for refactoring.

     

    The good thing in legacy systems are the gems what you can find. My favorite goes like this:

    Function GetUserID: Integer;
    Begin
     Case MenuID Of
      0: Result := -1;
      1: Result := -1;
      2: Result := -1;
      [...]
      100: Result := -1;
      Else Result := -1;
     End;
    End;

    ... and oooooh, those sweet comments 🙂 Too bad that most of them are in Hungarian, otherwise I'd have posted them on DevHumor.

    • Thanks 1

  16. 12 hours ago, Fr0sT.Brutal said:

    Word has never been a good HTML generator )) CSS styles in all pages it created were a nightmare.

    I know, just wanted bring a really negative example. Most of the CMS WYSIWYG editors are no exception though and since they love to use shared libraries I suppose webmail interfaces are guilty as well.

     

    12 hours ago, Fr0sT.Brutal said:

    Well, I tried the first online minifier Google has given me and the source of this very page. Initial 76217, minified 63707, "compression" 16%. On the one hand, that's more than nothing. On the other hand, that makes the page source unreadable, could cause errors (f.ex. in embedded scripts or styles), and finally saves only 13 kB.

    As I mentioned I mainly work with XML, which is similar (but not the same) as HTML; and in XML the results are surprising already. I’ll go ahead and call minifying a compression method (a fairly inefficient one, though) from now on.

    As with most compressions the output heavily depends on the input; but usually the larger the input, the better the ratio becomes. The purpose of a compression is not to make it readable, and if it causes errors it’s a bug in the code.

     

    All I’m saying is whether if it’s efficient or not - minifying is still viable as this is the only way the output is still accepted by the target software.

     

    Just out of curiosity I’ll run my XML minifyer on a HTML page and will post the results.


  17. On 4/3/2020 at 6:27 PM, Fr0sT.Brutal said:

    Will it worth the efforts?

    Oh, you would be surprised. The last WYSIWYG program that wrote clean HTML code was Front Page Express around 2000. My favorite was creating a simple page, saving it in Word 2003 as HTML and then manually trimming the waste out. I usually could save aywhere between 5 or 20 kilobytes, keeping the original design.

    Now imagine trimming the "useless" characters too, used only to make the code readable.

     

    We deal with XMLs mostly at work. As an example, a beautified XML is 687722 characters, the same minimized is 455528; that's about 33% "compression".

    beauty.PNG

    mini.PNG


  18. So after weeks of attempting to find the guilty code piece I found nothing. Every allocation had a release pair, memory usage was perfectly consistent for close to a month. This combined with the fact that only one machine was affected means that whatever happened, it was not caused by my code (and therefore generics and classes) and was only a coincidence.

    And I think I already know what happened.

     

    So thanks all for the tips; at least we know that (even if it's unsupported) D10.3 applications are running happily (and correctly) on Windows 2000 too 🙂

×