程序退出的后,出现一个警告:Warning: calling DestroyWindow in CWnd::~CWnd; OnDestroy or PostNcDestroy in derived class will not be called._CrtDbgReport: String too long or IO Error程序“[2716] DemoDockableForm.exe: 本机”已退出,返回值为 0 (0x0)。
网上一般说,这个缘故是 窗口对象没有销毁,就销毁c++对象导致的,,但是我的内存没有泄露啊!!!首先说说用到的类:CMyDialog------------CDockableForm----------------------CDialog销毁依然是非模式的销毁方式。。这里非常特殊,  CMyDilaog放在一个 面板上, CMyDialog去掉了边框 不存在什么 OK ,INCANCEL 按钮的!!你懂的, 当面板销毁的时候,才销毁 对话框。。面板销毁的时刻是在 程序结束的时候销毁。所以如果想 正确销毁对话框,那么可以在 面板的 OnDestroy中顺便销毁 对话框即可!!问题有些复杂, 大家帮忙看看啊
// CMyDialog 对话框, CDockableForm是基类class CMyDialog : public CDockableForm
{
............protected:

virtual void PostNcDestroy();  //我已经override了PostNcDestroy!! 按照常规,还应该在OnClose中调用DesrtoyWindow,当时我已经去掉了 OK,CANCEL, 边框也去掉了
...................
};void CMyDialog::PostNcDestroy()
  {
  CDockableForm::PostNcDestroy();
  delete this;  //销毁对话框
  }
需要看基类:CDockableForm的,可以看以下,代码如下(不看可以跳过)
//辅助类
class CDockablePaneAsContainer : public CDockablePane
{
public:
CDockablePaneAsContainer(CDialog* pDialog) : m_pDialog(pDialog) { }private:
CDialog* m_pDialog;  
public:
DECLARE_MESSAGE_MAP()
afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnDestroy();  //销毁面板,在这个里面销毁对话框};
//基类对话框类,这个是基类,基类
class CDockableForm : public CDialog
{
public:
CDockableForm(UINT nIDTemplate);  BOOL Create(LPCTSTR lpszCaption, CWnd* pParentWnd, const RECT& rect, BOOL bHasGripper, UINT nID, DWORD dwStyle, DWORD dwTabbedStyle = AFX_CBRS_REGULAR_TABS, DWORD dwControlBarStyle = AFX_DEFAULT_DOCKING_PANE_STYLE, CCreateContext* pContext = NULL); CDockablePane& GetDockablePane() { return m_wndPane; } virtual ~CDockableForm(){}private:
CDockablePaneAsContainer m_wndPane;};
//这里是关键!!!!!!!!!! 说明,DesroyWindow是虚函数,会调用void CDockablePaneAsContainer::OnDestroy()
{
CDockablePane::OnDestroy(); // TODO: 在此处添加消息处理程序代码
m_pDialog->DestroyWindow();  // 销毁对话框
}
CDockableForm::CDockableForm(UINT nIDTemplate)
: CDialog(nIDTemplate, &m_wndPane)
, m_wndPane(this)
{
}
CDockableForm中的面板对象 ,不是有个指针:CDialog* m_pDialog;吗???? 在构造函数中已经使其执向 CDockableForm了!!!!!!
问题比较复杂,大家帮忙看看 谢谢了啊

解决方案 »

  1.   

    非模的直接DELETE指针就可以了,别走其他流程,不然MFC资源删除MFC会有各种错,
    而且别夸线程删除,因为MFC对象作用域仅仅在线程中有效,如果是多线程,想深入理解,跟MFC的删除流程,和GOOGLE  TLS
      

  2.   

    费模式的, delete???怕是不行吧, 窗口对像delete 不会销毁的
      

  3.   

    实现析构函数,
    非模创建是new出来,销毁时,delete,没有问题啊。
      

  4.   

    destroy是MFC回收资源,
    delete后hwnd就是null了,自动回收safe_deletw()跳过。
      

  5.   


    CDockableForm中的面板对象 ,不是有个指针:CDialog* m_pDialog;吗???? 在构造函数中已经使其执向 CDockableForm了!!!!!!m_pDialog应该要指向CMyDialog吧?
      

  6.   


    我不知道,不过我设置过断点当m_pDialog->DestroyWindow后,  会掉入CMyDialog::PostNCDestroy中???
    我问个问题:对于 基类, 子类 他们的地址一样否???我认为是一样的,,不过子类多了一部分数据成员,对吧???m_pDialog是一个CDialog型指针DestroyWindow是一个 虚函数。m_pDialog->DestroyWindow 调用的是谁??????哪一个类的????这里让我混乱的原因在于: c++ 中的类,和窗口类。。窗口显然只有一个,  但是有父类, 派生类。从窗口的角度来看,必然 销毁的是: 对话框, 因为只有一个对话框啊???
    从 c++来看, m_pDialog是一个基类的基类指针。。但是 对于终极派生类CMyDialog来说,它 并没有override:DestroyWindow。所以肯定是调用了CDialog::DestroyWindow!!!!也就是 ,窗口肯定被销毁了。可是为什么还会有警告呢????
      

  7.   

    认真看帖,谢谢我已经DestroyWindow了。。否则内存必然泄露
      

  8.   

    无须关注,是说DELETE this窗口自调用的.
      

  9.   

    你的意思是不是, 压根没必要管它因为 是我 delete this了对吧???还有:能否解释一下6楼的问题。
    6楼疑问:m_pDialog指针 指着谁  ? CMyDialog?? CDockableForm?? CDialog??还是三个类的地址其实是一样的!!!
      

  10.   

    你这里是不是重复销毁导致的错误?CMyDialog最好不用自己delete this
    你是在CDockablePaneAsContainer中创建的嘛
    直接在这里销毁,同时把指针置空就ok了,这个逻辑也清晰按照下面的试试void CDockablePaneAsContainer::OnDestroy()
    {
        CDockablePane::OnDestroy();    // TODO: 在此处添加消息处理程序代码
        if (m_pDialog!=NULL)
       {
            m_pDialog->DestroyWindow();  // 销毁对话框
            delete m_pDialog;
           m_pDialog = NULL;
        }
    }// 这个就不用重载了,去掉
    void CMyDialog::PostNcDestroy()
      {
          CDockableForm::PostNcDestroy();
          delete this;  //销毁对话框
      }
      

  11.   

    看来楼主对C++对象在内存中的布局不清楚,还有C++中的多态技术也不熟。对于 基类, 子类 他们的地址一样否???
    =》你想表达的是子类对象中基类中的内容吧,基类中的Member Data作为一个整体存放在子类的内存空间中,子类的Start Address对应的可能就是基类在子类中的地址,也有可能不一样,具体要看各厂商的Compiler设计,目前C1是这样的,但是对于virtual继承的,c1也不一样。m_pDialog是一个CDialog型指针。(前提你调用的是虚函数)
    =》所以你把该指针指向哪个派生类,那么到时候就会调用哪个派生类的对应函数,这就是所谓的多态.
      

  12.   

    你可以试试在销毁的那里断点,然后进去基类CDialog的DestroyDialog中断点看看是不是两次进入,两个是的话,可能是你两次销毁出错了,个人感觉最有可能是delete this那里再销毁了一次
      

  13.   

    第一个问题:我对基类 ,派生类 地址 是否一致,很含糊,说明内存分布不清楚
    第二个问题:你也看到了  m_pDialog  指针 某个东西的时候,仅仅是在构造函数里面!!!所以这个造成 我很难分析,是否是多态大家都知道,多态一般是这样用的:A *p;p=&b;p->虚函数
    现在的情况不一样!!! 具体看 构造函数:CDockableForm::CDockableForm(UINT nIDTemplate)
        : CDialog(nIDTemplate, &m_wndPane)
        , m_wndPane(this)
    {
    }
    当 终极派生类构造的时候,就会调用 CDockableForm的构造函数,,CDockableForm的构造函数又会调用CDialog构造函数,CDialog 构造函数完毕之后回到CDockableForm的构造函数,最后回到CMyDialog的构造函数然后 去初始化 它所包含的成员对象:m_wndPane!!!
    m_wndPane中包含了 CMyDialog的指针m_pDialog!!!!问题对于我来说,有些复杂吃力请赐教,,,,,1.我分析的对不对? 2.m_pDialog
    最终只着谁? 问什么指着它??
      

  14.   

    m_wndPane中包含了 CMyDialog的指针m_pDialog!!!!跟正这句话, 是CDialog* m_pDialog!!1
      

  15.   

    非常感谢,我先尝试一下之所以会选择到 终极派生类CMyDialog里面delet this;是我认为: 三个对话框类,只有CMyDialog真正的对着窗口对象,其他两个类,不过是其 基类所以我选择 在CMyDialog中了。2. 对于 面板对象来说,资料少, 它会不会进入面板的PostNCDestroy不好说。一般对于非模式,我们都是在PostNCDestroy中delete this;
      

  16.   

    父窗口Destroy的时候会先把所有字窗口Destroy的,你不用在OnDestroy里面重复去Destroy一次
      

  17.   

    17楼的是错误的,,,,  DockablePane面板容器上有个对话框,如果不销毁对话框,只是销毁面板,那么程序 造成泄露为了 让编译器闭嘴,不提示:没哟掉用基类的OnDestroy,我已经手动去 调用OnDestroy注意:没哟内存泄露,但是只要程序结束的时候,输出会有个 提示
      void CDockablePaneAsContainer::OnDestroy()
    {
    CDockablePane::OnDestroy(); if(m_pDialog->GetSafeHwnd())
    {
    // m_pDialog->DestroyWindow(); //delete m_pDialog; //m_pDialog=NULL; ::SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_MYMESSAGE,0,0);
    }
    //加代码,发送消息给主窗口,让其销毁:CMyDialog,注意,实验证明:m_pDialog只是指着CDockableForm!!!!!
    }
    让 主窗口去销毁对话框
    CDockablePaneAsContainer::OnDestroy()只是销毁面板容器LRESULT CMainFrame::OnMyMessage(WPARAM wParam,LPARAM  lParam)
     {
    // pdlg->DestroyWindow();  delete pdlg;  pdlg=NULL;  return 1; }
    CMyDialog::~CMyDialog()
    { if(GetSafeHwnd())
      OnDestroy();
    }
    代码dubug发现,可以运行到:
    if(GetSafeHwnd())
      OnDestroy();
    tmd 还提示 继承类没有调用OnDestroy 
    受不了 了
      

  18.   

    看CDocableForm的完成代码:
    .h#pragma once
    #include "afxwin.h"class CDockablePaneAsContainer : public CDockablePane
    {
    public:
    CDockablePaneAsContainer(CDialog* pDialog) : m_pDialog(pDialog) { }private:
    CDialog* m_pDialog;public:
    DECLARE_MESSAGE_MAP()
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnDestroy();
    };class CDockableForm : public CDialog
    {
    public:
    CDockableForm(UINT nIDTemplate); virtual BOOL Create(LPCTSTR lpszCaption, CWnd* pParentWnd, const RECT& rect, BOOL bHasGripper, UINT nID, DWORD dwStyle, DWORD dwTabbedStyle = AFX_CBRS_REGULAR_TABS, DWORD dwControlBarStyle = AFX_DEFAULT_DOCKING_PANE_STYLE, CCreateContext* pContext = NULL); CDockablePane& GetDockablePane() { return m_wndPane; }private:
    CDockablePaneAsContainer m_wndPane;
    };.cpp#include "StdAfx.h"
    #include "DockableForm.h"
    BEGIN_MESSAGE_MAP(CDockablePaneAsContainer, CDockablePane)
    ON_WM_SIZE()
    ON_WM_DESTROY()
    END_MESSAGE_MAP()void CDockablePaneAsContainer::OnSize(UINT nType, int cx, int cy)
    {
    CDockablePane::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码
    if (m_pDialog->GetSafeHwnd())
    {
    CRect rc;
    GetClientRect(rc);
    m_pDialog->MoveWindow(rc);
    }
    }void CDockablePaneAsContainer::OnDestroy()
    {
    CDockablePane::OnDestroy(); // TODO: 在此处添加消息处理程序代码
    m_pDialog->DestroyWindow();
    }CDockableForm::CDockableForm(UINT nIDTemplate)
    : CDialog(nIDTemplate, &m_wndPane)
    , m_wndPane(this)
    {
    }BOOL CDockableForm::Create(LPCTSTR lpszCaption, CWnd *pParentWnd, const RECT &rect, BOOL bHasGripper, UINT nID, DWORD dwStyle, DWORD dwTabbedStyle, DWORD dwControlBarStyle, CCreateContext *pContext)
    {
    m_wndPane.Create(lpszCaption, pParentWnd, rect, bHasGripper, nID, dwStyle, dwTabbedStyle, dwControlBarStyle, pContext);
    CDialog::Create(m_nIDHelp, &m_wndPane);
    SetParent(&m_wndPane);
    ShowWindow(SW_SHOW);
    return TRUE;
    }
      

  19.   


    直接在PostNCDestroy中delete this,这个我比较少这样用,不知道对话框这样delete会不会还会调用他的destroywindow来销毁这个对话框。一般都是哪里new出来,哪里delete,哪里create哪里destroywindow,这样可以减少一些错误
    至于你说不知道面板对象不知会不会进入销毁,这个从逻辑上说一定要有才行的,没有的话肯定也是哪里出问题了,不行的话就在其他地方显式的调用咯。我看你的对话框指针是别的地方传过来的。你就在传进来的那个地方销毁就可以了
    原来我就经常碰到重复销毁出错的情况,这样规范一下后就比较少了你可以各种方法都调试一下,找准原因,找到一个适合你自己的
      

  20.   

    谢谢  这个问题迄今为止,没有解决内存没有泄露也没有其他问题,,暂且到这里吧,希望有高手,或者自己哪天找到法子进行根除掉。对于 面板中的m_pDialog这个指针,  有人指出是多态,,,指着:CMyDialog我不赞同!!!
    希望有人对继承这一块把握深刻的人, 帮忙解答一下,,,这个指针,真不好分析。。 比书上那些复杂多了多态一般是这样用的:A *p;p=&b;p->虚函数
    现在的情况不一样!!! 具体看 构造函数:CDockableForm::CDockableForm(UINT nIDTemplate)
      : CDialog(nIDTemplate, &m_wndPane)
      , m_wndPane(this)
    {
    }
    当 终极派生类构造的时候,就会调用 CDockableForm的构造函数,,CDockableForm的构造函数又会调用CDialog构造函数,CDialog 构造函数完毕之后回到CDockableForm的构造函数,最后回到CMyDialog的构造函数然后 去初始化 它所包含的成员对象:m_wndPane!!!
    m_wndPane中包含了 CMyDialog的指针m_pDialog!!!!问题对于我来说,有些复杂吃力请赐教,,,,,1.我分析的对不对? 2.m_pDialog
    最终只着谁? 问什么指着它??
      

  21.   

    帮助文件“TN017: Destroying Window Objects”
      

  22.   

    或许自己过于钻了
    会了验证 一些网友的意见,或者自己无形中产生的一些概念吧我尝试过,在父类中 PostNcDestroy是正确的。三个类,继承路线即:CBDialog----CADialog--------CMyDialog--------CDialog现在 大家说说, 创建非模式对话框,用的是:CBDialog类,但是却在其直接基类CADialog中进行了PostNCDestroy,DestroyWindow。。销毁非模式对话框, 我是没有发现有什么问题,但是有几个c++中的继承概念不清
    看代码://MyDialog.h
    class CMyDialog : public CDialog
    {
    // Construction
    public:
      CMyDialog(UINT,CWnd* pParent = NULL);   
           UINT id;   //对话框id
    .......................
    };构造函数:
    CMyDialog::CMyDialog(UINT ID,CWnd* pParent /*=NULL*/)
    : CDialog(ID, pParent),id(ID)
    {}
    看代码://ADialog.h#include"MyDialog.h"class CADialog : public CMyDialog
    {
    // Construction
    public:
    CADialog(UINT ID,CWnd* pParent = NULL);   // standard constructor protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    virtual void PostNcDestroy();

    protected: afx_msg void OnClose();

    DECLARE_MESSAGE_MAP()
    };看 CBDialog的基类的 重要代码:
    void CADialog::OnClose() 
    {
    // TODO: Add your message handler code here and/or call default

    //CDialog::OnClose();
    DestroyWindow();

    }void CADialog::PostNcDestroy() 
    {

    CDialog::PostNcDestroy();delete this;}
    //BDialog.h
    #include "ADialog.h"class CBDialog : public CADialog
    {
    public:
    CBDialog(CWnd* pParent = NULL);   // standard constructor
    enum { IDD =IDD_DIALOG1};
    ...........
    };
    CBDialog这个类对应这个一个对话框,当对话框销毁的时候, 代码运行到CBDialog::PostNcDestroy中它由于继承的是CADialog, 所以代码和CADialog::PostNcDestroy() 一模一样,,大家说说看,这个this是谁??????是CADialog??? CBDialog???  是CBDialog才对。。基类CADialog的一切public, protected,都给了派生类CBDialog。。试验证明,我的分析是对的, vc6,vs2010 都通过,没有任何异常问题出现可是为什么主帖中却出现了 那个诡异的警告呢??很可能是 面板容器 有些诡异。。在基类中调用DestroyWindow是完全可以的DestroyWindow是一个虚函数。主帖中:CMyDialog构造函数被调用的时候,它立即构造一个CDockableForm对象,,CDockableForm的构造函数,将 this传给了 它的成员对象。(初始化这个成员对象)这里的这个this 指针,  是CMyDialog的 基类的 地址。CMyDialog的地址和基类CDockableForm的 地址完全一致,至少这里是一样的
      

  23.   

    void CMyDialog::PostNcDestroy()
      {
          CDockableForm::PostNcDestroy(); //去掉这句试试
          delete this;  //销毁对话框
      }
      

  24.   

    void CMyDialog::PostNcDestroy()
      {
          CDockableForm::PostNcDestroy();//去掉这句试试
          delete this;  //销毁对话框
      }
      

  25.   

    试验证明:  只要我在 CMainFrame的析构函数中: delete 对话框
    那么没有 一下警告如果我把delete 对话框 移植到其他地方,立即有警告,但是内存没有泄露。比如在CMyDialog::PostNcDestroy中 //CDockableForm::PostNcDestroy();
    ::SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_MYMESSAGE,0,0);
    发送一个自定义消息给 主框架让主框框中 delete 对话框但是依然会有提示只有把delete  对话框放在 析构函数中,就没有这个警告了calling DestroyWindow in CWnd::~CWnd; OnDestroy or PostNcDestroy in derived class will not be called.
    我的猜测: 这个警告不用理会。  由编译器扯淡去吧
    谢谢各位, 时间到,我就结贴
      

  26.   

    up up up 期待高手高手