## 学习方法:
> 主要就是模拟开发,即使你的基础不牢,模拟训练反而有助于对前期的语法理解。API的量大,API比SOCKET要简单一些,因为直观,按钮,下拉。
### 1、windows资源:
> 常用的资源有对话框、菜单、图标、光标、字符串表、快捷键、位图等。
### 2、HWND与CWnd对象之间的转换:
1. 如果有CWnd对象如何获取这个对象内部的句柄?
```C++
pWnd ->m_hWnd
pWnd ->GetSafeHwnd();
CWnd wnd;
HWND h = wnd; // hwnd是指向cwnd对象的句柄,通过句柄可以操作控件
// cwnd包含了hwnd。
```
2. 如果有句柄HWND如何转为CWnd?
`static CWnd* CWnd::FromHandle(HWND hWnd);`
3. 注意:`GetDlgItem`内部都是靠`FromHandle`实现的。
4. 注意:`FromHandle`内部是有Map禁止一个句柄被多个对象包含。
就如同一个端口只能被一个进程占用,一个句柄只能有一个对象包含。
5. 注意:`FromHandle`的返回值只限于本函数使用,不可以保存在成员变量长期使用。
原因是不定期清理Map,参见:`CWnd::DeleteTempMap`
### 3、句柄嫁接与子类化:
1. `Attach`和`Detach`就是单纯的嫁接与分离函数。
对象一旦嫁接入一个句柄,就可以自由地调用CWnd或其派生类的功能。
2. 子类化`Subclass`内部包含`Attach`,额外再增加一个消息转拨到派生类(SubClass就是子类)
3. `SubClassWindow`函数内部核心功能就是Attach和`::SetWindowLongPtr`
4. `SubClassWindow()`必须与`UnsubclassWindow()`成对使用,如同Attach与Detach那样。
5. `SubClassDlgItem()`是把2个函数合成一个函数:`m_eye.SubclassDlgItem(IDC_SHOW, this)`; `m_eye.SubclassWindow(::GetDlgItem(m_hWnd, IDC_SHOW));`
6. 而且SubClassDlgItem不需要反子类化,可以不用调用UnsubclassWindow
### 4、总结嫁接与子类化:
1. Attach和Detach就是单纯的嫁接
2. 子类化Subclass内部包含Attach,增强了消息转移机制。
3. SubClassDlgItem简化了子类化功能,不需要反子类化UnsubclassWindow
4. 类向导中建立关联变量的方法(内部就是子类化)
DDX_Control函数内的核心内容是,引用类成员变量m_xxx,
```c++
{
pDX->m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
rControl.SubclassWindow(hWndCtrl);
}
void CXxxxxx::DoDataExchange(CDataExchange* pDX){
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST, m_list);
DDX_Control(pDX, IDC_PRIOR, m_combo);
}
```
附录:
```C++
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;
}
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 defined(_AFXDLL) || !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
CMFCControlContainer* pMFCCtrlContainer = pDX->m_pDlgWnd->GetMFCControlContainer();
if (pMFCCtrlContainer != NULL && pMFCCtrlContainer->IsSubclassedFeaturePackControl(hWndCtrl))
{
pMFCCtrlContainer->ReSubclassControl(hWndCtrl, (WORD)nIDC, rControl);
return;
}
#endif
if ((hWndCtrl != NULL) && !rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE); // possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
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);
}
}
}
}
```