如何在Delphi中没有MainForm?

时间:2010-10-21 13:51:55

标签: delphi windows-7 taskbar delphi-5

我一直试图让我的应用程序中的一些无模式窗体显示在任务栏上 - 利用Windows 7中新的有用任务栏。

在任务栏上存在表单之前,VCL有许多问题需要撤消。

但最后一个问题是,最小化VCL指定主窗体的形式会导致应用程序中的所有窗口消失。

Ten years ago, Peter Below (TeamB) documented these problems,尝试解决这些问题。但是有些问题无法解决。这些问题在VCL本身内部如此深入,实际上不可能使Delphi应用程序正常运行。

这一切都源于您在工具栏上看到的按钮不代表应用程序窗口的事实;它代表TApplications窗口,它是隐藏的,从未见过。然后是应用程序的MainForm,然后充满了特殊的能力,如果被最小化,那么它会指示应用程序隐藏自己。

在我看来,如果我能做到

Application.MainForm := nil;
然后所有这些错误都会消失。该应用程序可以有其隐藏窗口,同时我将覆盖应用程序中的每个 其他 表单,包括我的主表单, :

procedure TForm2.CreateParams(var params: TCreateParams ); 
begin 
   inherited CreateParams(params); 
   params.ExStyle := params.ExStyle or WS_EX_APPWINDOW; 
end; 

但在Delphi中,Application.MainForm属性是只读的。

我怎么能在Delphi中没有MainForm

另见

6 个答案:

答案 0 :(得分:8)

如果未分配MainForm,则无法运行GUI项目。主消息循环将立即退出而没有一个。但是,这并不意味着MainForm必须运行您的UI。您可以使用空白隐藏的TForm作为指定的MainForm,然后让它将您的真实MainForm实例化为辅助TForm。例如:

HiddenMainFormApp.dpr:

project HiddenMainFormApp;

uses
  ..., Forms, HiddenMainForm;

begin
  Application.Initialize;
  Application.CreateForm(THiddenMainForm, MainForm);
  Application.ShowMainForm := False;
  Application.Run;
end.

HiddenMainForm.cpp:

uses
  ..., RealMainForm;

procedure THiddenMainForm.FormCreate(Sender: TObject);
begin
  RealMainForm := TRealMainForm.Create(Self);
  RealMainForm.Show;
end;

RealMainForm.cpp:

procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Application.Terminate;
end;

可替换地:

HiddenMainFormApp.dpr:

project HiddenMainFormApp;

uses
  ..., Forms, HiddenMainForm, RealMainForm;

begin
  Application.Initialize;
  Application.CreateForm(THiddenMainForm, MainForm);
  Application.ShowMainForm := False;

  RealMainForm := TRealMainForm.Create(Application);
  RealMainForm.Show;
  RealMainForm.Update;

  Application.Run;
end.

RealMainForm.cpp:

procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Application.Terminate;
end;

答案 1 :(得分:5)

你不能,特别是在Delphi 5中。

关于TApplication窗口的引用是在任务栏上看到的,现在几个Delphi版本都没有(我相信D2007改变了它)。

因为您使用的是Delphi 5,所以您使用的是Delphi的过时副本;目前的版本几乎没有你要写的东西了。我建议你升级到更高版本的Delphi(D5非常老); Delphi 2007如果你需要避免使用Unicode,Delphi XE,如果你可以在VCL和RTL中使用(或者不介意)Unicode支持。

您所描述的内容不是错误,BTW。它们是在设计Delphi 1时做出的有意设计决策,并且通过Delphi 7可以正常使用Windows版本。更高版本的Windows(XP / Vista / Win7和等效的服务器版本)的变化使得该架构的变化成为必要,并且它们随着Delphi与Windows一起发展而制定。因为你已经选择不继续使用你的Delphi版本来保持它最近不会使你写的东西神奇地变成bug。 : - )

答案 2 :(得分:3)

分配Application.MainForm似乎不是问题,因为在最小化MainForm的同时在任务栏上显示另一个无模式窗体。

Project1.dpr:

program Project1;

uses
  Forms,
  Windows,
  Unit1 in 'Unit1.pas' {MainForm},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

var
  MainForm: TMainForm;

begin
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  ShowWindow(Application.Handle, SW_HIDE);
  Application.Run;
end.

Unit1.pas:

unit Unit1;

interface

uses
  Windows, Messages, Classes, Controls, Forms, StdCtrls, Unit2;

type
  TMainForm = class(TForm)
    ShowForm2Button: TButton;
    ShowForm2ModalButton: TButton;
    procedure ShowForm2ButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ShowForm2ModalButtonClick(Sender: TObject);
  private
    FForm2: TForm2;
    procedure ApplicationActivate(Sender: TObject);
    procedure Form2Close(Sender: TObject; var Action: TCloseAction);
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Visible := True; //Required only for MainForm, can be set designtime
  Application.OnActivate := ApplicationActivate;
end;

procedure TMainForm.ApplicationActivate(Sender: TObject);
{ Necessary in case of any modal windows dialog or modal Form active }
var
  TopWindow: HWND;
  I: Integer;
begin
  TopWindow := 0;
  for I := 0 to Screen.FormCount - 1 do
  begin
    Screen.Forms[I].BringToFront;
    if fsModal in Screen.Forms[I].FormState then
      TopWindow := Screen.Forms[I].Handle;
  end;
  Application.RestoreTopMosts;
  if TopWindow = 0 then
    Application.BringToFront
  else
    SetForegroundWindow(TopWindow);
end;

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    ExStyle := ExStyle or WS_EX_APPWINDOW;
    WndParent := GetDesktopWindow;
  end;
end;

procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
  if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Handle, SW_MINIMIZE)
  else
    inherited;
end;

{ Testing code from here }

procedure TMainForm.ShowForm2ButtonClick(Sender: TObject);
begin
  if FForm2 = nil then
  begin
    FForm2 := TForm2.Create(Application); //Or: AOwner = nil, or Self
    FForm2.OnClose := Form2Close;
  end;
  ShowWindow(FForm2.Handle, SW_RESTORE);
  FForm2.BringToFront;
end;

procedure TMainForm.Form2Close(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FForm2 := nil;
end;

procedure TMainForm.ShowForm2ModalButtonClick(Sender: TObject);
begin
  with TForm2.Create(nil) do
    try
      ShowModal;
    finally
      Free;
    end;
end;

end.

Unit2.pas:

unit Unit2;

interface

uses
  Windows, Messages, Classes, Controls, Forms;

type
  TForm2 = class(TForm)
  private
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

{$R *.dfm}

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    ExStyle := ExStyle or WS_EX_APPWINDOW;
    WndParent := GetDesktopWindow;
  end;
end;

procedure TForm2.WMSysCommand(var Msg: TWMSysCommand);
begin
  if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Handle, SW_MINIMIZE)
  else
    inherited;
end;

end.

(在XP和Win7上用D5和D7测试过。)

(是的,你可能会认为这不是一个答案,因为它不是:还有一个主要形式。但我想这会回答问题背后的问题......)

答案 3 :(得分:2)

我不能代表Delphi 5,但是在Delphi 7中,如果你愿意弄脏你,你绝对可以在没有主变形的情况下运行。我在另一个答案here中介绍了很多细节。

由于Delphi 5没有MainFormOnTaskbar属性,您需要在dpr中执行以下操作:

// Hide application's taskbar entry
WasVisible := IsWindowVisible(Application.Handle);
if WasVisible then
  ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
  GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
if WasVisible then
  ShowWindow(Application.Handle, SW_SHOW);
// Hide the hidden app window window from the Task Manager's
// "Applications" tab.  Don't change Application.Title since
// it might get read elsewhere.
SetWindowText(Application.Handle, '');

这将隐藏应用程序窗口,只要您覆盖表单的 CreateParams 以设置Params.WndParent := 0,每个人都将拥有自己的任务栏条目。 Application.MainForm未分配,因此最小化覆盖等问题不是问题,但您必须小心任何假定MainForm有效的代码。

答案 4 :(得分:0)

你可以将你的无模式表格放在一个dll中,然后它们就可以自己动作了。 (如果在创建它时不使用dll的Application实例(Application.CreateForm),则dll中的Application.Mainform为nil。)

当然,这可能不太可行,具体取决于表格可能需要做什么。

答案 5 :(得分:0)

实际上,你抱怨的大部分内容实际上都是Windows的设计,而不是VCL。有关所有详细信息,请参阅Windows Features

问题的关键是所有者财产,我的意思是窗户所有者而不是VCL所有者。

  

隐藏拥有的窗口时   它的主人是最小化的。

如果您希望能够在不隐藏其他窗口的情况下最小化主窗体,那么您需要掌握所拥有的窗口的工作方式。