Detailed explanation of SubClassWindow

Many Windows programmers skip the SDK and go directly to the RAD development tools [or VC, I think VC should not belong to RAD], and some people may be relatively unfamiliar with the subclassing mechanism. Let's first look at what subclassing of Windows is.
Windows has defined many rich common controls for us or for itself, such as Edit, ComboBox, ListBox, etc. These controls are rich in functions and can bring great aspects to our development work. Just imagine: we are only ourselves How hard is it to implement an EDIT control! However, in actual development, there are still some situations where these standard controls are powerless. For example, in our application, an EDIT is required to get the teacher's evaluation of students A, B, and C [don't tell me you want to use ComboBox to implement J], which When it is required to prohibit the input operation of other letters and numbers in Edit, what should I do? The EDIT control itself does not provide this mechanism, and we can use subclassing to solve such problems well.
We know that each Windows window [EDIT here] has a window handler function responsible for message processing. The way to subclass is to replace the original, standard handler function of the window with our own message handler function. Of course, our own window processing function only cares about those specific messages [here, of course, WM_CHAR], and other messages are sent to the original window function for processing.
The implementation method in the SDK is to call the function SetWindowLong:
WNDPROC * oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)AfxGetAfxWndProc()); where AfxGetAfxWndProc() is our own window processing function, in which we have dealt with After the message of interest, it is possible to process other messages according to the standard method through the returned original window processing function pointer oldWndProc. For details, please refer to the relevant information.
But in the "era" of MFC, everything has been packaged up, and the original window class registration and window functions are gone [or incognito]. The subclassing mechanism, I made a summary of my own "exploration", hoping to give you some inspiration.
We first use MFC to achieve the requirements I mentioned above: an EDIT control that can only input A, B, and C. The interface at startup is as follows: When inputting, only A, B, C can be input, and only one letter is allowed.
Implementation method: First derive a own class CsuperEdit, after 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 our Cprog1Dlg class, and enter in CProg1Dlg::OnInitDialog(): m_edit.SubclassDlgItem(IDC_EDIT1,this);
m_edit.SetWindowText("<please input 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 is this compared to the SDK's subclassing method! Let's see what MFC has done behind our backs! Here are mainly to solve two problems that are easy for beginners to be confused:
1. m_edit is just a C++ class object defined by us. Why can we call its member function SetWindowText to control the control whose resource number is: IDC_EDIT1 in our program?
2. Why can the CSuperEdit class handle WM_CHAR messages? As we all know, the control of Windows windows, controls, resources... is achieved through their handles, such as HHANDLE, HWND, HDC are handles, it is represented as a 32-bit long integer data, stored in a specific area in Windows, We can understand it as an index to the window, control, resource we want to control, and with it, we can control the object we want to control. Here you can think why most API functions have a parameter HWND hwnd!
BOOL SetWindowText( HWND hWnd, // handle to window or control LPCTSTR lpString // title or text );
Our C++ variable m_edit wants to control IDC_EDIT1, but also through its handle, but how is this achieved? You may have noticed the m_edit.SubclassDlgItem(IDC_EDIT1,this); sentence, yes, this is the key! F9 sets a breakpoint here, after F5, the program reaches here, and 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 } 代码开始时对传入的父窗口做些检查,然后就是
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 the SubclassWindow function, this function is the key to the implementation, let's see what it does:
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)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (INT_PTR)AfxGetAfxWndProc());
ASSERT(oldWndProc != AfxGetAfxWndProc());
if (*lplpfn == NULL) *lplpfn = oldWndProc; // the first control of that type created
#ifdef _DEBUG
else if (*lplpfn != oldWndProc)
{
TRACE(traceAppMsg, 0, “Error: Trying to use SubclassWindow with incorrect CWnd/n”);
TRACE(traceAppMsg, 0, “/tderived class./n”);
TRACE(traceAppMsg, 0, “/thWnd =   %08X) is not a %hs./n”,
(UINT)(UINT_PTR)hWnd, _AfxGetDlgCtrlID(hWnd), GetRuntimeClass()->m_lpszClassName);
ASSERT(FALSE); // undo the subclassing if continuing after assert
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (INT_PTR)oldWndProc);
}
#endif return TRUE;
}
函数Attach内部如下:
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;
}
What we want to explain here is
pMap->SetPermanent(m_hWnd = hWndNew, this); one sentence, it assigns the handle of our IDC_EDIT1 to the data member m_hWnd of the class CsuperEdit [Don't forget Since our CsuperEdit class is derived from Cedit],
you may have vaguely understood something now, yes, in m_edit.SetWindowText("<please input A, B, C>"); it is through this data member m_hWnd Implement control of IDC_EDIT1:
void CWnd::SetWindowText(LPCTSTR lpszString)
{
ASSERT(::IsWindow(m_hWnd));
if (m_pCtrlSite == NULL) ::SetWindowText(m_hWnd, lpszString);
else m_pCtrlSite->SetWindowText(lpszString) ;
}
The functions of other CEdit classes are also wrapped around "m_hWnd + API functions". And our commonly used DDX_Control method is to call SubclassWindow after all. how about it? Do you understand the ins and outs of the first question?
Now look at the second question: why can the CSuperEdit class handle WM_CHAR messages? Some friends may be puzzled now. Although the control of IDC_EDIT by m_edit is realized through the handle, the message sent to it still goes to the standard processing function of EDIT. How is the processing of WM_CHAR realized? If the message still goes to the standard processing function of EDIT, of course it cannot be processed! I don't know if you have seen such a small section in the SubclassWindow function above and I have highlighted it: // 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 and let's start To link the subclassing mechanism in the SDK, understand? MFC here unknowingly engaged in stealing activities! The AfxGetAfxWndProc() function is like this: WNDPROC AFXAPI AfxGetAfxWndProc() { #ifdef _AFXDLL return AfxGetModuleState()->m_pfnAfxWndProc; #else return &AfxWndProc; #endif } Friends who have read Mr. Hou Jie's "Introduction to MFC" don't know if they still remember MFC The command-routing mechanism of . is based on this function! In this way, when the program receives the WM_CHAR sent to Edit, it should call the EDIT standard window processing function, but now it is changed to call LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam), and then the WM_CHAR message goes through a series of flows, and finally successfully reaches our processing function CSuperEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags), as for how it flows and how to reach it Please refer to "MFC in simple language" [if your book is a traditional electronic version, please read from page 566]. Finally, we are out of the MFC subclassing maze. CSDN Roasted Chicken Wings 2002-12-3 [FINISH] PS: I have little talent and knowledge. If there is anything that makes people "grin with their teeth", be sure to let me know.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325988054&siteId=291194637