How to get a link to TScriptForm in Delphi at the runtime

Good day! My question about forms that are created in runtime.

I need to have a link in Delphi to the form that the script creates. I've created a class

TApplicationForm = class(TScriptForm)

with a Manager property to be able to close all generated forms from the application. I assign this Manager property at the time of creating a new unit:

TApplicationForm(AUnit.Form).Manager := FFormManager;

But I understand that in runtime a new instance of this form "doesn't know" about the Manager (Manager=nil). Tell me please, how to get a link to this instance of TApplicationForm in the main (native) code?

Have you set ScriptFormClass property to make sure that all script forms created by scripter are of type TApplicationForm?

IDEScripter1.ScriptFormClass := TApplicationForm;

Reference: How to init ScripterForm from native-delphi code

Yes Wagner, I set this value. The forms themselves work fine. The problem is that there are two of them — one in design mode, the other is created by a script. The first one has access and the Manager property is assigned correctly, but I need to access the second).

In fact, I need to somehow get a reference to this constructor:

constructor CreateFromScript(AOwner: TComponent; AMachine: TatVirtualMachine);

Maybe engine somewhere saves this pointer?

Hi Vitalii, sorry but I don't understand what you need. Can you please be more specific, or illustrate with some source code? The design-time and runtime forms are created the same way, they are just two different instances.

Everything is very simple.

  1. I am creating a new unit and form TApplicationForm via TIDEEngine.NewFormUnit.
  2. I assign property TApplicationForm(Result).Manager := FFormManager.
  3. Run project.
  4. I press the RESET button, which calls the destructor of the form through the manager:
procedure TApplicationFormItems.CloseAllForms;
var
   i: Integer;
begin
   for i: = 0 to Count - 1 do
     if Assigned(Items[i].Form) then
       Items[i].Form.Free;
end;
  1. Result: design-time form (in the editor) is deleted, but the form that the script created is still exists (see screenshot). Of course, I need to delete/close runtime form, not design-time form.

  2. Design-time form: Manager<>nil; runtime form: Manager=nil. I know that design-time and runtime forms are created the same way, but how can I access the runtime form?

1 Like

Please detail this. NewFormUnit returns a TIDEProjectFile, not a TScriptForm. If you are casting it to TApplicationForm, it's wrong. You should cast Result.Form.

Wagner, the problem is not in this place. I am overriding TIDEEngine.CreateScriptForm method:

function TApplicationEngine.CreateScriptForm: TIDEScriptForm;
begin
   Result := inherited CreateScriptForm;
   if Assigned(Result) and (Result is TApplicationForm) then
     begin
       TApplicationForm(Result).Manager := FFormManager;
     end;
end;

I also tried the way you wrote it, ie using TIDEEngine.NewFormUnit.Form.

This is not the question. The question is, where do I get a pointer to the runtime form from?

If I understand correctly, this code creates the form:

function TatVirtualMachine.CreateFormRunScript (AForm: TComponent): TatScript;

Is it possible to somehow get a link to the form that the script creates? Just something like this:

"TIDEEngine.NewFormUnit.RuntimeInstanceForm" or "VirtualMachine.RuntimeForm"

Hope, you understand)

I'm still confused. Why TIDEEngine.NewFormUnit.Form doesn't work for you?

Because TIDEEngine.NewFormUnit.Form returns pointer to the design-time form (as my tests show).

A simple test.

  1. We create one form in the designer using the method
    Form1 := TIDEEngine.NewFormUnit.Form;

  2. If we execute the project, for example, 3 times in a row, we will get 3 runtime forms, right? (see screenshot 1) I understand that it is wrong to run the project more than 1 time, but this is for example. The bottom line is that before re-executing, I need to delete the runtime form (Form.Free).

  3. Question: Where can I find pointers to these runtime forms? To close them, for example. If we now use variable Form1 and execute Form1.Close, then in my case the form is closed in the designer, and all runtime forms are unchanged (screenshot 2). What I'm doing wrong? Sorry for the long discussion :)

Screenshot1:

Screenshot 2:

Ok, I see that TApplication is the owner of all runtime forms, so we can iterate over the components:

procedure TFormManager.CloseAllForms;
var
  i: Integer;
  Form: TScriptForm;
begin
  for i := Application.ComponentCount - 1 downto 0 do
    if Application.Components[i] is TScriptForm then
      begin
        Form := TScriptForm(Application.Components[i]);
        Form.Free;
      end;
end;

It will be very good if the engine stores pointers to all created forms somewhere. Then it would be easier to manage them.

Just like a wish:
TScripter.RuntimeForms[i]

Thanks! Problem is resolved.

1 Like

Ok, I understand now, sorry. Well, indeed, that's one solution. Scripter doesn't hold pointers to created forms, forms are just like any other regular objects. It doesn't hold pointers or do any memory management, that's up to you indeed.

1 Like

In fact, such a solution would be very useful:

function TatVirtualMachine.CreateFormRunScript(AForm: TComponent): TatScript;
begin
...
  LastCreatedRuntimeForm := AForm;
...
end;

Can you add a property like LastCreatedRuntimeForm? The property simply returns the last form created at runtime.

If you add it in the next releases, the scripter users will thank you very much, I'm sure)

But you can do that already in your own TScriptForm descendant.

Yes, you right, but in this case we need to create some global list / collection to hold this info. I think, ideally work only with classes and components, no global variables. Therefore, it is important to have a class that would hold a pointer to the created object. Only from the perspective of the class model)

Anyway, thank you, Wagner!

1 Like