孙鑫VC++ 20节课的反思

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中方法,实验代码如下:
 //*******************************************************************************

[cpp] view plain copy print ?
  1. <span style="font-size:18px;">//方法一  
  2.      /* 
  3.   int num1,num2,num3; 
  4.   char ch1[10],ch2[10],ch3[10]; 
  5.   GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); 
  6.   GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10); 
  7.   num1=atoi(ch1);//数值型字符串转换为整数 
  8.   num2=atoi(ch2); 
  9.   num3=num1+num2; 
  10.      _itoa(num3,ch3,10);//以10进制将数值转换到字符串中 
  11.      GetDlgItem(IDC_EDIT3)->SetWindowText(ch3); 
  12.   */  
  13.   //方法二  
  14.   /* 
  15.   int num1,num2,num3; 
  16.   char ch1[10],ch2[10],ch3[10]; 
  17.      GetDlgItemText(IDC_EDIT1,ch1,10); 
  18.   GetDlgItemText(IDC_EDIT2,ch2,10); 
  19.      num1=atoi(ch1); 
  20.   num2=atoi(ch2); 
  21.   num3=num1+num2; 
  22.      _itoa(num3,ch3,10); 
  23.      SetDlgItemText(IDC_EDIT3,ch3); 
  24.   */  
  25.   //方法三  
  26.   /* 
  27.      int num1,num2,num3; 
  28.      num1=(int)GetDlgItemInt(IDC_EDIT1,NULL,TRUE); 
  29.   num2=(int)GetDlgItemInt(IDC_EDIT2,NULL,TRUE); 
  30.      num3=num1+num2; 
  31.   SetDlgItemInt(IDC_EDIT3,num3,TRUE); 
  32.   */  
  33.   //方法四 关联变量  
  34.      /* 
  35.   UpdateData(); 
  36.   m_num3=m_num1+m_num2; 
  37.   UpdateData(FALSE); 
  38.   */  
  39.   //方法五  
  40.   /* 
  41.   int num1,num2,num3; 
  42.   char ch1[10],ch2[10],ch3[10]; 
  43.   m_ctlEdit1.GetWindowText(ch1,10); 
  44.   m_ctlEdit2.GetWindowText(ch2,10); 
  45.   num1=atoi(ch1); 
  46.   num2=atoi(ch2); 
  47.   num3=num1+num2; 
  48.      _itoa(num3,ch3,10); 
  49.      m_ctlEdit3.SetWindowText(ch3); 
  50.   */  
  51.   //方法六 消息方法  
  52.   /* 
  53.   int num1,num2,num3; 
  54.   char ch1[10],ch2[10],ch3[10]; 
  55.   //发送消息方法 
  56.   //::SendMessage(GetDlgItemText(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); 
  57.   //::SendMessage(m_ctlEdit1.m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); 
  58.      //GetDlgItemText(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(LPARAM)ch1); 
  59.      m_ctlEdit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1); 
  60.      m_ctlEdit2.SendMessage(WM_GETTEXT,10,(LPARAM)ch2); 
  61.   num1=atoi(ch1); 
  62.   num2=atoi(ch2); 
  63.      num3=num1+num2; 
  64.      _itoa(num3,ch3,10); 
  65.      m_ctlEdit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3); 
  66.      */  
  67.      //方法七  SendDlgItemMessage  
  68.   int num1,num2,num3;  
  69.   char ch1[10],ch2[10],ch3[10];  
  70.   SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1);  
  71.   SendDlgItemMessage(IDC_EDIT2,WM_GETTEXT,10,(LPARAM)ch2);  
  72.      num1=atoi(ch1);  
  73.   num2=atoi(ch2);  
  74.      num3=num1+num2;  
  75.      _itoa(num3,ch3,10);  
  76.     SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3);  
  77.     SendDlgItemMessage(IDC_EDIT3,EM_SETSEL,0,-1);//复选消息  
  78.     m_ctlEdit3.SetFocus();</span>  

//*******************************************************************************
 方法总结如下图:


孙鑫VC++ 20节课的反思_第1张图片

(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++ 。


你可能感兴趣的:(windows,文档,mfc,vc++,button)