5

我一直在尝试让我的应用程序中的一些无模式表单出现在任务栏上——利用 Windows 7 中新的有用任务栏。

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

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

十年前,彼得·弗莱德 (TeamB) 记录了这些问题,并尝试解决这些问题。但是有些问题是无法解决的。这些问题在 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属性是只读的。

我怎么能没有MainForm德尔福?

也可以看看

4

6 回答 6

10

如果没有分配 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;
于 2010-10-21T19:38:45.540 回答
5

你不能,尤其是在 Delphi 5 中。

您关于 TApplication 窗口是在任务栏上看到的窗口的报价对于现在的几个 Delphi 版本并不正确(我相信 D2007 改变了它)。

因为您使用的是 Delphi 5,所以您使用的是过时的 Delphi 副本;当前版本几乎没有您正在写的任何内容。我建议您升级到更高版本的 Delphi(D5 非常旧);如果您需要避免使用 Unicode,请使用 Delphi 2007,如果您可以在 VCL 和 RTL 中使用(或不介意拥有)Unicode 支持,请使用 Delphi XE。

你描述的东西不是错误,顺便说一句。它们是在设计 Delphi 1 时有意做出的设计决策,并且通过 Delphi 7 可以很好地与可用的 Windows 版本配合使用。更高版本的 Windows(XP/Vista/Win7 和等效的服务器版本)的更改使该体系结构发生了必要的更改,并且随着 Delphi 与 Windows 一起进步而进行了更改。因为您选择不继续使用您的 Delphi 版本以使其保持更新,所以您所写的内容不会神奇地变成错误。:-)

于 2010-10-21T14:09:56.587 回答
3

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

项目1.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.

单元1.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.

单元2.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 测试。)

(是的,您可能会将其标记为不是答案,因为它不是:仍然存在 MainForm。但我确实认为这回答了问题背后的问题......)

于 2011-06-03T16:36:42.557 回答
2

我不能代表 Delphi 5,但在 Delphi 7 中,如果你愿意亲自动手,你绝对可以在没有主窗体的情况下运行。我在这里的另一个答案中介绍了很多细节。

由于 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 有效的代码。

于 2011-06-03T18:46:06.657 回答
0

您可以将无模式表单放入 dll 中,然后它们几乎可以自行运行。(如果在创建它们时不使用 dll 的 Application 实例(Application.CreateForm),则 Application.Mainform 在 dll 中为零)。

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

于 2010-10-21T14:51:30.250 回答
0

实际上,您所抱怨的大部分实际上是 Windows 的设计,而不是 VCL。有关所有详细信息,请参阅Windows 功能

问题的症结在于所有者财产,我指的是windows所有者而不是VCL所有者。

拥有的窗口在其所有者最小化时被隐藏。

如果您希望能够在不隐藏其他窗口的情况下最小化主窗体,那么您需要了解拥有的窗口是如何工作的。

于 2011-06-03T17:35:55.690 回答