Create a new form in delphi radstudio 10.4 firemonkey - forms

I work on this app for android and windows with Delphi firemonkey. It was running fine before android 11. Having updated the cellphone, the app is getting force closed. Failing to resolve the issue with newest sdk as well as manipulating the manifest file, I had to migrate to rad studio 10.4.
The app contains a button with the following code to create a new form which used to run just fine in rad studio 10.3. But in rad studio 10.4, with pressing the button, nothing happens and the form is not shown.
I was wondering if there is a newer or efficient way to create and show the form.
Application.CreateForm(Tfrm01, frm01);
frm01.Parent:=frmMAIN;
TRY
{$IFDEF ANDROID}
frm01.Show;
{$ENDIF}
{$IFDEF MSWINDOWS}
frm01.ShowModal;
{$ENDIF}
FINALLY
{$IFDEF ANDROID}
FreeAndNil(frm01);
{$ENDIF}
{$IFDEF MSWINDOWS}
frm01.Free;
{$ENDIF}
END;
By the way, I have also tried the following code to create the new form with the same results:
frm01 := Tfrm01.Create(self);

TForm.Show() is not modal, it exits immediately, so you are Free()'ing the Form before it can be displayed onscreen. Use the TForm.ShowModal() method on both platforms, the overloaded version that takes a ResultProc as a parameter should work on Android. To free such a modal TForm when it closes, use its OnClose event, setting the Action parameter to caFree. This is all explained in the documentation:
Using FireMonkey Modal Dialog Boxes
For example:
procedure Tfrm01.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := TCloseAction.caFree;
end;
frm01 := Tfrm01.Create(Application);
frm01.Parent := frmMAIN;
frm01.ShowModal(
procedure(ModalResult: TModalResult)
begin
// do something...
end
);

Related

Using the Plantronics SDK with a Delphi application

and thank you in advance for any advice / insight / assistance that can be provided.
The Background:
We have a soft phone application that is written in Delphi (XE3) for Windows. The ability to answer an incoming call by activating the answer button an a Plantronics Wireless Headset was recently requested. The MSI files were downloaded and executed, and the Plantronics SDK was converted / altered into a Delphi Library File.
I then started to follow the "First Steps" section of the Plantronics Website. I knew that the "First Steps" code would have to be tweaked to fit the Delphi system.
The Problem:
In adjusting the code to work within Delphi, a few translation problems were encountered and resolved. One such error is proving to be stubborn - When an instance of one specific class is created, the error "Class not registered" is thrown.
"First Steps" code for reference -
// Connect to the Plantronics COM API:
myAppName = "SDK .NET COM sample";
sessionManager = new COMSessionManager();
sessionManager.Register(myAppName, out session);
// Hook to SessionManager events:
sessionManagerEvents = sessionManager as ICOMSessionManagerEvents_Event;
if (sessionManagerEvents != null)
{
sessionManagerEvents.onCallStateChanged += SessionManagerEvents_onCallStateChanged;
sessionManagerEvents.onDeviceStateChanged += SessionManagerEvents_onDeviceStateChanged;
}
Delphi Code:
//Connect to the Plantronics COM API:
plugin_name: "Plugin Name";
the_session: CoCOMSession.Create;
session_manager = new COMSessionManager.Create;
session_manager.Register(plugin_name, the_session);
//Hook to Session Manager Events
state_device_event_args := CoCOMStateDeviceEventArgs.Create;
call_event_args := CoCOMCallEventArgs.Create;
The final line of Delphi Code is the issue. The other three "Create" calls go off without a hitch. The line "call_event_args := CoCOMCallEventArgs.Create;" throws the error "Class not registered", even through it is declared and implemented in the library file along with the other three.
Excerpts from the library file:
Class Declarations:
IID_ICOMStateDeviceEventArgs: TGUID = '{91542BEE-4931-4620-9E96-23AE4001E93F}';
CLASS_COMStateDeviceEventArgs: TGUID = '{335D08FD-8BB5-4EF5-964B-E8A8C010530F}';
IID_ICOMCallEventArgs: TGUID = '{0280956C-C644-4CD8-B124-C8A99E5D505E}';
CLASS_COMCallEventArgs: TGUID = '{705129C3-2265-4F10-9768-0FF8A20234C0}';
Class creation functions:
//Works
class function CoCOMStateDeviceEventArgs.Create: ICOMStateDeviceEventArgs;
begin
Result := CreateComObject(CLASS_COMStateDeviceEventArgs) as ICOMStateDeviceEventArgs;
end;
// Doesn't Work
class function CoCOMCallEventArgs.Create: ICOMCallEventArgs;
begin
Result := CreateComObject(CLASS_COMCallEventArgs) as ICOMCallEventArgs;
end;
Every tutorial / forum answer about resolving the "Class Not Registered" error that I have found has not resolved the issue.
Does anyone have any advice or insight as to what I have been doing wrong?
Thank you.
The issue has been (potentially) resolved by the following -
I had mistakenly thought from the tutorial / first steps section that a "COMCallEventArgs" object needed to be created before it could be used.
Upon further review, the COMCallEventArgs object gets created when necessary at a later point.
More testing needs to be done, but I believe this issue is resolved.

Setting FormStyle as MDIChild without showing it [duplicate]

How can I hide a MDIChild window in Delphi?
I'm using this code in FormClose() event of my MDI children, but it doesn't seem to work:
procedure TfrmInstrument.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caNone;
ShowWindow(Handle, SW_HIDE);
frmMainForm.MDIChildClosed(Handle);
end;
My child window is minimized instead of being hidden.
There is a protected procedure in TCustomForm defined as:
procedure TCustomForm.VisibleChanging;
begin
if (FormStyle = fsMDIChild) and Visible and (Parent = nil) then
raise EInvalidOperation.Create(SMDIChildNotVisible);
end;
Override it in your MDI child windows as:
procedure TMDIChildForm.VisibleChanging;
begin
// :-P
end;
Here's a simple example
After reading Jeroen's comment, I tried another solution which works as well, but with a little flickering:
procedure TMDIChildForm.VisibleChanging;
begin
if Visible then
FormStyle := fsNormal
else
FormStyle := fsMDIChild;
end;
Maybe this works on all Windows versions.
PS: I didn't find any problem with the first solution on Windows 2k3SP2 x86 and Windows 7 Ultimate x86
You cannot hide an MDI child window. This is a Win32 limitation.

How to close (in code) a Form which the IDE has open, without closing its associated .Pas file

The code below is simplified from something I'm doing in a design-time .BPL for D7.
Update: Since posting this, I've found one way to do what I'm after, namely just send the form a WM_Close message, but I'd still be interested to know whether there's a more "official" way to do it, because using WM_Close seems like it has the potential for wrong-footing the IDE.
All I'm trying to do in this code that's causing me the problem is to close all files open in the IDE,
and then open a specific .Pas file that happens to have an associated .Dfm file. I don't want the form defined in the .Dfm to be open on-screen, so I'm trying to close it, without closing the .Pas file too - I just want the IDE Form designer and this form out of the way.
Eventually, I found out how to get at the form via OTA + NTA services in my .BPL's code and, naively but for want of any other obvious way to do it, I've tried calling .Close on it, by this snippet.
AForm := TForm(INTAComp.GetComponent);
AForm.Close;
However, the form does not close. I've traced into TCustomForm.Close from the CPU window
and evidently the reason it doesn't close is that its Visible property is already False. This is what evaluating Visible prior to AForm.Close returns, too.
Evaluation various other of its properties prior to AForm.Close tells me that
- its Owner is Nil BUT
- it has an apparently valid window handle // Arrghh! [sound of penny dropping... see update above]
I dare say that this is something to do with how the IDE's form designer works.
My question is simply: What do I need to do in my code to get the form to close,
as it does when I simply click its [x] button on its frame?
Btw, I've confirmed that the instance of the form I'm getting via AForm := [...] is the
instance that's on-screen, by changing on-screen instance's caption in the OI.
procedure TOTAForm.CloseAForm;
var
IServices : IOTAServices;
IActionServices : IOTAActionServices;
IModuleServices : IOTAModuleServices;
IEditorServices : IOTAEditorServices60;
IModule : IOTAModule;
i : Integer;
IEditor : IOTAEditor;
ISourceEditor : IOTASourceEditor;
IFormEditor : IOTAFormEditor;
IComponent : IOTAComponent;
INTAComp : INTAComponent;
AForm : TForm;
begin
IServices := BorlandIDEServices as IOTAServices;
IServices.QueryInterface(IOTAACtionServices, IActionServices);
if IActionServices <> Nil then begin
IServices.QueryInterface(IOTAModuleServices, IModuleServices);
IModuleServices.CloseAll;
if IActionServices.OpenFile(EditorFileName) then begin
IModule := IModuleServices.Modules[0];
ISourceEditor := Nil;
for i := 0 to IModule.ModuleFileCount - 1 do begin
IEditor := IModule.ModuleFileEditors[i];
IEditor.QueryInterface(IOTAFormEditor, IFormEditor);
if IFormEditor <> Nil then begin
IComponent := IFormEditor.GetRootComponent;
IComponent.QueryInterface(INTAComponent, INTAComp);
AForm := TForm(INTAComp.GetComponent);
AForm.Close;
end;
end;
end;
end;
end;
All it needed was:
AForm := TForm(INTAComp.GetComponent);
SendMessage(AForm.Handle, WM_Close, 0, 0);
//AForm.Close;
But I'd still be interested in knowing whether there is an official way to do this, because
my "solution" feels like it's doing an end-run around OTA and NTA services. Otoh, the user can always close the form manually, on-screen, so maybe I'm worrying about nothing

Accesing Values From One Form on Another

Im trying to build in a language switch into one of my programs.
Where the user selects a language and in runtime the app gets translated. I sort of got this working in a small test project. BUT only when the forms are auto created, which i dont want.
the way the forms get created is the following:
SideNote: Most of my forms are fsMDIChild forms.
ParametersForm := TParametersForm.Create(Self); //(consider this the mainform for now)
On ParametersForm I have
procedure TParametersForm.FormCreate(Sender: TObject);
begin
ResourceStringsDM.ParametersF; //(consider this the second form)
end;
this Datamodule houses the captions for ParametersForm.
procedure TResourceStringsDM.ParametersF;
begin
with ParametersForm do
begin
bsSkinLabel1.Caption := 'Execute Nieuwefacturen';
bsSkinLabel2.Caption := 'Execute Viewfacturen';
end;
end;
I have added ResourceStringsDM to the implementation uses of ParametersForm and ParametersForm to the interface uses of ResourceStringsDM.
This all above gives me an access violation cause in the procedure where it sets the captions uses ParametersForm (var name of the form i want to translate) but at that moment this var is nil. Prolly cause it isnt done creating the form yet and hasnt filled in the form var.
The only way i got this all working was by using
Application.CreateForm(TParametersForm, ParametersForm);
But I want to avoid this after reading about it, and that you should only use this on your mainform. Also it doesnt handle passing parameters very well.
Does anyone of you fine people have any hints or tips / help to get me access to the captions of form 1 from form 2?
i probably forgot tons of info you guys need. just tell me and ill add it in.
In an "MDI Application" template (file->new-other->...>, neither MDI childs are auto created nor the child form's unit contains the global form reference. There's a reason for this, more than one instance of a child form should be able to run at the same time. When you have two instances of the same child for instance, which one will the form reference hold?
Anyway, it is of course possible to not to use this design, but then if you don't use the construct you mention in the question, you're responsible for assigning the instance to the reference yourself. So either do it (which I don't recommend):
procedure TParametersForm.FormCreate(Sender: TObject);
begin
ParametersForm := Self;
ResourceStringsDM.ParametersF; //(consider this the second form)
end;
or better, pass the instance to the function in your data module so that it can work on it:
procedure TResourceStringsDM.ParametersF(ParametersForm: TParametersForm);
begin
with ParametersForm do
begin
bsSkinLabel1.Caption := 'Execute Nieuwefacturen';
...
 
procedure TParametersForm.FormCreate(Sender: TObject);
begin
ResourceStringsDM.ParametersF(Self); //(consider this the second form)
end;

InnoSetup: Is it possible to open my custom Delphi form (from the DLL) instead of the standard setup wizard

I need to create a complex form with my own components (kinda OneClick installer), and use it as the replacement of the standard InnoSetup wizard. Is it possible?
My form is placed into DLL, and this DLL will be available for InnoSetup process.
This is how I tried to do that:
Delphi DLL code
library OneClickWizard;
uses
SysUtils,
Classes,
Wizard in 'Wizard.pas' {FormWizard};
{$R *.res}
exports
CreateWizardForm,
DestroyWizardForm;
begin
end.
Delphi form
unit Wizard;
interface
type
TFormWizard = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormWizard: TFormWizard;
procedure CreateWizardForm(AppHandle: THandle); stdcall;
procedure DestroyWizardForm; stdcall;
implementation
{$R *.dfm}
procedure CreateWizardForm(AppHandle: THandle);
begin
Application.Handle := AppHandle;
FormWizard := TFormWizard.Create(Application);
FormWizard.Show;
FormWizard.Refresh;
end;
procedure DestroyWizardForm;
begin
FormWizard.Free;
end;
InnoSetup script (iss)
[Setup]
;Disable all of the default wizard pages
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyMemo=true
DisableReadyPage=true
DisableStartupPrompt=true
DisableWelcomePage=true
DisableFinishedPage=true
[Files]
Source:"OneClickWizard.dll"; Flags: dontcopy
[Code]
procedure CreateWizardForm(AppHandle: Cardinal);
external 'CreateWizardForm#files:OneClickWizard.dll stdcall';
procedure DestroyWizardForm;
external 'DestroyWizardForm#files:OneClickWizard.dll stdcall';
procedure InitializeWizard();
begin
CreateWizardForm(MainForm.Handle);
end;
The form appearing on the screen, but it doesn't react on my input. Seems it is out of main message cycle. How to do this correctly?
In my setup I do something similar.
InnoSetup code I pass the handle as StrToInt(ExpandConstant('{wizardhwnd}')) (my guess is that MainForm.Handle is zero)
in the DLL:
OldAppHandle := Application.Handle;
try
Application.Handle := hAppHandle; // hAppHandle the handle from InnoSetup
F := TfmZForm.Create(Application);
try
F.Caption := lpTitle;
F.ShowModal;
Result := F.ErrorCode;
finally
F.Free;
end;
finally
Application.Handle := OldAppHandle;
end;
I know precisely nothing about InnoSetup but surely you need to use ShowModal rather than Show here. Installation UI is invariably modal and what you want here is to wait until the user has finished iteracting with the form before you return to Inno. Otherwise, how would Inno know when to proceed? ShowModal runs a message loop to service the form so there will be no problems receiving input.
You would also change your DLL to remove DestroyWizardForm since the function that calls ShowModal can both create and destroy the form.
If you want to entirely replace the UI, it will probably be easier to create a stub application that presents the form then runs the normal setup in silent mode passing various command line parameters.
Either that or at the very least, using Inno's native form and wizard page functions/logic.