学习VC++一个月整,贴一篇学习笔记庆祝一下:)(100分)

  • 主题发起人 主题发起人 远帆
  • 开始时间 开始时间

远帆

Unregistered / Unconfirmed
GUEST, unregistred user!
如果不让用户在Edit中输入字母b,应该怎么做呢?
要实现这一目的大概有四个方法:
1、建立一个从CEdit继承下来的类CEditEx,在类中屏蔽输入字母b的消息。
a)映射WM_CHAR消息
void CEditEx::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar=='b') return;
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
b)建立控件ddx,将控件类型由CEdit改为CEditEx:CEditEx Edt2;

2、重载PreTranslateMessage方法,在其中屏蔽相应消息:
BOOL CchangeDlg::PreTranslateMessage(MSG* pMsg)
{
if ((pMsg->message==WM_CHAR)&&(pMsg->hwnd==Edt3.m_hWnd)
&&(LOWORD(pMsg->wParam)=='b')) return true;
return CDialog::PreTranslateMessage(pMsg);
}

3、使用子类化方法,使用新的消息处理过程取代原先的CEdit的消息处理过程:
a)声明:新旧处理过程
WNDPROC OldProc;
LRESULT CALLBACK EdtProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
b)在OnInitDialog中进行子类化:
OldProc=(WNDPROC)SetWindowLong(Edt1.m_hWnd,GWL_WNDPROC,(long)EdtProc);
c)在新的处理函数中屏蔽字母b的输入:
LRESULT CALLBACK EdtProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if (uMsg==WM_CHAR&&wParam=='b') return S_OK;
return OldProc(hwnd,uMsg,wParam,lParam);
}
使用子类化方法的关键是调用SetWindowLong方法。
LONG SetWindowLong(
HWND hWnd,
int nIndex,
LONG dwNewLong
);
nIndex:[in] Specifies the zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. To set any other value, specify one of the following values.
GWL_EXSTYLE:Sets a new extended window style. For more information, see CreateWindowEx.
GWL_STYLE:Sets a new window style.
GWL_WNDPROC:Sets a new address for the window procedure.
Windows NT/2000/XP: You cannot change this attribute if the windowdo
es not belong to the same process as the calling thread.
GWL_HINSTANCE:Sets a new application instance handle.
GWL_ID:Sets a new identifier of the window.
GWL_USERDATA:Sets the user data associated with the window. This data is intended for use by the application that created the window. Its value is initially zero.The following values are also available when the hWnd parameter identifies a dialog box.
DWL_DLGPROC:Sets the new address of the dialog box procedure.
DWL_MSGRESULT:Sets the return value of a message processed in the dialog box procedure.
DWL_USER:Sets new extra information that is private to the application, such as handles or pointers.

4、使用SubclassDlgItem方法:
a)添加由CWnd继承来的CSwnd类
class CSwnd:public CWnd
b)向CSwnd映射WM_CHAR消息,
void CSwnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar!='b') CWnd::OnChar(nChar, nRepCnt, nFlags);
}
c)在OnInitDialog中加入
Swnd.SubclassDlgItem(IDC_EDIT1,this);
还有一个与SubclassDlgItem类似的函数SubclassWindow,如果我们使用SubclassWindow,那么应该用
Swnd.SubclassWindow(GetDlgItem(IDC_BUTTON1)->m_hWnd);
代替上面的句子。
SubclassDlgItem与SubclassWindow有什么不同呢?下面是SubclassDlgItem的源码,位于wincore.cpp单元中。
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
}
可见SubclassDlgItem实际上是使用全局函数GetDlgItem得到了窗体句柄后调用SubclassWindow的。
那SubclassWindow函数又做了什么工作呢?
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 (nIDC=$%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;
}
这一段程序中关键部分应该就是用红色标注的SetWindowLongPtr调用。
LONG_PTR SetWindowLongPtr(
HWND hWnd,
int nIndex,
LONG_PTR dwNewLong
);
nIndex:[in] Specifies the zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. To set any other value, specify one of the following values.
GWL_EXSTYLE
GWL_STYLE
GWLP_WNDPROC
GWLP_HINSTANCE
GWLP_ID
GWLP_USERDATA
DWLP_DLGPROC
DWLP_MSGRESULT
DWLP_USER
与上面讲到的SetWindowLong系统函数并没有什么不同。看看其源码就会更清楚。
#ifdef SetWindowLongPtrA
#undef SetWindowLongPtrA
inline LONG_PTR SetWindowLongPtrA( HWND hWnd, int nIndex, LONG_PTR dwNewLong )
{
return( ::SetWindowLongA( hWnd, nIndex, LONG( dwNewLong ) ) );
}
#endif
#ifdef SetWindowLongPtrW
#undef SetWindowLongPtrW
inline LONG_PTR SetWindowLongPtrW( HWND hWnd, int nIndex, LONG_PTR dwNewLong )
{
return( ::SetWindowLongW( hWnd, nIndex, LONG( dwNewLong ) ) );
}
#endif
可见SubclassDlgItem使用的其实就是方法3的子类化方法,不过MFC帮我们包装了一下,更容易使用一点而已。

经常在源码中看到有人用SubclassDlgItem来做控件变量与控件关联的操作,而这通常由DDX来完成,那么DDX与SubclassDlgItem有什么关系吗?看看DDX_Control的源码(在dlgdata.cpp单元中)
void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd&
rControl)
{
if ((rControl.m_hWnd == NULL) &&
(rControl.GetControlUnknown() == NULL))
// not subclassed yet
{
ASSERT(!pDX->m_bSaveAndValidate);
pDX->PrepareCtrl(nIDC);
HWND hWndCtrl;
pDX->m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
if ((hWndCtrl != NULL) &&
!rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE);// possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
#ifndef _AFX_NO_OCC_SUPPORT
else
{
if (hWndCtrl == NULL)
{
if (pDX->m_pDlgWnd->GetOleControlSite(nIDC) != NULL)
rControl.AttachControlSite(pDX->m_pDlgWnd, nIDC);
}
else
{
// If the control has reparented itself (e.g., invisible control),
// make sure that the CWnd gets properly wired to its control site.
if (pDX->m_pDlgWnd->m_hWnd != ::GetParent(rControl.m_hWnd))
rControl.AttachControlSite(pDX->m_pDlgWnd);
}
}
#endif //!_AFX_NO_OCC_SUPPORT
}
}
看到以红色标注的SubclassWindow了吗?看来DDX的确使用了子类化技术,我们真是有先见之明*!*

与SubclassDlgItem、SubclassWindow相关的还有两个函数:
virtual void PreSubclassWindow();
HWND UnsubclassWindow();
UnsubclassWindow用于取消子类化,而PreSubclassWindow是一个虚函数,它并没有实现体,它的作用是让从CWnd继承下的类能够得到被子类化的“消息”,因为在SubclassWindow函数中我们对它进行了调用。
 
恭喜+领分!
 
如果用delphi实现的话,只需edit换成maskedit[:D]
鹅是vc++高手的崇拜者!
 
在delphi中,我们也可以用子类化技术来做,基本上是一样的。
 
是啊,学了VC后再回头去重新理解Delphi
感觉会把程序做的更好。把思路理的更清楚,从它RAD的表面进入实质
之后才可以说是慢慢理解了Delphi
 
子类化技术的一个很好的例子(控件)是XPMenu,核心就是替换WinProc。
不过这些东东到Linux下就全没有了。没有跨平台性。
所以学VC不要仅仅局限于MFC,应该深入STL,还有一些算法。
 
谢谢yhee指教。
 
恭喜[:D]
我现在正在考虑是学Java那 还是.NET
 
我也来学习学习。。。谢谢。。。
 
不错不错!收藏了
 
谢谢各位。
 
多人接受答案了。
 
后退
顶部