formsdelphiidedesignertoolsapi

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;

Solution

  • 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