Jump to content

Alexander Halser

Members
  • Content Count

    59
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by Alexander Halser


  1.  tested with polardark_win and polarlight_win styles

    I confirm that it works for me with the styles included in Delphi and those premium styles available for download when you have an active subscription. 

    That's why I asked the OP where the dark styles origins from. Maybe it is an older style?

     

    My argument is that if a simply outdated style compiles just fine and looks good on screen, but generates potential runtime issues that may cause problems I would have never expected and that are difficult to find, then we're in for future problems with FMX styles :classic_wacko:


  2. ChildForm is not released, StyleBookLight don't contain data

    Where do you see a problem here? Form2 is auto-created and auto-released, StyleBookLight is not required to produce the problem.

    I had some similar problem using an old 10.2 style

    That worries me ... what happens if a project that includes a TStylebook with a previously up-to-date style gets updated to a new Delphi version? Does that mean it compiles just fine but fails at runtime with mysterious errors? How does one test an app for potential problems like the one above? I'd like to know where it comes from before using FMX styles.


  3. Fascinating example... thanks a lot! I am currently experimenting with FMX styles and very much appreciate this.

     

    It's got something to do with the dark style - the style itself damages the runtime functionality. I can duplicate the issue with Delphi 11.0, both 32 and 64 bit, it is reproducible with the dark style (only). Switching back to the light style makes it work again.

     

    My first guess was that it has to do with TStyleBook. It is apparent that the second form does not pick up the style, so I thought it may have to do with this issue. But this is not the case.

     

    So I loaded my own dark skin into your dark stylebook.

    Guess what? Works like a charm, despite not applying the style to the second window.

    There is, that's for sure, some flaw in the dark style you are using. Fascinating this is possible at all... I did not think it was.

     

    Where does your dark style come from? 

     

    Untitled.png


  4. FMX doesn't auto-capture the control the mouse went down on. But you can do this manually, either by TForm.MouseCapture (that's for the form itself) or with TForm.SetCaptured(AnyControl) - this is for a particular control on the form.

    procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    begin
      if (Sender <> self) then
        SetCaptured(TControl(Sender))
      else
        MouseCapture;
    
      memo1.lines.add(Sender.Classname + ' mouse down');
    end;
    
    procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    begin
      ReleaseCapture;
    
      memo1.lines.add(Sender.Classname + ' mouse up');
    end;

     

    Setting the mouse capture is important if you do anything in MouseDown that interacts with the control being clicked. Without capture, the user may click (mouse-down) on Panel1, move it, and release it on Panel2. The MouseUp will come from Panel2 in this case, or no mouse-up at all. By setting the mouse capture, you are guaranteed that you'll receive a MouseUp from that control that is being captured. VCL does this automatically, FMX does not.

     

    If you don't need the MouseDown on form level, but just for particular controls, use TControl.AutoCapture instead.

    • Thanks 1

  5. For a new FMX app (Win/Mac only), I need some UI controls that go beyond the simple features that the Delphi built-in controls have to offer. One of them is a color picker component. There are very few color picker tools available for FMX and creating a new one turned out to be more work than anticipated. That's why I put mine on SourceForge. If you require a similar control, feel free to use it as you please.

     

    Delphi FMX Color Selector download | SourceForge.net

     

    I hope to get some feedback on the component as well, because it currently has 2 flaws (explained below).

     

    To use the color picker, reference the unit and call one of the two global methods ColorDropdown and ColorButtonDropdown. The first method is generic and can be used with any control, the second expects a standard TColorButton. I have implemented the component in a TForm that is dynamically created at runtime. No component installation required. This was an important point [for me], because I need a few more very specialized popup controls that are supposed to be placed in this form. Using a simple TForm makes it much easier to design them than doing everything in code.

     

    Current issues:

    One of the obstacles I came across was a drawing bug in TGrid, at least with Delphi 11.0 (maybe fixed in later versions). The grid is/was designed to fit exactly 10 rows and on the first display, it properly shows all 10 rows. Close and reopen the popup control, and the original 10 rows have shrunken to 9 visible rows. I fixed it by making the grid tall enough for 11 rows and filled the whitespace below by moving the opacity slider up. Looks fine and works so far, but if someone has an idea how to fix the visible rows in TGrid, I would appreciate a feedback. 

    The second issue is the color picker. It's implemented as a picture that you can drag & drop anywhere on the parent form to pick a color. This method is safe, but not 100% intuitive. A click-button-then-select-color function would be nicer. So, if anyone wants to spend time on that, I appreciate it.

     

    Apart from that, have fun trying it - demo application is included.

     

    delphi-fmx-color-dialog1.png

    delphi-fmx-color-dialog2.png

    • Like 1

  6. Quote

    No, it doesn't.

    I was about to suggest to create a custom inplace editor. But the form that contains the TStringGrid does get KeyDown notifications, even when editing content. Out of curiosity, I wanted to know the internal structure of the grid, when it's editing.

     

    procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
      Shift: TShiftState);
    var
      ctl: tfmxobject;
      s: string;
    begin
      if key = vkF5 then
      begin
        ctl := activecontrol;
        while ctl <> form2 do
        begin
          s := '> ' +ctl.classname + S;
          ctl := ctl.Parent;
        end;
        showmessage(s);
      end;
    end;

    It says:

     

    TStringGrid > TGridScrollContent > TDefaultEditor

     

    So if you check the OnKeyDown on form level and detect that ActiveControl is a TDefaultEditor with a TStringGrid as grandparent, you should be all set. Aren't you?


  7. Si Señor! Winapi.GDIOBJ instead of FMX.Graphics does the trick. And saves a few megabytes.
    Thank you very much for the hint, Uwe!
     
    To sum up the solution...
     
    If an FMX DLL requires drawing capabilities in the initialization (that requirement might be triggered by 3rd party units), then the VCL host application must load GDI+ before loading the DLL:
    1. In the VCL host app, include Winapi.GDIOBJ to init GDI+
    2. Load the FMX DLL dynamically (static linking would cause the DLL to load first, before host can load GDI+)

     

    If the host app is FMX and not VCL, it loads GDI+ by default. In this case, point (1) is void, but dynamic loading of the DLL is still required.


  8. Update, a partial solution, and a question...

     

    Observations with an FMX host app + FMX dll:

    • The FMX DLL requires GDI+/DirectX in the intialization section. It creates an FMX.TBitmap and loads an image. To do this, it requires a GDI+/DirectX canvas.
    • Such a DLL cannot be statically linked, fullstop. Not even with an FMX host application, despite the FMX host does initialize GDI+/DirectX.
    • The reason is that statically linked DLLs get loaded before the host app can initialize GDI+/DirectX.
    • Solution for FMX host + FMX dll: link the DLL dynamically, let the host app initialize GDI+/DirectX (as it does by default), then load the DLL.
    • Result: the initialization of the DLL works, because GDI+/DirectX is already available (loaded by the FMX host).

    Observations with a VCL host app + FMX dll:

    • It's obvious that GDI+/DirectX must be initialized by the host app before the DLL gets loaded.
    • Dynamic DLL loading is therefore a must.
    • The VCL host does not - at least not by default - initialize GDI+/DirectX, because it does not require it.
    • Question: can we force the VCL host to deliberately load GDI+/DirectX, which is required by the DLL?
    • Answer: yes, we can by including FMX.Graphics into the VCL section. Technically, that works (and the compiler does not complain).

     

    VCL host app test unit, modified:

    Note, that the VCL app references FMX.Graphics. This adds about 7mb to the EXE and seems to work.

    Is this a reliable construct?

    Or is there a different way to invoke (just) GDI+/DirectX in the host app, without all the FMX.Graphics overhead?

     

    unit VCL_hostUnit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
      FMX.Graphics;  { <--- This works and intializes GDI+/DirectX for the DLL. But will this work reliably? }
    
    type
      TVCLForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
      TLoadImage = function(Filename: PChar): Boolean;
    
    var
      VCLForm1: TVCLForm1;
      dllHandle: Cardinal;
      LoadImageFunc: TLoadImage;
    
    implementation
    
    {$R *.dfm}
    
    
    function LoadDLL: Boolean;
    begin
      dllHandle := LoadLibrary('TestDLL.dll') ;
      @LoadImageFunc := GetProcAddress(dllHandle, 'LoadImage') ;
    end;
    
    procedure TVCLForm1.Button1Click(Sender: TObject);
    begin
      if LoadDLL then
        LoadImageFunc('C:\Users\Alexander\Documents\Embarcadero\Studio\Projects\fmx-dll-test\test.png')
      else
        Showmessage('DLL not loaded');
    end;
    
    initialization
      dllHandle := 0;
    
    finalization
      if dllHandle <> 0 then FreeLibrary(dllHandle) ;
    
    end.

     


  9. Hello programmerdelphi2k!

    I should have been more precise: 

    • Delphi 11.0
    • Win32 (not 64 bit)
    • The DLL is 100% FMX, so TBitmap is an FMX.Graphics.TBitmap
    • The host app is 100% VCL

    Host and DLL do not exchange bitmaps, nor other complex data structures. They exchange PChars and function results.

    When the host app starts and the DLL loads, I get the message "Load DLL" and after that: "Exception EAccessViolation in Module TestDLL.dll at 0000842F."

     

    Host app (VCL 32 bit, 1 form):

    unit hostUnit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
      function LoadImage(Filename: PChar): Boolean; external 'TestDLL.dll';
    
    var
      Form1: TForm1;
    
    implementation
    {$R *.dfm}
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      LoadImage('C:\Users\Alexander\Documents\Embarcadero\Studio\Projects\fmx-dll-test\test.png');
    end;
    
    end.

    DLL Code:

    library TestDLL;
    uses
      FMX.Forms,
      FMX.Graphics,
      FMX.Dialogs;
    
    {$R *.res}
    
    function LoadImage(Filename: PChar): Boolean;
    var
      dummy: FMX.Graphics.TBitmap;
    begin
      dummy := FMX.Graphics.TBitmap.Create;
      dummy.LoadFromFile(Filename);
      dummy.Free;
      Showmessage('Image loaded');
    end;
    
    exports
      LoadImage;
    
    var
      dummy: FMX.Graphics.TBitmap;
    begin
      Showmessage('Load DLL');
      dummy := FMX.Graphics.TBitmap.Create;
    //This line included, the DLL fails to load:
    //  dummy.LoadFromFile('C:\Users\Alexander\Documents\Embarcadero\Studio\Projects\fmx-dll-test\test.png');  
      dummy.Free;
    end.

     

    HostApp.zip


  10. I need some FMX functionality in a VCL app. An FMX Windows DLL should handle the task. So far, the (FMX) DLL is working, but... 
     
    Loading a TBitmap in the initialization of the DLL fails. The host application (actually the DLL) comes up with a general error. Loading the TBitmap at a later point in time (e.g. triggered by the host app) works. Unfortunately, the real DLL is a bit more complex and stripping all the "initialization" from all units is not an option.

    Static vs. dynamic loading of the DLL by the host VCL app does not make a difference. I've also tried ShareMem (not convinced it would change anything), but to give it a try: same result.

    Stripped down to the core, the entire DLL code is in the screenshot below.

    To me, it looks as if the GDI+ or DirectX environment is not loaded, when the DLL initializes. Is this a general limitation or could that be worked around by forcing earlier loading of GDI+/DirectX?

    fmx-dll-load-bitmap-in-initialization.png


  11. @Uwe Raabe & Günter: Thank you, that does the trick! This saves me a lot of code-rewriting 🙂

     

     

    function SortShapesByWidth(const item1, item2: TShape): Integer;
    begin
      result := item1.width - item2.width;
    end;
    
    procedure TForm2.BtnSortClick(Sender: TObject);
    var
      AList: TList;
      i: Integer;
    begin
      AList := TList.Create;
      try
        for i := 0 to ComponentCount-1 do
          if (Components[i] is TShape) then
            AList.Add(Components[i]);
    
        AList.Sort(@SortShapesByWidth);
    
        for i := 0 to AList.Count-1 do
          TShape(AList[i]).Top := 30 + (40 * i);
      finally
        AList.Free;
      end;
    end;


     


  12. I am currently working on some legacy code that uses simple TLists with object references and custom sort. 

    This has worked and still works perfectly in 32 bit. But in 64 bit output, the list is not sorted (correctly), although it compiles without any errors.

     

    The form contains a couple of TShapes with different widths. The code below is supposed to sort and rearrange them by width.

    In 32 bit, the sort is ok. In 64 bit it's random.

    In fact, the function SortByWidth is called with item1 and item2 being the same (here: pointing to the same TShape).

     

    I am concerned and want to understand what's going on and why this does not work in 64 bit.

    There are more modern ways to sort a TList or use dictionaries. But this is legacy code that contains sorting functions of this kind in several places. 

     

    procedure TForm2.BtnSortClick(Sender: TObject);
    
      function SortByWidth(const item1, item2: TShape): Integer;
      begin
        result := item1.width - item2.width;
      end;
    
    var
      AList: TList;
      i: Integer;
    begin
      AList := TList.Create;
      try
        for i := 0 to ComponentCount-1 do
          if (Components[i] is TShape) then
            AList.Add(Components[i]);
    
        AList.Sort(@SortByWidth);   //what is wrong about this in 64 bit?
    
        for i := 0 to AList.Count-1 do
          TShape(AList[i]).Top := 30 + (40 * i);
      finally
        AList.Free;
      end;
    end;


     

    compare32.png

    compare64.png


  13. I am afraid I cannot help much. As described in the referenced thread... (I haven't changed this configuration since it got it working)

     

    iOS 15.1 - FMX - Delphi-PRAXiS [en] (delphipraxis.net)

     

    Reformatted the Mac, installed maOS Big Sur, an older version of Xcode. Things now work as advertised with Delphi 11.

    Delphi 11 plus November and January patch
    Hardware: M1 (2020) Mac running macOS Big Sur
    Xcode 13.2

     


  14. Thank you for all your answers! I have used memory mapped files in the past, which works just fine in many situations, so this is definitely among the options. I'm also going to explore pipes, which I am not very familiar. 

     

    Quote

    "logic" part in a separate console application, why don't use its stdin/stdout (which are already pipes)?

    Interesting idea! Console output is of course the most simple solution, but I did not think of console input.

    I like this idea because of its simplicity.

     

    Because back in my mind (not yet on the map) is the idea to make the execution part platform-independent from the beginning. If that requires a completely separate GUI part for MacOS/Linux (no plans for iOS/Android here) or can be done in one FMX app, is another story.


  15. I've taken over a legacy project from a friend, that needs a structural redesign. The application basically does file analysis and modification. It is currently a GUI app with everything built into the main executable. I would like to split that and am thinking of multiple EXEs. One that resembles the GUI, a console application that executes one particular task. On top of that, it needs a Windows service to execute scheduled tasks automatically, if no user is logged in.

     

    What is the best practice to accomplish this?

    Bi-directional communication is a must and could be done with custom messages and console output.

    Any other thoughts? Plugin system via DLL? Runtime packages?

     

    My thoughts about the general design so far:

    • EXE1 (the GUI app)
    • EXE2 (the non-GUI Windows service)
    • EXE3 (the console app that executes tasks, can be used stand-alone in console mode and must run in multiple instances)

     

    EXE1 and EXE2 would both call the console EXE3. However, the tasks being executed can be quite long (from minutes to hours), so bi-directional communication is required. And while a task is being executed, it is not feasible to simply kill the thread in order to stop it. EXE3 must complete or roll back the detail, it's currently working on.

     

    Furthermore, EXEs should run without installation (except for the Windows service, that is). If EXE3 was, for instance, a COM server, this wouldn't work, would it? We could of course build everything into every EXE and separate the functionality in the code design. But I don't like the idea in general and multi-threaded execution is easier to handle with separate processes - in particular if the GUI app and the Windows service run at the same time, but must not execute the same task twice.

    • Like 1

  16. Quote

    Just delete the extra ones.

    Thanks, Doug! Please tell me how to do that, and I'll be eternally grateful.

     

    Apple documentation says, that in order to revoke a certificate, you must contact product-security@apple.com. They find themselves not responsible and have redirected me to Apple Developer support. But support doesn't feel responsible either and suggested to contact product-security@apple.com. That email conversion has been going back and forth for more than a week now. No result yet.

    Maybe I should contact Embarcadero and ask them to build a new version of Delphi, that handles ambiguous certificates better. Or does it already? If Delphi did not specify the developer ID certificate by name, I could just use the existing one(s) and don't care about duplicates.


  17. I'm trying to get my MacOS app notarized and have applied for an Apple Developer ID certificate, which has been issued, downloaded, etc.

    Well, in the process of trying to get this thing to work I'm now completely stuck.

     

    Delphi tells me that when deploying the app, there are ambiguous certificate matches.

    Indeed there are: Xcode shows me that there are 2 Developer ID certificates for my company. When logging in to developer.apple.com, there are even three... Please don't ask me how I managed to add them (I don't remember what I did all day today in order to get it working).

     

    Question: How do I get rid of the duplicate certificates? There's no "Remove" button anywhere.

     

    apple-certificates-xcode.png

    apple-certificates-website.png


  18. On MacOS when using a TMainMenu that integrates into the Apple system menu bar, sub-menus automatically close the entire menu when clicked, as illustrated in the screenshot.

     

    My configuration:

    Delphi 11
    MacOS Big Sur

    Before I get into this really deep, could someone please confirm (or not) that this is a Delphi bug?

    Ideally with Delphi 11 plus an older version.

     

    Steps to reproduce the issue:

    1. Create a new project
    2. Drop a TMainMenu on the form
    3. Add several menu items with subitems
    4. No onclick handlers required, just go and run on MacOS

     

    This bug occurs on the native Apple menu only. The bug occurs with all menu items that represent a submenu.

    There's no such problem with TPopupMenu and there's no problem on Windows with TMainMenu at all.

    Screenshot 2022-07-03 at 14.26.33.png

    • Like 1

  19. Update:

    Reformatted the Mac, installed maOS Big Sur, an older version of Xcode. Things now work as advertised with Delphi 11. But I am wondering what the underlying problem is and if anyone has managed to make this configuration work. The Mac is an M1 (2020), not an Intel machine.

     

    This configuration works:

    Delphi 11 plus November and January patch
    Hardware: M1 (2020) Mac running macOS Big Sur
    Xcode 13.2

     

    Not working configuration:

    Delphi 11 plus November and January patch
    Hardware: M1 (2020) Mac running macOS Monterey
    Xcode 12.5.1 / 13.0 / 13.2 / 13.4.1
    • Like 1

  20. Following...
    I have the same problem with macOS and macOS ARM.

     

    Current config:

    Delphi 11 plus November and January patch
    MacOS Monterey 12.4
    Xcode 13.4.1 (replace with 12.5.1, 13.0, you name it, all combinations tried) 

     


  21. The heading is not a typo.
    I've taken over Mac development from a colleague who recently left, so Delphi/Mac is pretty new to me. I'm running Delphi 10.4 Seattle with all patches and have a new Macbook running Monterey with Xcode 12.5.1. My basic "hello world" test app compiles and deploys successfully. But it doesn't start on the Mac. When started manually, it flickers for a moment, then terminates.

     

    PAServer reveals the underlying problem:

    dyld[xxxx]: Library not loaded: ?^???
    Referenced from: /Users/alexander/PAServer/scratch-dir/Alexander-MacProfile/MacTest.app/Contents/MacOS/MacTest
    Reason: tried: '?^???' (no such file)

    The app obviously has a reference to "?^???" which is not found.

    But where does this come from?

     

    My current configuration:

    Delphi 10.4 with all patches
    MacOS Monterey with Xcode 12.5.1 (after realizing that Xcode 13 is not compatible I managed to install and run 12.5.1)
    Delphi SDK Manager: MacOSX 11.3

    The app compiles and deploys successfully, both in Release and Debug mode. It just doesn't run.

    I'm pretty much stuck here...  Google delivers plenty of results for "library not loaded" but none of them deals with a string called "?^???".

     

    PS:

    When changing the encoding of the (very simply) main unit from ANSI to UTF8, this slightly affects the error message.

    The UTF8 variant looks for a library called "?^;?[" instead of "?^???". Any hints where to look for this?

×