Jump to content

Remy Lebeau

Members
  • Content Count

    2985
  • Joined

  • Last visited

  • Days Won

    135

Everything posted by Remy Lebeau

  1. That does not happen in my test project. Here is the full code I used: Main Form: unit MultiFormUnit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); {$IFDEF MSWINDOWS} protected procedure CreateHandle; override; {$ENDIF} private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation uses MultiFormUnit2 {$IFDEF MSWINDOWS} , Winapi.Windows , Winapi.Messages , Winapi.CommCtrl , FMX.Platform.Win {$ENDIF} ; {$R *.fmx} procedure TForm1.Button1Click(Sender: TObject); begin TForm2.Create(Self).Show; end; {$IFDEF MSWINDOWS} function SubclassProc(Wnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall; begin case uMsg of WM_NCDESTROY: begin RemoveWindowSubclass(Wnd, SubclassProc, uIdSubclass); end; WM_ACTIVATE: begin if wParam in [WA_ACTIVE, WA_CLICKACTIVE] then begin SetFocus(Wnd); //SetForeGroundWindow(Wnd); //BringWindowToTop(Wnd); end; end; end; Result := DefSubclassProc(Wnd, uMsg, wParam, lParam); end; procedure TForm1.CreateHandle; var Wnd: HWND; ExStyle: LONG_PTR; begin inherited; Wnd := FormToHWND(Self); // a Taskbar button is already handled for the MainForm by FMX, don't force it here... //ExStyle := GetWindowLongPtr(Wnd, GWL_EXSTYLE); //SetWindowLong(Wnd, GWL_EXSTYLE, ExStyle or WS_EX_APPWINDOW); SetWindowSubclass(Wnd, SubclassProc, 1, DWORD_PTR(Self)); end; {$ENDIF} end. Child Form: unit MultiFormUnit2; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs; type TForm2 = class(TForm) procedure FormClose(Sender: TObject; var Action: TCloseAction); private {$IFDEF MSWINDOWS} protected procedure CreateHandle; override; {$ENDIF} { Private declarations } public { Public declarations } end; var Form2: TForm2; implementation {$R *.fmx} {$IFDEF MSWINDOWS} uses Winapi.Windows, Winapi.Messages, Winapi.CommCtrl, FMX.Platform.Win; function SubclassProc(Wnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall; begin case uMsg of WM_NCDESTROY: begin RemoveWindowSubclass(Wnd, SubclassProc, uIdSubclass); end; WM_ACTIVATE: begin if wParam in [WA_ACTIVE, WA_CLICKACTIVE] then begin SetFocus(Wnd); //SetForeGroundWindow(Wnd); //BringWindowToTop(Wnd); end; end; end; Result := DefSubclassProc(Wnd, uMsg, wParam, lParam); end; procedure TForm2.CreateHandle; var Wnd: HWND; ExStyle: LONG_PTR; begin inherited; Wnd := FormToHWND(Self); ExStyle := GetWindowLongPtr(Wnd, GWL_EXSTYLE); SetWindowLong(Wnd, GWL_EXSTYLE, ExStyle or WS_EX_APPWINDOW); SetWindowSubclass(Wnd, SubclassProc, 1, DWORD_PTR(Self)); end; {$ENDIF} procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := TCloseAction.caFree; end; end. Then you must be doing something wrong in how you setup your subclassing. A subclass is per-window. The only way the child form subclass would be called for the main form is if you registered the child form subclass for the main form window by mistake. That is not how it works. The MainFormOnTaskbar property controls whether the Taskbar button belongs to the hidden TApplication window or the TApplication.MainForm window. If TApplication owns the button, then it delegates various actions to the MainForm. That is not necessary.
  2. I assure you that it does. I tested the code before I posted it. That will not work in this situation, as HandleMessage() can see only messages that are posted to the main message queue, it will not see messages that are sent directly to the Form window. FMX does not dispatch non-queued messages in any way that the app's code can override. Which is why I suggest using a manual window subclass instead. Correct, because those messages do not go through the message queue. WM_KEY... messages go through the message queue. FMX does handle a few non-queued messages, such as WM_ACTIVATE. But clearly not in a way that behaves as you want. SetWindowSubclass() does not use GetLastError() for reporting errors. The only reason I can think of for SetWindowSubclass() to fail is if you are not using ComCtrl32.dll v6, or it has not been loaded yet. Does your project have an app manifest that enables ComCtl32.dll v6? It should be enabled by default. Also, you may need to call Winapi.CommCtrl.InitCommonControlsEx() at app startup, as the Winapi.CommCtrl unit loads SetWindowSubclass() dynamically and only if ComCtrl32.dll has already already loaded by a previous Winapi.CommCtrl call (specifically, it is loaded only by InitCommonControlsEx(), TaskDialogIndirect(), or TaskDialog()).
  3. Remy Lebeau

    Documentation links on indyproject.org not working

    I didn't create the zip file, so I can't help with that error. If someone wants to fix the zip, I'll replace it. The only link that is not working for me right now is the 10.1.5 doc archive. I have no control over other people's sites.
  4. Remy Lebeau

    Documentation links on indyproject.org not working

    That link work fine for me.
  5. Remy Lebeau

    Disable all controls on panel

    Indy has a TIdAntiFreeze component that processes pending UI messages during blocking socket operations. This would allow the WaitFor() call to process messages while waiting for the data to arrive. Though, that wouldn't address the case where the button click occurs after the last WaitFor() call had already exited but the panel hasn't been enabled yet.
  6. Remy Lebeau

    Documentation links on indyproject.org not working

    Also see: https://www.indyproject.org/2021/02/10/links-to-old-indy-website-pages-are-currently-broken/ I never had free time to fix it.
  7. Unlike VCL, FMX hides Win32 window messages from you. And, it ignores most window messages in general, it only handles a handful of select messages for its own purpose. Since you are forcing the Taskbar button to appear on a sub-Form, you will have to manually subclass the Form's window so you can handle window messages like WM_ACTIVATE, eg: type TSubToolBarForm = class(TForm) private ... {$IFDEF MSWINDOWS} protected procedure CreateHandle; override; {$ENDIF} ... end; ... {$IFDEF MSWINDOWS} uses Winapi.Windows, Winapi.Messages, Winapi.CommCtrl, FMX.Platform.Win; function SubToolBarSubclassProc(Wnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall; begin case uMsg of WM_NCDESTROY: begin RemoveWindowSubclass(Wnd, SubToolBarSubclassProc, uIdSubclass); end; WM_ACTIVATE: begin if wParam in [WA_ACTIVE, WA_CLICKACTIVE] then begin SetFocus(Wnd); //SetForeGroundWindow(Wnd); //BringWindowToTop(Wnd); end; end; end; Result := DefSubclassProc(Wnd, uMsg, wParam, lParam); end; procedure TSubToolBarForm.CreateHandle; var Wnd: HWND; ExStyle: LONG_PTR; begin inherited; Wnd := FormToHWND(Self); ExStyle := GetWindowLongPtr(Wnd, GWL_EXSTYLE); SetWindowLong(Wnd, GWL_EXSTYLE, ExStyle or WS_EX_APPWINDOW); SetWindowSubclass(Wnd, SubclassProc, 1, DWORD_PTR(Self)); end; {$ENDIF}
  8. By default, FMX does not create a separate Taskbar button for each Form. So, what are you doing manually to force this?
  9. No, they were merged into Indy's master code about a month after the release of 12.2. https://www.indyproject.org/2024/10/20/sasl-oauth-support-finally-added-to-master/
  10. Indy recently got a few new OAuth components that work with SMTP/POP3/IMAP4 SASL-based authentication. However, getting/refreshing a token is up to you to handle separately via each OAuth provider's API (via HTTP/REST, etc), but once you have a token then Indy can login with it. Geoffrey Smith's example predates those components, but he handles both REST and SASL portions. Indy doesn't have any components to handle the REST portion for you, but its TIdHTTP component can certain be used to send REST requests.
  11. Remy Lebeau

    REST Datasnap Server Accepting ECDH Ciphers

    It could happen if the app is running on Android 6+ where BoringSSL has replaced OpenSSL but might use the old OpenSSL .so filenames.
  12. Remy Lebeau

    Enable Discussions on github ?

    @Anders MelanderThanks I'll look into that. Indy has a lot of open issue tickets, I'm sure a good chunk of them could be discussions.
  13. Remy Lebeau

    REST Datasnap Server Accepting ECDH Ciphers

    Sorry I have no idea. I've never worked with Datasnap before and done know if/how it exposes access to configure Indy.
  14. Remy Lebeau

    Enable Discussions on github ?

    I went ahead and enabled Discussions on GitHub. We'll see how it works out. I'll still monitor this forum and others, of course.
  15. Remy Lebeau

    REST Datasnap Server Accepting ECDH Ciphers

    There are a few 3rd party GitHub repos that add OpenSSL 3.x/TLS 1.3 to Indy, including: https://github.com/MWASoftware/Indy.proposedUpdate https://github.com/JPeterMugaas/TaurusTLS
  16. When using VarSameValue(), you can get a conversion failure if the 2 Variants are not holding compatible types. This is even stated in the documentation: So, you should first make sure the 2 Variants have the same type before comparing their values. This is just logical to do anyway, since if you know they have different types then there is no point in wasting resources to compare the values. When I make these small changes to TTestComponent, the "Could not convert variant" error goes away: procedure TTestComponent.SetCheckedValue(const Value: Variant); begin if (VarType(FCheckedValue) <> VarType(Value)) or // <-- ADD THIS! (not VarSameValue(FCheckedValue, Value)) then begin FCheckedValue := Value; CheckedValueChanged(); end; end; function TTestComponent.IsCheckedValueStored: Boolean; begin Result := (VarType(FCheckedValue) <> varBoolean) or // <-- ADD THIS! (not VarSameValue(FCheckedValue, True)); end; Now, that just leaves the problem of the 'CheckedValue.Type' property displaying "Unknown" for string values. That is indeed a bug in the default Variant property editor, which I have now reported to Embarcadero: RSS-2844; TVariantTypeProperty is broken for string values You can easily work around the bug, either directly in your component's property setter: procedure TTestComponent.SetCheckedValue(const Value: Variant); begin if (VarType(FCheckedValue) <> VarType(Value)) or (not VarSameValue(FCheckedValue, Value)) then begin FCheckedValue := Value; if VarType(FCheckedValue) = varString then FCheckedValue := VarToStr(Value); // <-- change the VarType to varUString CheckedValueChanged(); end; end; Or by deriving a custom property editor in your component's design-time package (if you don't have one, make one) to fix the bug directly: uses Variants, DesignIntf, DesignEditors, DesignConst; //... { TMyVariantTypeProperty } // unfortunately, TVariantTypeProperty is hidden in the implementation // of the DesignEditors unit, so we have to copy the entire class just // to change a couple of lines! const VarTypeNames: array[varEmpty..varInt64] of string = ( 'Unassigned', // varEmpty 'Null', // varNull 'Smallint', // varSmallint 'Integer', // varInteger 'Single', // varSingle 'Double', // varDouble 'Currency', // varCurrency 'Date', // varDate 'OleStr', // varOleStr '', // varDispatch '', // varError 'Boolean', // varBoolean '', // varVariant '', // varUnknown '', // [varDecimal] '', // [undefined] 'Shortint', // varShortInt 'Byte', // varByte 'Word', // varWord 'LongWord', // varLongWord 'Int64'); // varInt64 type TMyVariantTypeProperty = class(TNestedProperty) public function AllEqual: Boolean; override; function GetAttributes: TPropertyAttributes; override; function GetName: string; override; function GetValue: string; override; procedure GetValues(Proc: TGetStrProc); override; procedure SetValue(const Value: string); override; end; function TMyVariantTypeProperty.AllEqual: Boolean; var i: Integer; V1, V2: Variant; begin Result := False; if PropCount > 1 then begin V1 := GetVarValue; for i := 1 to PropCount - 1 do begin V2 := GetVarValueAt(i); if VarType(V1) <> VarType(V2) then Exit; end; end; Result := True; end; function TMyVariantTypeProperty.GetAttributes: TPropertyAttributes; begin Result := [paMultiSelect, paValueList, paSortList]; end; function TMyVariantTypeProperty.GetName: string; begin Result := 'Type'; end; function TMyVariantTypeProperty.GetValue: string; begin case VarType(GetVarValue) and varTypeMask of Low(VarTypeNames)..High(VarTypeNames): Result := VarTypeNames[VarType(GetVarValue) and varTypeMask]; varString,varUString: // <-- FIX HERE! Result := SString; else Result := SUnknown; end; end; procedure TMyVariantTypeProperty.GetValues(Proc: TGetStrProc); var i: Integer; begin for i := 0 to High(VarTypeNames) do if VarTypeNames[i] <> '' then Proc(VarTypeNames[i]); Proc(SString); end; procedure TMyVariantTypeProperty.SetValue(const Value: string); function GetSelectedType: Integer; var i: Integer; begin Result := -1; for i := 0 to High(VarTypeNames) do if VarTypeNames[i] = Value then begin Result := i; break; end; if (Result = -1) and (Value = SString) then Result := varUString; // <-- FIX HERE! end; var NewType: Integer; V: Variant; begin NewType := GetSelectedType; case NewType of varEmpty: VarClear(V); varNull: V := NULL; -1: raise EDesignPropertyError.CreateRes(@SUnknownType); else V := GetVarValue; // <-- move here for good measure... try VarCast(V, V, NewType); except { If it cannot cast, clear it and then cast again. } VarClear(V); VarCast(V, V, NewType); end; end; SetVarValue(V); end; { TMyVariantProperty } // fortunately, TVariantProperty is public in the DesignEditors unit, // so we need to override only 1 method in it... type TMyVariantProperty = class(TVariantProperty) procedure GetProperties(Proc: TGetPropProc); override; end; procedure TMyVariantProperty.GetProperties(Proc: TGetPropProc); begin Proc(TMyVariantTypeProperty.Create(Self)); end; procedure Register; begin //... // change the 2nd and 3rd properties if you want to reuse this editor for all Variant properties, eg: // RegisterPropertyEditor(TypeInfo(Variant), nil, '', TMyVariantProperty); RegisterPropertyEditor(TypeInfo(Variant), TTestComponent, 'CheckedValue', TMyVariantProperty); end;
  17. Not directly by Close() itself, no. But after Close() sets the ModalResult to mrCancel, the modal loop will then trigger the OnClose event to see if the ModalResult needs to be updated. Right, because ModalResult has already been set to non-zero before CloseModal() is called. CloseModal() gets the final decision on what ModalResult is set to before the loop evaluates it for termination. It actually is. The OnClose event is triggered whether the user clicks on the X button or on the Close system menu, or the code calls Close() or sets the ModalResult.
  18. Remy Lebeau

    Could not load OpenSSL library.

    In theory, Embarcadero is supposed to be using a private copy of Indy for their technologies, like DataSnap, where the IPPeer... units are part of an abstraction layer that interacts with that copy. And then users are supposed to be able to freely update the public version of Indy for their own projects without affecting that private layer. But in practice, things don't always work out that way.
  19. Delphi has no concept of OneDrive. It is merely using your user profile's standard Documents folder to store your project files, 3rd party packages, etc. But OneDrive has taken over your Documents folder and is storing it remotely. That is not Delphi's fault. If you don't like this, then either configure OneDrive to leave your Documents folder alone, or else configure Delphi's environment settings to use different folders that OneDrive doesn't touch.
  20. Remy Lebeau

    Memory leak on parsing json

    Before freeing the TJSONObject, can you verify that all of its child objects are set to Owned=True? Also, do you have the same problem if you try with FASTMM5 instead of FASTMM4?
  21. Remy Lebeau

    Memory leak on parsing json

    That alone should not be creating memory leaks. Just having FastMM enabled should not be accounting for a 20MB increase in memory usage. And, although FastMM does cache freed memory for later reuse, which can appear as a leak from Task Manager's POV but really isn't, it should not be reported as a leak during process shutdown. Only real leaks (ie unfreed allocations) should be reported as leaks. Can you provide the actual leak report? And, do you have tracing enabled so the report shows details about the code that allocated the leaked memory?
  22. And vice versa. But doing so works safely and reliably only if you stick to certain rules to ensure compatibility across programming languages/ABIs, environments, etc. Which limits what you can and can't use across the DLL boundary.
  23. Indy's TIdHTTP component supports Digest, just make sure the IdAuthenticationDigest unit is in your uses clause (directly, or via the IdAllAuthentications unit) . And then you can set the TIdHTTP.Request.Username and TIdHTTP.Request.Password as needed.
  24. Remy Lebeau

    TListView - manually adding items faster at runtime

    Can you provide an small example/steps that reproduces this problem? How to use Checkboxes in TListView (OwnerData True) in Delphi
  25. Remy Lebeau

    TListView - manually adding items faster at runtime

    Are you custom drawing the TListView? Virtual mode affects only data storage, so it should not cause drawing issues if you are otherwise using default settings.
×