zoukankan      html  css  js  c++  java
  • 关于Form.Close跟Form.Dispose

    我们在Winform开发的时候,使用From.Show来显示窗口,使用Form.Close来关闭窗口。熟悉Winform开发的想必对这些非常熟悉。但是Form类型实现了IDisposable接口,那我们是否需要每次关闭窗口后都去调用Dispose呢?对于这个问题我们可以查看一下Form的源码。

    Form.Close
     public void Close()
        {
          if (this.GetState(262144))
            throw new InvalidOperationException(SR.GetString("ClosingWhileCreatingHandle", new object[1]
            {
              (object) "Close"
            }));
          else if (this.IsHandleCreated)
          {
            this.closeReason = CloseReason.UserClosing;
            this.SendMessage(16, 0, 0);
          }
          else
            base.Dispose();
        }
    很明显这个方法有3个分支。第一个分支是关闭出现异常的情况,第二个分支是句柄已经创建的时候执行,很明显第三个分支的时候直接调用了基类的Dispose方法。大部分时候窗口调用Close时句柄肯定是被创建了,那就会进入第二个分支。很明显第二个分支没有调用Dispose,那是不是真的呢?继续跟进去。
     
    SendMessage  
    internal IntPtr SendMessage(int msg, int wparam, int lparam)
        {
          return UnsafeNativeMethods.SendMessage(new HandleRef((object) this, this.Handle), msg, wparam, lparam);
        }
    调了一个静态方法继续进去
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);

    明白了吧。SendMessage其实是调用了user32.dll的API,向指定的窗口发送消息。这里就是向自己发送一个消息,注意msg=16哦。既然有发送消息的,那肯定得有拦截消息的方法啊。(这里多扯一句,.NET Winform使用了事件驱动机制,事件机制其实也是封装了消息机制。)

    WndProc
        protected override void WndProc(ref Message m)
        {
          switch (m.Msg)
          {
            case 529:
              this.WmEnterMenuLoop(ref m);
              break;
            case 530:
              this.WmExitMenuLoop(ref m);
              break;
            case 533:
              base.WndProc(ref m);
              if (!this.CaptureInternal || Control.MouseButtons != MouseButtons.None)
                break;
              this.CaptureInternal = false;
              break;
            case 546:
              this.WmMdiActivate(ref m);
              break;
            case 561:
              this.WmEnterSizeMove(ref m);
              this.DefWndProc(ref m);
              break;
            case 562:
              this.WmExitSizeMove(ref m);
              this.DefWndProc(ref m);
              break;
            case 288:
              this.WmMenuChar(ref m);
              break;
            case 293:
              this.WmUnInitMenuPopup(ref m);
              break;
            case 274:
              this.WmSysCommand(ref m);
              break;
            case 279:
              this.WmInitMenuPopup(ref m);
              break;
            case 167:
            case 171:
            case 161:
            case 164:
              this.WmNcButtonDown(ref m);
              break;
            case 71:
              this.WmWindowPosChanged(ref m);
              break;
            case 130:
              this.WmNCDestroy(ref m);
              break;
            case 132:
              this.WmNCHitTest(ref m);
              break;
            case 134:
              if (this.IsRestrictedWindow)
                this.BeginInvoke((Delegate) new MethodInvoker(this.RestrictedProcessNcActivate));
              base.WndProc(ref m);
              break;
            case 16:
              if (this.CloseReason == CloseReason.None)
                this.CloseReason = CloseReason.TaskManagerClosing;
              this.WmClose(ref m);
              break;
            case 17:
            case 22:
              this.CloseReason = CloseReason.WindowsShutDown;
              this.WmClose(ref m);
              break;
            case 20:
              this.WmEraseBkgnd(ref m);
              break;
            case 24:
              this.WmShowWindow(ref m);
              break;
            case 36:
              this.WmGetMinMaxInfo(ref m);
              break;
            case 1:
              this.WmCreate(ref m);
              break;
            case 5:
              this.WmSize(ref m);
              break;
            case 6:
              this.WmActivate(ref m);
              break;
            default:
              base.WndProc(ref m);
              break;
          }
        }

    WndProc就是用来拦截窗口消息的。看一下代码,Form重写了这个方法,一个很简单的switch。Case 16调用了 WmClose方法,继续跟进去。

    WmClose
    private void WmClose(ref Message m)
        {
          FormClosingEventArgs e1 = new FormClosingEventArgs(this.CloseReason, false);
          if (m.Msg != 22)
          {
            if (this.Modal)
            {
              if (this.dialogResult == DialogResult.None)
                this.dialogResult = DialogResult.Cancel;
              this.CalledClosing = false;
              e1.Cancel = !this.CheckCloseDialog(true);
            }
            else
            {
              e1.Cancel = !this.Validate(true);
              if (this.IsMdiContainer)
              {
                FormClosingEventArgs e2 = new FormClosingEventArgs(CloseReason.MdiFormClosing, e1.Cancel);
                foreach (Form form in this.MdiChildren)
                {
                  if (form.IsHandleCreated)
                  {
                    form.OnClosing((CancelEventArgs) e2);
                    form.OnFormClosing(e2);
                    if (e2.Cancel)
                    {
                      e1.Cancel = true;
                      break;
                    }
                  }
                }
              }
              Form[] ownedForms = this.OwnedForms;
              for (int index = this.Properties.GetInteger(Form.PropOwnedFormsCount) - 1; index >= 0; --index)
              {
                FormClosingEventArgs e2 = new FormClosingEventArgs(CloseReason.FormOwnerClosing, e1.Cancel);
                if (ownedForms[index] != null)
                {
                  ownedForms[index].OnFormClosing(e2);
                  if (e2.Cancel)
                  {
                    e1.Cancel = true;
                    break;
                  }
                }
              }
              this.OnClosing((CancelEventArgs) e1);
              this.OnFormClosing(e1);
            }
            if (m.Msg == 17)
              m.Result = (IntPtr) (e1.Cancel ? 0 : 1);
            if (this.Modal)
              return;
          }
          else
            e1.Cancel = m.WParam == IntPtr.Zero;
          if (m.Msg == 17 || e1.Cancel)
            return;
          this.IsClosing = true;
          if (this.IsMdiContainer)
          {
            FormClosedEventArgs e2 = new FormClosedEventArgs(CloseReason.MdiFormClosing);
            foreach (Form form in this.MdiChildren)
            {
              if (form.IsHandleCreated)
              {
                form.OnClosed((EventArgs) e2);
                form.OnFormClosed(e2);
              }
            }
          }
          Form[] ownedForms1 = this.OwnedForms;
          for (int index = this.Properties.GetInteger(Form.PropOwnedFormsCount) - 1; index >= 0; --index)
          {
            FormClosedEventArgs e2 = new FormClosedEventArgs(CloseReason.FormOwnerClosing);
            if (ownedForms1[index] != null)
            {
              ownedForms1[index].OnClosed((EventArgs) e2);
              ownedForms1[index].OnFormClosed(e2);
            }
          }
          FormClosedEventArgs e3 = new FormClosedEventArgs(this.CloseReason);
          this.OnClosed((EventArgs) e3);
          this.OnFormClosed(e3);
          base.Dispose();
        }

    WmClose这个方法略有点复杂,主要是用来出发OnCloseing,OnClosed等事件。看看最后,它终于调用了base.Dispose()。看来Close方法确实会自动调用Dispose。是吗,不要高兴的太早。仔细看看前面的代码,if (this.Modal) return; 看到没,当窗口是模态的时候,方法直接return了。

    总结

    到这里就差不多了。所以当我们使用ShowDialog来显示窗体的时候,当你关闭的时候,最好手动Dispose一下。为什么是最好呢,因为其实在GC回收垃圾的时候还是会调用窗体的Dispose的,因为在Form的基类的终结器里面有调用Dispose(false);

       ~Component()
        {
          this.Dispose(false);
        }

    其实在MSDN上微软就对这有说明,顺便吐槽一下中文MSDN的翻译,实在是太烂了。

    image

    有2种情况下需要手工调用Dispose:
    1. 窗口是MDI的一部分且是不可见的

    2.模态的时候
    第二种情况就是现在说的,但是第一种情况我测试了下,没有复现出来,MDI里面的子窗口调用Close的时候跟正常一样,每次都会自动Dispose。试了很久还是一样的结果,求高人指定吧。


  • 相关阅读:
    关于抑或
    【vue】条件渲染 v-if v-else
    【vue】vue的目录结构、项目流程、vue-router
    【vue】在vue中引入iview
    【vue】vue如何创建一个项目
    【jquery】jquery怎么实现点击一个按钮控制一个div的显示和隐藏
    【angularjs】ng-model controller中取不到值(input)
    打印机增强软件pdfpro
    vagrant 安装ubuntu12.04 64 bit
    debian 7 stable 不能编译android源码
  • 原文地址:https://www.cnblogs.com/kklldog/p/3269977.html
Copyright © 2011-2022 走看看