父窗口与物主窗口_(小经验总结)

父窗口与物主窗口的区别
本文详细解析了Windows窗口中的父/子窗口与物主/从属窗口的区别,包括移动、显示位置、销毁和隐藏等操作的不同行为,并提供实例说明。

父窗口与物主窗口

 

 

windows形形色色的窗口家族中,每个窗口也只能是三种基本类型之一:

1.    重叠窗口(Overlapped

2.    弹出窗口(Pupup

3.    子窗口    Child

当调用CWndCreate/CreateEx或是CDialogCreate创建一个基于以上三种类型之一的窗口时,会要求同时指定ParentpParentWnd参数(以下统称Parent)。这其中涉及到了/子窗口(CHILD & PARENT) 物主/从属窗口(CHILD & OWNER) 的概念差别。个人理解就像是父亲/儿子 监护人/养子 对之间的关系。其具体差别请参考以下行为列表。

 

1.    /子窗口

若新创建的窗口类型为子窗口,则参数Parent须指定为其父窗口,且不能为NULL.

 

2.    物主/从属窗口

若新创建的窗口为重叠窗口或弹出窗口,参数Parent指定为其物主窗口,其值为NULL时,物主窗口为桌面窗口。

 

 

/子窗口

 

物主/从属窗口

 

移动窗口

 

子随父

 

分离(两者无相互影响)

显示位置

子在父内

分离

销毁窗口

子随父

从属随物主

隐藏

子随父

分离

父最小化

子被隐藏

从属被隐藏

子最小化

分离

分离

 

父/子窗口示例子窗口无法“逃离”到父窗口之外

 

 

物主/从属窗口示例从属窗口位置不受物主窗口约束

 

 

 

注:

1.   一个子窗口可能是另一个子窗口的父窗口,但绝不可能是一个物主窗口。如果试图使一个子窗口成为一个物主窗口,那么系统只能使那个子窗口的最顶层窗口作为物主窗口。

2.   CWnd::SetParent 可改变父窗口。

3.   CWnd::SetOwner 可改变物主窗口。

 

 

 

应用:

         在基于对话框的应用程序中,若需要在多个对话框之间进行切换,可建立一个父窗口并将其它所有窗口作为其子窗口嵌入其中进行管理控制,以避免个对话框之间移动等问题。

 

 

 

 

 

 

 

<think> 我们正在处理一个MFC C++问题:从子窗口打开父窗口的DoModal窗口,关闭后报iswindows异常。 根据经验,这个异常通常是因为窗口对象被提前销毁导致的。具体来说,当你在子窗口中打开一个父窗口的模态对话框(使用DoModal),关闭这个模态对话框后,如果父窗口已经被销毁或者处于无效状态,那么在DoModal返回后可能会触发这个异常。 常见的场景是:子窗口本身可能是父窗口创建的一个非模态对话框。当我们从子窗口调用父窗口的DoModal方法时,如果子窗口父窗口之前被销毁,那么父窗口的DoModal返回后,父窗口可能已经被销毁,从而导致访问无效。 解决方案: 1. 确保在打开父窗口的模态对话框时,父窗口仍然有效(即没有被销毁)。 2. 检查生命周期管理,确保在父窗口存在的情况下才调用其DoModal方法。 3. 考虑使用消息机制来通知父窗口打开模态对话框,而不是直接在子窗口中调用父窗口的方法。 具体步骤: - 在子窗口中,不要直接持有父窗口的指针,而是通过安全的方式(比如发送消息)让父窗口自己打开模态对话框。 - 或者,在子窗口中获取父窗口指针时,使用MFC提供的安全方法(如GetParent()或GetTopLevelParent()),并确保在使用前检查指针是否有效(使用IsWindow函数)。 示例代码: 假设子窗口是一个CDialog,父窗口是主框架窗口(CMainFrame)。在子窗口中,可以这样通知父窗口: 在子窗口中: ```cpp CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd(); if (pMainFrame && ::IsWindow(pMainFrame->m_hWnd)) { pMainFrame->OpenModalDialog(); // 假设这是父窗口中定义的打开模态对话框的函数 } ``` 但是,更安全的做法是发送消息: 在父窗口(比如CMainFrame)中定义消息处理函数: ```cpp // MainFrm.h afx_msg void OnOpenModalDialog(); // MainFrm.cpp ON_MESSAGE(WM_USER_OPEN_MODAL, OnOpenModalDialog) void CMainFrame::OnOpenModalDialog() { CMyDialog dlg; dlg.DoModal(); } ``` 在子窗口中发送消息: ```cpp CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd(); if (pMainFrame && ::IsWindow(pMainFrame->m_hWnd)) { pMainFrame->PostMessage(WM_USER_OPEN_MODAL); } ``` 这样,模态对话框的创建和显示完全由父窗口自己处理,避免了子窗口持有父窗口指针可能导致的野指针问题。 另外,还有一种情况:子窗口父窗口创建的非模态对话框,当父窗口关闭时,子窗口应该被关闭。如果子窗口还在尝试打开父窗口的模态对话框,就会有问题。因此,需要确保在父窗口关闭前,子窗口已经关闭。 总结: 这个问题的核心是窗口生命周期管理。确保在访问窗口句柄之前,窗口仍然有效。使用消息机制可以解耦,避免直接访问可能已经销毁的窗口。 §§相关问题§§ 1. MFC中如何安全地获取父窗口指针? 2. MFC中如何发送自定义消息? 3. 如何避免窗口对象被提前销毁? 4. MFC模态对话框和非模态对话框的区别是什么? 5. 在MFC中,如何处理窗口关闭的顺序? </think> ### MFC C++中子窗口打开父窗口模态对话框后报iswindows异常的解决方案 该异常通常由窗口句柄失效或线程同步问题引起。以下是具体原因和解决方案: #### 核心原因分析 1. **父窗口提前销毁** 子窗口调用父窗口的`DoModal()`时,若父窗口已被销毁但未置空句柄,会导致访问无效窗口句柄[^1]。 2. **消息队列冲突** 模态对话框阻塞消息循环时,父窗口的关闭消息可能被异常处理[^2]。 3. **句柄悬挂问题** 使用`GetParent()`获取的指针可能在对话框关闭后失效(常见于多文档MDI应用)。 --- ### 解决方案 #### 方法1:安全调用父窗口(推荐) ```cpp // 在子窗口代码中 CWnd* pParent = AfxGetMainWnd(); // 获取主框架窗口 if (pParent && ::IsWindow(pParent->m_hWnd)) { CMyParentDialog dlg(pParent); dlg.DoModal(); } else { AfxMessageBox(_T("父窗口无效")); } ``` > **关键点**: > - 使用`AfxGetMainWnd()`替代`GetParent()`确保获取顶级窗口 > - `::IsWindow()`验证句柄有效性 #### 方法2:异步打开对话框 通过PostMessage避免直接调用: ```cpp // 子窗口.h #define WM_OPEN_PARENT_DLG (WM_USER + 100) // 子窗口.cpp void CChildDlg::OnOpenParent() { GetParent()->PostMessage(WM_OPEN_PARENT_DLG); } // 父窗口消息映射 ON_MESSAGE(WM_OPEN_PARENT_DLG, OnOpenParentDlg) LRESULT CMainFrame::OnOpenParentDlg(WPARAM, LPARAM) { CMyDialog dlg; dlg.DoModal(); return 0; } ``` #### 方法3:生命周期管理 在父窗口关闭时主动释放子窗口: ```cpp // 父窗口关闭时 void CMainFrame::OnClose() { if(m_pChildDlg) { m_pChildDlg->DestroyWindow(); delete m_pChildDlg; m_pChildDlg = NULL; } CFrameWnd::OnClose(); } ``` --- ### 预防措施 1. **启用调试检查** 在`Debug`模式下添加: ```cpp afxDump.SetDepth(1); // 跟踪对象销毁 ``` 2. **使用智能指针** ```cpp std::unique_ptr<CMyDialog> pDlg = std::make_unique<CMyDialog>(this); if (pDlg) pDlg->DoModal(); ``` 3. **验证对话框数据** 在`PostNcDestroy()`中清理指针: ```cpp void CMyParentDialog::PostNcDestroy() { CDialog::PostNcDestroy(); if(m_pCaller) m_pCaller->m_pDlg = NULL; // 清除子窗口引用 } ``` > **典型错误场景**: > 子窗口使用`GetParent()->DoModal()`后立即访问父窗口成员变量,此时父窗口可能已被销毁[^2]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值