Step out of the MFC subclass maze

Source: Internet
Author: User
Tags 04x
Many Windows programmers skip the SDK and directly use the RAD development tool [or VC. I think VC should not belong to RAD]. Some may be unfamiliar with the subclass mechanism.

Let's first look at what is Windows subclass. Windows defines many general controls for us or for them, such as Edit, ComboBox, ListBox ...... Etc. These controls have rich functions and can bring us a lot of development work. Imagine how difficult it is to implement an EDIT control by ourselves! However, in actual development, there are still some situations where these standard controls are powerless, such: in our application, an EDIT request is required to get the instructor's comments on students A, B, and C [do not tell me that you want to use ComboBox to implement J]. At this time, what should I do if I want to prohibit the input of other letters and numbers in Edit? The EDIT control does not provide such a mechanism, so we can use subclass to solve such problems.

We know that every Windows window [here is EDIT] has a window processing function responsible for message processing, the subclass method is to use our own message processing functions to replace the original and standard processing functions of the window. Of course, our own window handler function only cares about specific messages [WM_CHAR here of course], and other messages are sent to the original window handler for processing. The implementation method in the SDK is to call the SetWindowLong function:

WNDPROC * oldWndProc = (WNDPROC) SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) AfxGetAfxWndProc ());

AfxGetAfxWndProc () is our own window processing function, after processing the messages we are interested in, we may use the returned original window processing function pointer oldWndProc to process other messages in a standard way. For details, refer to relevant materials.

However, in the "time" of MFC, everything was packaged. The original window class registration and window functions were gone [or invisible]. I would like to learn more about the subclass mechanism of MFC for programmers who are "Exploring" themselves, hoping to give some inspiration to everyone.

We first use MFC to implement the requirements mentioned above: one can only enter the EDIT Control of A, B, and C.

The startup interface is as follows:

Only A, B, and C can be entered, and only one letter can be entered.

Implementation Method:

Create a class CsuperEdit, Ctrl + W, process WM_CHAR in it, and then edit the message processing function:

 

Void CSuperEdit: OnChar (UINT nChar, UINT nRepCnt, UINT nFlags)

{

// TODO: Add your message handler code here and/or call default

TCHAR ch [20];

GetWindowText (ch, 20 );

If (strlen (ch) = 1 & (nChar <= 'C' & nChar> = 'A '))

Return;

If (nChar! = 'A'

& NChar! = 'B'

& NChar! = 'C'

)

Return;

CEdit: OnChar (nChar, nRepCnt, nFlags );

}

 

Then add a data member CsuperEdit m_edit to the Cprog1Dlg class and add the following to CProg1Dlg: OnInitDialog:

M_edit.SubclassDlgItem (IDC_EDIT1, this );

M_edit.SetWindowText ("<enter A, B, C> ");

And process the notification message sent by EDIT to DIALOG: EN_SETFOCUS:

Void CProg1Dlg: OnSetfocusEdit1 ()

{

// TODO: Add your control notification handler code here

M_edit.SetWindowText ("");

M_edit.SetFocus ();

}

 

OK, everything is done! How easy it is to compare with the SDK subclass method!

Let's take a look at what we did with MFC! Here we mainly solve two problems that are easy to confuse beginners:

1. m_edit is just a defined C ++ Class Object. Why can I use it to call its member function SetWindowText to control the control with the resource number IDC_EDIT1 in our program?

2. Why can the CSuperEdit class process WM_CHAR messages?

 

We all know that Windows, controls, resources ...... All are implemented through their handles, such
HHANDLE, HWND, and HDC are all handles, which are represented as a 32-bit long integer data stored in a specific area in Windows, we can understand it as pointing to the index of windows, controls, and resources we want to control. With it, we can control the objects we want to control.

Here you can think of why most API functions have an HWND hwnd parameter!

BOOL SetWindowText (
HWND hWnd, // handle to window or control
Lptstr lpString // title or text
);
The C ++ variable m_edit also uses its handle to control IDC_EDIT1, but how is this implemented? You may have noticed m_edit.SubclassDlgItem (IDC_EDIT1, this); By the way, this is the key!

F9 sets the breakpoint here. After F5, the program will arrive here. F11 follows the SubclassDlgItem function:

BOOL CWnd: SubclassDlgItem (UINT nID, CWnd * pParent)

{

ASSERT (pParent! = NULL );

ASSERT (: IsWindow (pParent-> m_hWnd ));

 

// Check for normal dialog control first

HWND hWndControl =: GetDlgItem (pParent-> m_hWnd, nID );

If (hWndControl! = NULL)

Return SubclassWindow (hWndControl );

 

# Ifndef _ AFX_NO_OCC_SUPPORT

If (pParent-> m_pCtrlCont! = NULL)

{

// Normal dialog control not found

COleControlSite * pSite = pParent-> m_pCtrlCont-> FindItem (nID );

If (pSite! = NULL)

{

ASSERT (pSite-> m_hWnd! = NULL );

VERIFY (SubclassWindow (pSite-> m_hWnd ));

 

# Ifndef _ AFX_NO_OCC_SUPPORT

// If the control has reparented itself (e.g., invisible control ),

// Make sure that the CWnd gets properly wired to its control site.

If (pParent-> m_hWnd! =: GetParent (pSite-> m_hWnd ))

AttachControlSite (pParent );

# Endif //! _ AFX_NO_OCC_SUPPORT

 

Return TRUE;

}

}

# Endif

 

Return FALSE; // control not found

}

When the code starts, check the input parent window, and then

HWND hWndControl =: GetDlgItem (pParent-> m_hWnd, nID );

If (hWndControl! = NULL)

Return SubclassWindow (hWndControl );

This is the key code. First Use hWndControl to get the handle of our IDC_EDIT1 control, and then call

SubclassWindow function. This function is the key to implementation. Let's take a look at what it has done:

 

 

 

BOOL CWnd: SubclassWindow (HWND hWnd)

{

If (! Attach (hWnd ))

Return FALSE;

 

// Allow any other subclassing to occur

PreSubclassWindow ();

 

// Now hook into the afx wndproc

Wndproc * lplpfn = getsuperwndprocaddr ();

Wndproc oldwndproc = (wndproc): setwindowlong (hwnd, gwl_wndproc, (DWORD) afxgetafxwndproc ());

Assert (oldwndproc! = (Wndproc) afxgetafxwndproc ());

 

If (* lplpfn = NULL)

* Lplpfn = oldwndproc; // The first control of that type created

# Ifdef _ debug

Else if (* lplpfn! = Oldwndproc)

{

Trace0 ("error: trying to use subclasswindow with incorrect cwnd/N ");

Trace0 ("/tderived class./N ");

TRACE3 ("/thWnd =$ % 04X (nIDC =$ % 04X) is not a % hs./n", (UINT) hWnd,

_ AfxGetDlgCtrlID (hWnd), GetRuntimeClass ()-> m_lpszClassName );

ASSERT (FALSE );

// Undo the subclassing if continuing after assert

: SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) oldWndProc );

}

# Endif

 

Return TRUE;

}

 

The Attach function is as follows:

BOOL CWnd: Attach (HWND hWndNew)

{

ASSERT (m_hWnd = NULL); // only attach once, detach on destroy

ASSERT (FromHandlePermanent (hWndNew) = NULL );

// Must not already be in permanent map

 

If (hWndNew = NULL)

Return FALSE;

 

CHandleMap * pMap = afxMapHWND (TRUE); // create map if not exist

ASSERT (pMap! = NULL );

 

PMap-> SetPermanent (m_hWnd = hWndNew, this );

 

# Ifndef _ AFX_NO_OCC_SUPPORT

AttachControlSite (pMap );

# Endif

 

Return TRUE;

}

 

Here we need to describe pMap-> SetPermanent (m_hWnd = hWndNew, this, it assigns the handle of IDC_EDIT1 to the data member m_hWnd of the class CsuperEdit [don't forget that our CsuperEdit class is derived from Cedit]. You may have vaguely understood something, which is good, in m_edit.SetWindowText ("<enter A, B, C>"), IDC_EDIT1 is controlled through m_hWnd, the data member:

Void CWnd: SetWindowText (LPCTSTR lpszString)

{

ASSERT (: IsWindow (m_hWnd ));

 

If (m_pCtrlSite = NULL)

: SetWindowText (m_hWnd, lpszString );

Else

M_pCtrlSite-> SetWindowText (lpszString );

}

Other CEdit functions are encapsulated around the "m_hWnd + API function.

The common DDX_Control method calls SubclassWindow.

 

How is it? Do you understand the ins and outs of the first question?

 

Now let's look at the second question: why can the CSuperEdit class process WM_CHAR messages?

Some may be confused. Although m_edit controls IDC_EDIT through the handle, the messages sent to it still go to the standard processing function of EDIT, how does one implement WM_CHAR processing?

If the message still runs to the standard processing function of EDIT, it cannot be processed! I wonder if you have seen such a short section in the SubclassWindow function above. I added the key mark:

// Now hook into the AFX WndProc

WNDPROC * lplpfn = GetSuperWndProcAddr ();

WNDPROC oldWndProc = (WNDPROC): SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) AfxGetAfxWndProc ());

ASSERT (oldWndProc! = (WNDPROC) AfxGetAfxWndProc ());

 

If (* lplpfn = NULL)

* Lplpfn = oldWndProc; // the first control of that type created

Then we can relate to the subclass mechanism in the SDK we started to talk about. Do you understand? MFC is here to start a day-over-the-clock scam!

The AfxGetAfxWndProc () function is as follows:

 

 

Wndproc afxapi AfxGetAfxWndProc ()

{

# Ifdef _ AFXDLL

Return AfxGetModuleState ()-> m_pfnAfxWndProc;

# Else

Return & AfxWndProc;

# Endif

}

I have read Mr. Hou Jie's article "let's get down to MFC" and I wonder if I still remember that the command Routing Mechanism of MFC started with this function!

In this way, when the program receives the WM_CHAR sent to Edit, it should have called the EDIT standard window processing function. Now it is changed to calling lresult callback AfxWndProc (HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, then the WM_CHAR message is sent to a series of dashes, and finally it reaches our processing function CSuperEdit: OnChar (UINT nChar, UINT nRepCnt, UINT nFlags ), for more information about how to stream and how to get to the system, see [For more information, see MFC. [if your book is in a traditional Chinese version, read it on the 566 page].

 

Finally, we walked out of the FMC subclass maze.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.