Windows程序设计__孙鑫C++课程学习的反思
第一次看完孙鑫老师的视频,跟着他写代码能运行很多程序,但是过了几个月发现好多都忘记了,于是又重新学习了一遍,这次把20节课程的学习全都以博客日志的形式记录了下来,感觉学到了很多东西。观察孙老师的视频,千万不要跳着看,因为他是前后连续的,而且很多原理性的东西一跳而过,只抓程序,不重理论分析,在以后的程序设计时还会遇到同样的困惑,犯下同样的错误。
在这里,我感谢孙鑫老师,他讲授VC++课程的风格,我很欣赏。
孙鑫老师20节课程,给了我很大的启示,主要有以下几点:
1.理解windows程序的运行的消息机制。
消息机制在整个开发的过程中都是十分重要的,消息在实现函数功能时的具有重大的作用。
比如在RichEdit中实现一个文本的查找:
实现代码一 利用CRichEditCtrl提供的函数FindText:
//**********************************************************************************
FINDTEXTEX ft;
ft.chrg.cpMin = 0;
ft.chrg.cpMax = -1;
ft.lpstrText = (LPSTR) lpszmyString;
long n = pmyRichEditCtrl->FindText(FR_MATCHCASE|FR_WHOLEWORD, &ft);
if (n != -1)
pmyRichEditCtrl->SetSel(ft.chrgText);
实现代码二 利用RichEditCtrl提供的消息EM_FINDTEXT:
//**********************************************************************************
FINDTEXT ft;
ft.chrg.cpMin=0;
ft.chrg.cpMax=GetTextLength();
int len=text.GetLength();
ft.lpstrText=text.GetBuffer(len);//text is the string you search
Pos=SendMessage(EM_FINDTEXT,(WPARAM)FR_DOWN, (LPARAM)&ft);
if(-1==Pos)
{
nStartChar=Pos;
nEndChar=nStartChar+len;
GetRichEditCtrl().SetSel(nStartChar,nEndChar);
}
//**********************************************************************************
我们可以看到发送消息的方法和控件函数方法往往是等价的。
在看一下CRichEditCtrl类自身提供的一些函数实现:
//**********************************************************************************
//代码来自MFC 类库
int CRichEditCtrl::GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const
{
ASSERT(::IsWindow(m_hWnd));
*(LPINT)lpszBuffer = nMaxLength;
return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);
}
void CRichEditCtrl::GetSel(long& nStartChar, long& nEndChar) const
{
ASSERT(::IsWindow(m_hWnd));
CHARRANGE cr;
::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
nStartChar = cr.cpMin;
nEndChar = cr.cpMax;
}
CString CRichEditCtrl::GetSelText() const
{
ASSERT(::IsWindow(m_hWnd));
CHARRANGE cr;
cr.cpMin = cr.cpMax = 0;
::SendMessage(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
LPSTR lpsz = (char*)_alloca((cr.cpMax - cr.cpMin + 1)*2);
lpsz[0] = NULL;
::SendMessage(m_hWnd, EM_GETSELTEXT, 0, (LPARAM)lpsz);
return lpsz;
}
//**********************************************************************************
你要是查看该类的实现文件会发现,几乎大部分的函数都是发送消息的方式来完成操作。
至于WindowsHook编程中更是用到了消息截获的原理。而且用户可以自定义消息,或者注册消息完成某些函数功能。可见消息机制的重要性。
2.理解句柄的概念
句柄就是某种资源的标识,但又不仅如此。
孙鑫老师的课程中反复提到了句柄的概念。尤其是在窗口应用程序中。以下示例了避免重复创建一个button的经典代码:
void CTestDlg::OnBtnAdd()
{
// TODO: Add your control notification handler code here
//重复创建解决方法一
/*
static BOOL m_bIsBtnCreate=FALSE;//静态局部变量
if(!m_bIsBtnCreate)
{
m_btn.Create("ButtonTest",WS_CHILD|WS_VISIBLE,CRect(0,0,50,50),this,111);
m_bIsBtnCreate=TRUE;
}
else
{
m_btn.DestroyWindow();
m_bIsBtnCreate=FALSE;
}
*/
//重复创建解决方法二 利用m_btn自己的成员变量m_hWnd 该成员变量保存了button的句柄
if(!m_btn.m_hWnd)
m_btn.Create("ButtonTest",WS_CHILD|WS_VISIBLE,CRect(0,0,50,50),this,111);
else
m_btn.DestroyWindow();
}
3.尝试从不同方法解决问题
孙鑫老师喜欢从不同的方法入手,发散思维 ,解决问题。
(1)在Lesson7中用7种方法实现了编辑框的加法运算
编辑框加法运算的实现7中方法,实验代码如下:
//*******************************************************************************
//*******************************************************************************
方法总结如下图:
(2)Lesson9中用三种方法获取应用程序实例句柄加载图标
m_hIcon[0]=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1));//方法一
m_hIcon[1]=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));//方法二
m_hIcon[2]=LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3));//方法三
(3)Lesson9中用四种方法获取应用程序状态栏的指针 向状态栏写入数据
( (CMainFrame *)GetParent())->m_wndStatusBar.SetWindowText(str);//方法一
( (CMainFrame *)GetParent())->SetMessageText(str);//方法二
( (CMainFrame *)GetParent())->GetMessageBar()->SetWindowText(str);//方法三
GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);//方法四
4.善于利用断点跟踪、读取MFC类库源文档深入理解程序
(1)Lesson2中断点跟踪剖析了MFC 的框架
MFC对应于Lesson1中的WinMain实验的窗口创建过程。
窗口创建过程: a.产生全局对象 theApp b.进入WinMain c.调用虚函数InitInstance 完成窗口初始化工作,包括注册 创建 显示 更新 d.主框架窗口显示及更新 e.消息循环
(2)Lesson13利用断点跟踪法,了解单文档的OnFileNew和OnFileOpen执行过程。
//***********************************************************************
//单文档的OnFileNew执行过程
CWinApp::OnFileNew()
CDocManager::OnFileNew()
{
pTemplate->OpenDocumentFile(NULL)//单文档模板指针
}
CSingleDocTemplate->OpenocumentFile()
{
pDocument->CreateNewDocument()
pDocument->CreateNewFrame()
pDocument->OnNewDocument()//利用自子类指针调用OnNewDocument 调用CGraphicDoc::OnNewDocument()
}
CGraphicDoc::OnFileNew()
5.善于从不同语言、平台考虑程序实现
Lesson12文件操作中,讲述了C语言文件操作、C++文件操作、Win32 API文件操作、MFC CFile类的文件操作,全面讲解了不同语言、平台下的文件操作。
选择哪种语言去实现程序功能,需要合理选择。
6.善于利用MSDN的帮助文档
(1)函数的单词拼写法如获取一个当前线程的ID,可以尝试GetCurrentThreadId,设置某种属性使用Set打头的函数。
(2)一个类没有的函数到父类去查找,如要在CView类创建插入符,CView类本身没有这些操作但是父类CWnd有很多的Caret Functions 。
(3)如果一个类本身没有提供很多设置属性的方法怎么办,可以查看它的数据成员,比如CFontDialog类的成员m_cf,就是一个可以实现自定义字体对话框的结构体,CColorDialog类的数据成员m_cc,可以用来自定义颜色对话框。
总之,要实现某项功能,就可以从MSDN帮助文档获得帮助,可以查看其父类,子类,查看其成员,查看其方法。
7.善于从正常思维入手、步步改进,暴露错误并总结经验
孙鑫老师讲课时也会发生错误,但是它从不掩盖错误,而是带领学生一起分析错误,作为辅导班的老师,这一点很难能可贵。程序出错了要从多个几个方面思考,耐心调试。
当然学完了孙鑫老师的VC视频,只是对Windows程序设计入了一点门道,还有很多理论需要深入学习,至少应该看完《C++ Primer》 、《Windows核心编程》、《深入浅析MFC》这三本书,如果还要做其他开发则还需要学习其他的理论。程序员的工作是很多的,没有深入的学习就只能懂点皮毛。
继续学习Windows程序设计,继续学习C++ 。