Destroying Window Objects

jianzhanger發表於2011-07-26

Destroying Window Objects(銷燬視窗物件)[1]

TN017: Destroying Window Objects

銷燬視窗物件

This note describes the use of the CWnd::PostNcDestroy member function. Use this function if you wish to do customized allocation of CWnd-derived objects.

This note also explains some of the reasons for the cardinal rule:

To destroy a C++ Windows object, use DestroyWindow not "delete".

This is important. If you follow the guidelines below, you will have few cleanup problems (such as forgetting to delete/free C++ memory, forgetting to free system resources like HWNDs, or freeing objects too many times).

此章節講述了CWnd::PostNcDestroy成員函式的使用。如果你想對派生自CWnd的物件做自定義的處理,你可以使用這個函式。

此章節也闡述了“要銷燬一個C++Windows物件,使用DestroyWindow而不是delete”這個最重要規則的原因。

這是非常重要的。如果你遵照以下的指導方法,你就會很少碰到清除方面的問題(例如忘記刪除/釋放C++記憶體,忘記釋放系統資源比如說HWNDs,或者釋放物件太多次)。

The Problem

Windows objects (objects of classes derived from CWnd) represent both a C++ object (allocated in the application's heap) and an HWND (allocated in system resources by the window manager). Since there are several ways to destroy a window object, we must provide a set of rules that prevent system resource or application memory leaks and that prevent objects and Windows handles from being destroyed more than once.

問題

Windows物件(CWnd派生類的物件)既代表一個C++物件(在應用程式的堆中分配)也代表了一個HWND(由視窗管理器在系統資源裡分配)。由於存在多種途徑來銷燬一個視窗物件,我們必須提供一組規則以防止系統資源或者應用程式的記憶體洩露問題,同時也防止物件和Windows控制程式碼被多次銷燬。

This is more than a memory management problem. The presence of a Windows window has user-interface impact: a window drawn on the screen; once it is destroyed there is also an impact on system resources. Leaking C++ memory in your application address space is not as bad as leaking system resources.

這不僅僅是一個記憶體管理的問題。一個Windows視窗的存在會對使用者介面產生影響:視窗繪製在螢幕上;一旦它被銷燬,同樣對系統資源也會產生影響。在你的應用程式地址空間裡洩漏C++記憶體並不會像洩漏系統資源那樣糟糕。

 

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Destroying Window Objects (銷燬視窗物件)[2]

 

Destroying Windows

The two permitted ways to destroy a Windows object are:

  • Calling CWnd::DestroyWindow or the Windows API ::DestroyWindow.
  • Explicit deletion with the delete operator.

The first case is by far the most common. This case applies even if DestroyWindow is not called directly by your code. The is the case when the user directly closes a frame window (the default WM_CLOSE behavior is to callDestroyWindow), and when a parent window is destroyed, Windows calls DestroyWindow for all the children.

The second case, the use of the delete operator on Windows objects, should be very rare and only in the cases outlined below.

銷燬視窗

有兩種方法被允許來銷燬一個Windows物件:

         呼叫CWnd::DestroyWindowWindows API ::DestroyWindow.

         利用delete操作符來進行明確的刪除工作。

第一種方法是迄今為止最常用的。即使DestroyWindow沒有在你的程式碼裡被直接呼叫,此方法也照常適用。這種情況就是,當使用者直接關閉一個框架視窗時(預設的WM_CLOSE行為主是呼叫DestroyWindow),當一個父視窗(框架視窗)被銷燬時,Windows會呼叫DestroyWindow來銷燬它的所有的子視窗

Auto Cleanup with CWnd::PostNcDestroy

When destroying a Windows window, the last Windows message sent to the window is WM_NCDESTROY. The defaultCWnd handler for that message (CWnd::OnNcDestroy) will detach the HWND from the C++ object and call the virtual function PostNcDestroy. Some classes override this function to delete the C++ object.

利用CWnd::PostNcDestroy進行自動清除

當銷燬一個Windows視窗時,最後傳送給此視窗的Windows訊息是WM_NCDESTROYCWnd對此訊息的預設處理(CWnd::OnNcDestroy)會將C++物件與HWND分離,並呼叫虛擬函式PostNcDestroy一些類過載這個函式來刪除C++物件。

The default implementation of CWnd::PostNcDestroy does nothing which is appropriate for window objects allocated on the stack frame or embedded in other objects. This is not appropriate for window objects that are designed to be allocated by themselves on the heap (not embedded in other C++ object).

CWnd::PostNcDestroy的預設操作是什麼也不做,這適合於那些分配在堆疊或者嵌在其他物件裡面的視窗物件。這不適合於那些設計來分配在堆上的視窗物件(不嵌在其他C++物件中)。

Those classes that are designed to be allocated by themselves on the heap override the PostNcDestroy member function to perform a "delete this". This statement will free any C++ memory associated with the C++ object. Even though the default CWnd destructor calls DestroyWindow if m_hWnd is non-NULL, this does not lead to infinite recursion since the handle will be detached and NULL during the cleanup phase.

那些設計來分配在堆上的類可以過載成員函式PostNcDestroy以執行“delete this”操作。它將會釋放任何與此C++物件相關的C++記憶體。儘管預設的CWnd解構函式會在m_hWnd不為空的情況下呼叫DestoryWindow,但這不會導致無窮遞迴,因為此控制程式碼在清除階段將會處於分離狀態併為空。

Note   CWnd::PostNcDestroy is normally called after the Windows WM_NCDESTROY message is processed, as part of window destruction, and the HWND and the C++ window object are no longer attached. CWnd::PostNcDestroy will also be called in the implementation of most Create calls if failure occurs (see below for auto cleanup rules).

注意CWnd::PostNcDestroy一般會在Windows訊息WM_NCDESTORY處理後被呼叫,把它作為視窗銷燬的一部分,同時HWNDC++視窗物件不再關聯。

CWnd::PostNcDestroy也會在大部分Create呼叫的執行部分被呼叫,如果錯誤發生的話(自動清理規則如下)

 

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Destroying Window Objects 銷燬視窗物件 [3]

 

Auto Cleanup Classes

The following classes are not designed for auto-cleanup. They are normally embedded in other C++ object or on the stack:

  • All the standard Windows controls (CStaticCEditCListBox, and so on).
  • Any child windows derived directly from CWnd (for example, custom controls)
  • Splitter windows (CSplitterWnd)
  • Default control bars (classes derived from CControlBar, see Technical Note 31 for enabling auto-delete for control bar objects)
  • Dialogs (CDialog) - designed for modal dialogs on the stack frame
  • All the standard dialogs except CFindReplaceDialog
  • The default dialogs created by ClassWizard

自動清理類

以下的這些類不是設計來做自動清理的。他們通常嵌在其他C++物件或者在堆疊上:

l         所有的標準Windows控制元件(CStaticCEditClistBox

l         所有從CWnd直接派生來的子視窗(比例,自定義控制元件)

l         拆分視窗(CSplitterWnd

l         預設控制條(從CcontrolBar派生的類,檢視TN31來了解能夠自動刪除的控制條物件)

l         對話方塊(CDialog)設計來在堆疊上建立模態對話方塊

l         所有的標準對話方塊,除了CfindReplaceDialog

l         ClassWizard建立的預設對話方塊

The following classes are designed for auto-cleanup. They are normally allocated by themselves on the heap:

  • Main frame windows (derived directly or indirectly from CFrameWnd)
  • View windows (derived directly or indirectly from CView)

以下這些類設計來做自動清理。他們一般單獨分配在堆上:

         主框架視窗(直接或間接派生於CFrameWnd

         檢視視窗(直接或間接派生於CView

 

 

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

TN017: Destroying Window Objects 銷燬視窗物件 [4]

 

If you wish to break any of these rules, you must override the PostNcDestroy member function in your derived class. To add auto-cleanup to your class, simply call your base class and then do a delete this. To remove auto-cleanup from your class, call CWnd::PostNcDestroy directly instead of the PostNcDestroy member in your direct base class.

如果你想打破任何一條規則,你就必須在你的派生類中過載PostNcDestroy成員函式。為了增加自動清理到你的類,只需要呼叫你的基類並做delete this操作。為了將自動清理從你的類中移出,直接呼叫CWnd::PostNcDestroy來代替你基類的成員函式PostNcDestory.

The most common use of the above is to create a modeless dialog that can be allocated on the heap.

以上內容常用在建立一個能在堆上分配的非模態的對話方塊

When to Call 'delete'

The recommended way to destroy a Windows object is to call DestroyWindow, either the C++ member function or the global ::DestroyWindow API.

何時呼叫delete

銷燬一個視窗最好是呼叫DestoryWindow,不管是C++的成員函式還是全域性的::DestoryWindow API.

Do not call the global ::DestroyWindow API to destroy an MDI Child window, use the virtual member functionCWnd::DestroyWindow instead.

不要呼叫全域性的API ::DestroyWindow來銷燬一個MDI子視窗,使用虛擬成員函式CWnd::DestroyWindow來代替它。

For C++ Window objects that don't perform auto-cleanup, using DestroyWindow instead of delete avoids problems of having to call DestroyWindow in the CWnd::~CWnd destructor where the VTBL is not pointing to the correctly derived class. This can lead to subtle bugs so the diagnostic (debug) version of MFC will warn you with

Warning: calling DestroyWindow in CWnd::~CWnd

   OnDestroy or PostNcDestroy in derived class will not be called

對於那些不執行自動清理的C++視窗物件,使用DestoryWindow來代替delete以避免你必須在CWnd::~CWnd解構函式中呼叫DestoryWindow的問題,而在此處VTBL並沒有指向正確的派生類。這可能會導致許多bugs,所以MFC診斷版本(除錯)中會警告你:

Warning: calling DestroyWindow in CWnd::~CWnd

   OnDestroy or PostNcDestroy in derived class will not be called

In the case of C++ Windows objects that do perform auto-cleanup, you must call DestroyWindow. If you use operatordelete directly, the MFC diagnostic memory allocator will alert you that you are freeing memory twice (the first call todelete as well as the indirect call to "delete this" in the auto-cleanup implementation of PostNcDestroy).

對於執行自動清理工作的C++Windows物件,你必須呼叫DestroyWindow。如果你直接使用操作符deleteMFC的診斷記憶體分配算符將會警告你:你正在第二次釋放記憶體(第一次呼叫delete,還有在PostNcDestroy的自動清理執行過程中呼叫delete this)。

After calling DestroyWindow on a non-auto-cleanup object, the C++ object will still be around, but m_hWnd will be NULL. After calling DestroyWindow on an auto-cleanup object, the C++ object will be gone, freed by the C++ delete operator in the auto-cleanup implementation of PostNcDestroy..

對於一個不執行自動清理的物件,在呼叫DestroyWindow之後,這個C++物件仍然存在,但是m_hWnd會為空。對一個執行自動清理的物件,在呼叫DestroyWindow之後,此C++物件就不存在了,它被PostNcDestroy的自動清理執行過程裡的delete操作符釋放。

相關文章