问题提出:
从MainForm动态创建一个Form(已经设计好的),代码为:
if (NOT Assigned(ActiveForm)) then
  ActiveForm:= TActiveForm.Create(Application);
if(Assigned(ActiveForm)) then
  ActiveForm.Show()
当调试运行后从ActiveForm的FormCreate()事件中直接使用Self.Close()关闭ActiveForm后,再次执行上述创建窗体的代码后发生错误,并强制关闭。解决方法:
在ActiveForm的FormCreate()事件中使用PostMessage来关闭窗体。代码:
Windows.PostMessage(Self.Handle, WM_CLOSE, 0, 0);问题:
虽然关闭窗体的问题已经解决了,但是其中一些具体细节还不知道。为什么Self.Close()会发生错误,而PostMessage()却可以正常执行?
为什么是第二次时发生错误而不是第一次时???
FormCreate()事件中一般会有一些初始化的代码,并且一个Form一般都会有一些组件,当从FormCreate()中直接关闭时,是不是因为某些组件未能正常初始化或释放而发生错误?如果这样理解的话,PostMessage()是不是提供了一个缓冲时间,来完成FormCreate()事件后再关闭Form?

解决方案 »

  1.   

    另外做个调查:
    我是个懒人,如果遇到困难的问题,特别是涉及代码的,一般解决流程是:1>到CSDN社区提问;2>刷新帖子看回复;3.1>运气好,有高手给了解决方法,解决;3.2>运气差,再发帖子如果运气十分以及相当并且非常差,直接导致死循环最后被迫自己解决问题调查内容就是:
    看这个帖子的人有多少是和我一样或相仿的???如果都是的话,那么,证明CSDN和中国的短信业务一样,都是垃圾的东西比较多,当然也不排除垃圾堆里有好东西最后向CSDN给我提供的帮助致谢,我给CSDN导致的麻烦致歉!
      

  2.   

    代码就我说的那些。没什么特殊的。
    动态创建已经设计好的窗体,从动态窗体中的FormCreate()时间中使用Self.Close()关闭自己。然后再次创建如此而已。
      

  3.   

    PostMessage是把WM_CLOSE放入到窗口的消息队列中,而FormCreate实际是窗口正在执行WM_CREATE消息,因此,WM_CREATE完全执行完之后才会执行WM_CLOSE消息,即关闭窗口而你在FormCreate中调用Close方法时,WM_CREATE消息还没有完全执行完,这时候又进行了关闭,自然会出问题,具体问题可以看看VCL源码,我这里没有VCL源码,无法帮你分析.多数问题你可以自己分析VCL源码得到答案,这是最好的学习方式。
      

  4.   

    to:zwjchina(蒲石) 感谢!回答简练到位。
    一直在线等,一直没有等到好答案,所以看了看VCL。窗体的部分看过了,了解了不少,但是看完后有更多不明白的了,不过感觉还算不错。
    不过Windows消息部分一直没看,所以你的回答很有帮助。不过细节还是少了点。等等看还有没有更好的答案,再结贴。
      

  5.   

    呵呵,理解方向有问题。
    先不要尝试使用API或者直接使用Message。
    考虑这样一段代码:
    type
      TFoo = class;
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        Foo: TFoo;
        { Private declarations }
      public
        { Public declarations }
      end;
      TFoo = class(TObject)
      private
         FForm:TForm;
         function GetForm: TForm;
      public  end;
    var
      Form1: TForm1;implementation{$R *.dfm}function TFoo.GetForm: TForm;
    var S:String;
    begin
      if Assigned(FForm) then
      Result := FForm
      else
      begin
        FForm:=TForm.Create(nil);
        Result:=FForm;
      end;
    end;procedure TForm1.Button1Click(Sender: TObject);
    var AForm:TForm;
    begin
        AForm:=Foo.GetForm;
        AForm.ShowModal;
        FreeAndNil(AForm);
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      Foo:=TFoo.Create;
    end;procedure TForm1.FormDestroy(Sender: TObject);
    begin
      Foo.free;
    end;
    当第一次按Button1时,正常。第二次的时候,会发生什么事呢?
      

  6.   

    这段代码我怎么一次都运行不了,assigned总是返回true的,就是说create不会被执行。
    难道用var声明类的一个对象后就已经分配了vmt空间,而不是在create时分配的?
      

  7.   

    非也非也,在类中,内部属性所保存的对象是自处理的,并不会自动被外部程序影响,如果在外部被释放,自然会有问题。如果在Form的Close事件中,CloseAction设为caFree,那种,这个Form会被释放。对类中所保存的指针指向的地址就不再合法了。
    所以,Delphi中,有一组相关的事件通知这类事情,你可以查一下帮助,这类事件是
    procedure FreeNotification(AComponent: TComponent);
    procedure Notification(AComponent: TComponent; Operation: TOperation);
    procedure FreeNotification(AComponent: TComponent);
      

  8.   

    根据帮助,我们做出如下修改:注意:bluekitty(一只Colorful猪) 运行不了是因为没有链接
    FormCreate与FormDestroy;现在,我干脆贴出form文本出来。
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TFoo = class;
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        Foo: TFoo;
        { Private declarations }
      public
        { Public declarations }
      end;
      TFoo = class(TComponent)
      private
         FForm:TForm;
         function GetForm: TForm;
      public
         procedure Notification(AComponent: TComponent; Operation: TOperation); override;  end;
    var
      Form1: TForm1;implementation{$R *.dfm}function TFoo.GetForm: TForm;
    var S:String;
    begin
      if Assigned(FForm) then
      Result := FForm
      else
      begin
        FForm:=TForm.Create(nil);
        Result:=FForm;
        FForm.FreeNotification(Self);
      end;
    end;procedure TFoo.Notification(AComponent: TComponent; Operation: TOperation);
    begin
      inherited;
      if(AComponent=FForm)and(  Operation = opRemove) then
      begin     FForm:=nil
      end;
    end;procedure TForm1.Button1Click(Sender: TObject);
    var AForm:TForm;
    begin
        AForm:=Foo.GetForm;
        AForm.ShowModal;
        FreeAndNil(AForm);
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      Foo:=TFoo.Create(Self);
    end;procedure TForm1.FormDestroy(Sender: TObject);
    begin
      Foo.free;
    end;
    end.///以下是FORM
    object Form1: TForm1
      Left = 0
      Top = 0
      Caption = 'Form1'
      ClientHeight = 327
      ClientWidth = 436
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      OldCreateOrder = False
      OnCreate = FormCreate
      OnDestroy = FormDestroy
      PixelsPerInch = 96
      TextHeight = 13
      object Button1: TButton
        Left = 192
        Top = 80
        Width = 75
        Height = 25
        Caption = 'Button1'
        TabOrder = 0
        OnClick = Button1Click
      end
    end
      

  9.   

    所以,要搞清楚Form的创建,运行,与销毁过程。
    在Delphi工程中,工程文件中,一般会自动创建Form,在Option中可以移除。
    上面的代码中,创建是由单元程序自主控制的。自主控制的Form显然要令人放心得多。
      

  10.   

    做MDI的时候为了避免同一个窗口被两次创建通常会使用这种方法
    解决办法就是在窗口onClose的时候Action 等于 caFree(详细可以看delphi帮注有例子)
    而且我不理解为什么楼主会在OnCreate里面调用Close方法。
    如果你不想让它出现可以在OnShow的时候Close掉,这样绝对不会有问题的
    虽然你这么写,第一次没有出错,不过不能证明你这样就对...
      

  11.   

    继续 .................
    再回过头来看TFoo,再思考一个问题:如果外部调用者并没有Free Form,那么,这个Form会被Free掉吗?
    答案是不会的。为什么???delphi的工程文件中也自动创建也自动销毁呀?!!
    此时,要特别注意这样一个方法。
    function TFoo.GetForm: TForm;
    var S:String;
    begin
      if Assigned(FForm) then
      Result := FForm
      else
      begin
        FForm:=TForm.Create(nil);//再看一看!!!
        Result:=FForm;
        FForm.FreeNotification(Self);
      end;
    end;关键行是 FForm:=TForm.Create(nil);
    也就是说,Owner为nil。
    在Form(所有控件)的基类 TComponent中,有一个子控件列表,它自维护这个列表,如果destroy时,这个列表中有控件不为nil,那么,它会自动Free掉它们,相当于说,TComponent被分到的饭它会全部消化掉。
      观察.dpr工程文件中, 其实上,自动创建的Form,DataModule的Owner全都是Application,所以能够被自动释放。
      这就是我们创建控件时为什么都会象下面这样用的原因。
        AComponent := TComponent.Create(Self);
        ALabel:=TLabel.Create(Self);
        ...
        AEdit:=TEdit.Create(Self);
    ...这样可以简化代码,反正我交给self了,self会消化掉的,我只关心我要的业务逻辑控件就行了。其他的,管太多,我累不累呀我?!
       TForm有着一些特别的特点,在Close的时候,可以指定是不是释放。closeAction = caFree,就会自释放。通常,Close只不过是隐藏Form罢了,如果你指定了caFree,则它会自释放。 再回过头来,看看TFoo。
     FForm那碗饭并没有分给任何人,如果大家都放任不管,它一定会放在那里变馊。 所以,我们要完善一下TFoo类,需要重写Destroy方法。TFoo=class(TComponent);
    ....
    ...
    public 
    ...
     destructor Destroy;override;
    end;
    ...destructor TFoo.Destroy;
    begin
      if FForm<>nil then 
      begin
        FreeAndNil(FForm);
      end; 
      Inherited Destroy;
    end;真是因为有了FreeNotification,我们也重写了Notification,所以可以很放心地检查FForm是不是nil(当然,也可以用Assigned(FForm)),也可以保证正确释放对象。对象,创建,销毁,通知,宿主,这些内容不搞清楚,不容易抓住程序中的错误。
      

  12.   

    P.S. 如果楼主非要在Create之后立刻释放的话,不要在OnCreate事件中写,要重写Create方法。
    在那个方法中释放。
    这是因为,OnCreate事件发生时,Form的constructor 方法CreateNew ,并没有真正的执行完毕,结果被释放了,虽然这样并不一定会导致错误(视代码而定),但并不安全。
    关于你的Post message不会出错,可能的原因比较多,要具体分析:
    先说Free的问题,我们先不管是不是在OnCreate中Close,只管Close本身。
    在TActiveForm 的OnClose中,如果CloseAction设成了caFree,那么出错的原因与我们所讨论的TFoo对象是一样的。
    如果不是,那么情况就比较复杂了。应该是因为在OnCreate中执行Close的原因:
    PostMessage的特性是发送消息到消息对列,然后程序并不停止,立刻往下执行这个过程是十分快的。而Close是会完全执行的,不执行完是不会继续下去的。正是因为你在OnCreate中调用Close,然后又自动Free。类并没有完全被完整创建。因为没有详细的代码,出错的原因不可预计。因为在Create过程中,会读取DFM中的控件,会调用MakeObjectInstance,然后,响应一系列的消息,包括CreateParam,CreateWnd,很多。有不可知且不可控制的原因会令到响应到WM_Close的时候并不释放自身或者并不会引起冲突。这样的代码实在实在是不安全。不知道什么时候在什么机器上就会出错。建议:改变设计。
    请说明为什么要在OnCreate中Close? 为什么不能Hide?
    请说明是否为MDIChild。
    请说明主要意图,OnCreate中Close,令人感到相当奇怪。打个比方吧,就象生孩子,才出来一半,你又给塞回去,我的天。且不说小孩子(TActiveForm),就是妈妈(TApplication)也受不了的吧??
      

  13.   

    to teapot(茶壶)
    哈哈!!!你真地很幽默。你要是当老师,学生一定爱听课,上课肯定不会睡觉的。真地非常非常感谢!!!
    篇幅太大了。估计用了很长时间来打字吧?再次致谢
    还有:
    xjjrocker(了无痕)
    heluqing(鉴之小河〖挣大钱娶美女〗)(越来越硬) 
    谢谢了先说说和题目不相关的事情,我的ADSL猫坏掉了。因为这几天打雷下雨太厉害无奈。铁通的网络,03年开始用的。那个时候是买的猫,¥400.00的设备费,就是猫费!晕死。更可气的是,现在他们居然说可以租用而不用买了。区别就是:买的坏了自己弄,租用的坏了他们负责弄!亏死我了。今天刚刚借了个猫,凑合着用先
      

  14.   

    因为没有网上了而且3天前把脚弄破了请了假,所以这几天有时间。把VCL和FORM的代码看了看,不过还是没看全,耐性有问题。
    不过对于我自己来说看的已经够多了。回答:
    1> 请说明为什么要在OnCreate中Close? 为什么不能Hide?
    首先是习惯问题。对象不用了就要销毁。节约
    然后,涉及到我写的代码,窗体是一个关于设置/配置的窗体。在OnCreate中会调用函数读取ini文件并将数据保存到窗体中的一些变量中,如果出错(文件不存在,内容不符合等等),再保留窗体就没有意义了,当然就要Close掉了。不过,就像你(teapot(茶壶))说的一样,Close的地方有问题。不过用PostMessage就可以了,当窗体Create完成后,PostMessage会将他销毁调,因为Form存在且有效,Form中的控件、组件等等什么的也都创建并初始化完毕,也都存在且有效。所以用PostMessage代替Close,代码本身没问题。不过就是用了非Delphi的罢了,看着不爽——视觉方面的问题,不过能用就好。2>请说明是否为MDIChild。
    否。标准的MDI不是很好用,不是说有错误或BUG,而是说功能不强。为此,专门作了类似于TAppBuilder(Delphi主窗体)的主窗体,完成了90%多了。基本功能都实现了。为此也学习了一些API以及Windows消息,感觉虽然累了点,不过还好总算作出来了——打个比方吧:就像生孩子,生出来了,不过还没听到哭声