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

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

Related

How to declare local variable (Allen Bradley studio 5000)

How to declare local variable in structured Text in Allen Bradley studio 5000.
I have tried the following which did not work. mnemonic/syntax is not recognized?
VAR
x: INT;
END_VAR
global variables do work as, TEST_INPUT and TEST_INPUT_BOOL are globally declared.
IF TEST_INPUT.0 THEN
TEST_INPUT_BOOL := 1;
ELSE
TEST_INPUT_BOOL := 0;
END_IF;
In Allen-Bradley RSLogix 5000 / Studio 5000, local tags (variables) are not declared inline in structured text routines. Instead, tags are defined in the Tag Browser. "Controller Tags" holds the controller-scoped (global) tags, and each Program has its own "Parameters and Local Tags" section where the program-scoped tags are defined. Tags shown in the Local Tags section of a program can be used in any routine in that program.
Once you add your program-scoped tag to the appropriate Tag Browser, you can use the tag in your structured text routine. If you try to use a tag that is not declared as either a program-scoped or controller-scoped tag, you will get a verify error.

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.

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;

delphi Login Form

in my Delphi program i've a Login Form and it's Displayed Before the Main Form is Created , but the issue that i'm facing is that i want to Login Check to be processed in the main form , that means the Login Form will use the Main Form to check and proceed ,
please read the comment placed in :
procedure LogInButtonClick(Sender: TObject) ;
here is the TLoginForm code ( from delphi.about.com ):
unit login;
interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TLoginForm = class(TForm)
LogInButton: TButton;
pwdLabel: TLabel;
passwordEdit: TEdit;
procedure LogInButtonClick(Sender: TObject) ;
public
class function Execute : boolean;
end;
implementation
{$R *.dfm}
class function TLoginForm.Execute: boolean;
begin
with TLoginForm.Create(nil) do
try
Result := ShowModal = mrOk;
finally
Free;
end;
end;
procedure TLoginForm.LogInButtonClick(Sender: TObject) ;
begin
if passwordEdit.Text = 'delphi' then
{
Here how it's possible to use :
if MainForm.text=passwordEdit.Text then
ModalResult := mrOK
}
ModalResult := mrOK
else
ModalResult := mrAbort;
end;
end.
and here's the Main Program Initialization flow :
program PasswordApp;
uses
Forms,
main in 'main.pas' {MainForm},
login in 'login.pas' {LoginForm};
{$R *.res}
begin
if TLoginForm.Execute then
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm) ;
Application.Run;
end
else
begin
Application.MessageBox('You are not authorized to use the application. The password is "delphi".', 'Password Protected Delphi application') ;
end;
end.
thank you
If you need the main form to be created first, then create it first:
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);//created, but not shown
if TLoginForm.Execute then//now the login form can refer to the main form
Application.Run//this shows the main form
else
Application.MessageBox('....');
end;
That's a direct and naive answer to the question that you asked. Thinking more broadly, I would encourage you to move the login testing out of the main form. Put it somewhere that can be used by whatever higher-level code needs to. The design you are currently working towards has unhealthy coupling.
I usually do this from the OnCreate of the MainForm; Or from the OnCreate of the DataModule, if you have one. For example:
TMainForm.OnCreate(Sender: TObject);
var F: TLoginForm;
begin
F := TLoginForm.Create(Self);
try
F.ShowModal;
finally F.Free;
end;
end;
I don't like messing with the DPR file too much. This works, shows the forms in the correct order, and if the TMainForm was auto-created by Delphi then the MainForm variable is already assigned and ready to use when the OnCreate fires;
PS: Accesing the MainForm variable is actually bad design, but it's there if you want it.
Similar to David's answer, but with slightly different behaviour, I previously answered this solution which is capable of reuse in application's lifetime.

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.