VC++讲座备注.docx
《VC++讲座备注.docx》由会员分享,可在线阅读,更多相关《VC++讲座备注.docx(12页珍藏版)》请在冰豆网上搜索。
VC++讲座备注
视频特色
视频共分20课,平均每课两个多小时。
本套视频由孙鑫老师亲自授课录制,内容涵盖面广,从入门到精通,授课通俗易懂,分析问题独到精辟,绝对是目前国内顶级的VC++教学视频。
学员通过本套光盘的学习,能够快速掌握VC++,进一步精通VC++。
1、循序渐进
从Win32SDK编程开始讲解,帮助大家理解和掌握Windows编程的核心——消息循环机制。
2、通俗易懂
编程语言枯燥难懂,然而通过孙鑫老师形象化的讲解,Windows和MFC编程中的难点、重点,让您轻松掌握。
3、实战性强
编程中要注意什么?
如何阅读出错提示?
如何调试运行程序?
如何排查错误,解决问题?
通过孙鑫老师一步一步地操作讲解,带您迅速掌握程序开发的全过程。
4、内容全面
Windows编程知识,VC++面向对象的编程思想,MFC编程,涵盖了软件开发中的绝大部分应用,以及在以后开发中可能出现的问题,由孙鑫老师一一帮您剖析。
5、讲解深刻
在一些重难点知识以及属于操作系统内核编程方面的知识,孙鑫老师都将帮您剖析其本质,讲解其背后运行的原理,让您从根本上理解、掌握并灵活运用这些知识。
6、问答形式
在讲解一些重要知识点的时候,孙鑫老师会提出一些问题,引领大家思考,而这些问题正好是您在理解这些知识点时将要产生的疑问,在您还没有提出疑问时,孙鑫老师就会告诉您如何理解和解决这些问题了。
内容介绍
Lesson1:
Windows程序运行原理及程序编写流程,窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与cdecl调用规范的比较,初学者常犯错误及注意事项。
Lesson2:
C++经典语法与应用,类的编写与应用,构造与析构函数,函数的重载,类的继承,函数覆盖,基类与派生类的构造函数、析构函数先后调用顺序,如何在派生类构造函数中向基类的构造函数传递参数,this成员变量,类型转换的内幕,虚拟函数与多态性,引用和指针变量的区别与共同处。
VC工程的编译原理与过程,将工程中不同的类拆分到不同的文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFCAppWizard生成的工程奠定了良好基础。
Lesson3:
讲述MFCAppWizard的原理与MFC程序框架的剖析。
AppWizard是一个源代码生成工具,是计算机辅助程序设计工具,WinMain在MFC程序中是如何从源程序中被隐藏的,theApp全局变量是如何被分配的,MFC框架中的几个类的作用与相互关系,MFC框架窗口是如何产生和销毁的,对窗口类的PreCreateWidow和OnCreate两个函数的着重分析,Windows窗口与C++中的CWnd类的关系。
Lesson4:
MFC消息映射机制的剖析,讲述如何运用ClassWizard,,理解发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的函数进行响应的。
掌握设备描述表及其封装类CDC的使用,CDC是如何与具体的设备发生关联的,融合具体的画图程序进行分析。
如何设置封闭图形的填充刷子(位图画刷与透明画刷的使用)。
Lesson5:
掌握CDC的文字处理程序的编写,如何产生自定义字体和自定义插入符,熟悉对CString类的使用。
通过对卡拉OK程序的编写,讲解定时器的使用和DrawText函数的巧妙运用。
讲解如何使用CDC的裁减功能。
Lesson6:
菜单的工作原理及编写应用,菜单命令消息在MFC框架程序的几个类中的传递顺序和处理过程。
标记菜单、缺省菜单的实现原理、图形菜单的实现及常犯错误的分析,GetSystemMetrics的应用,快捷弹出菜单的实现方式及其命令响应函数有效范围(与弹出菜单时所指定的父窗口有密切的关系,最底层的子窗口具有最优先的处理机会)。
动态菜单的编写,如何让程序在运行时产生新的菜单项及如何手工为这些新产生的菜单命令安排处理函数,如何在顶层窗口中截获对菜单命令的处理,更进一步掌握CString类的应用。
Lesson7:
对话框用户界面程序的编写,如何向对话框控件关联数据成员及其实现机理,如何利用对话框类的成员函数向控件发送消息和获取对话框控件的类指针,如何直接利用对话框控件类操纵对话框控件(发送消息和直接调用成员函数)。
如何在程序运行时产生和销毁控件。
对话框控件的几种操作方式的优劣比较分析。
如何实现对话框的部分收缩和展开。
如何让对话框上的文本框在程序启动后立即获得焦点,如何利用SetWindowLong改变窗口的回调函数,通过改变文本框的默认回车处理方式进行演示。
实现多个输入文本框间通过回车逐一向下传递焦点的另一种巧妙方法(用缺省按钮来处理)。
Lesson8:
逃跑按钮的巧妙实现。
如何制作属性页对话框和向导对话框,融合讲解组合框(如何调整组合框的大小)、列表框、单选按钮、复选按钮等常用对话框控件的多种使用方法。
如何限制用户在不满足设定的条件时切换到其他属性页和向导页。
Lesson9:
如何修改MFCAppWizard向导生成的框架程序的外观和大小,修改图标、光标、背景的三种方法。
如何增加和删除工具栏按钮,如何给应用程序增加工具栏,如何显示和隐藏工具栏。
定制状态栏,在状态栏中添加时钟显示,CTime类及其用法。
在状态栏中添加进度条(主窗口产生后立即产生进度条的巧妙思想,不能在OnCreate函数中直接处理,要用到自定义消息的方法)。
鼠标坐标显示,在CView中获取状态栏对象的几种方式。
如何为应用程序添加启动画面。
Lesson10:
图形的绘制,如何使用自定义画笔(颜色,线宽,线型)。
如何为程序中添加选项菜单和选项设置对话框,如何使用标准颜色对话框,如何使用字体对话框,在选项对话框中实现预览功能。
实现选项对话框和窗口类中的数据交换。
如何改变对话框和控件的背景色,如何改变控件的文本颜色,对按钮控件的特殊处理。
如何在窗口中显示一幅位图。
Lesson11:
如何让CDC上输出的文字、图形具有保持功能,集合类CPtrArray的使用,CPaintDC与CClientDC的区别与应用,OnPaint与OnDraw在CView中的关系及实现内幕,滚动窗口的实现,坐标空间,映射方式,设备坐标与逻辑坐标的转换。
元文件设备描述表的使用,如何利用兼容DC实现图形的保存和再现。
Lesson12:
constchar*与char*const的区别。
C语言对文件读写的支持,FILE指针;文本文件和二进制文件的区别。
用文本方式读写文件和以二进制方式读写文件的注意事项。
C++对文件读写的支持,ofstream和ifstream的用法。
Win32SDK对文件读写的支持,CreateFile函数、WriteFile函数、ReadFile函数的使用;MFC对文件读写的支持,CFile类和CFileDialog的使用,文件过滤器的设置。
win.ini文件和注册表的读写方式及相关知识点。
Lesson13:
使用CArchive类对文件进行操作。
MFC框架程序提供的文件新建与打开功能内部的实现机制。
如何利用CDocument类的串行化存储功能保存与加载数据。
如何实现类对串行化的支持,CObArray的串行化实现内幕。
删除文档数据时常犯的错误。
MFC框架程序的文档类和视类的关系,以及如何获得相互的指针引用。
Lesson14:
网络的相关知识,网络程序的编写,Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建,通过bind与驱动程序建立关系。
此后,应用程序送给Socket的数据,由Socket交给驱动程序向网络上发送出去。
计算机从网络上收到与该Socket绑定的IP+Port相关的数据后,由驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据。
网络应用程序就是这样通过socket进行数据的发送与接收的。
TCP与UDP的工作原理与编写过程,如何在程序中链接库文件。
一个字符界面的聊天程序。
Lesson15:
多线程程序的编写,多线程应用中容易出现的问题。
互斥对象的讲解,如何使用互斥对象来实现多线程的同步。
如何利用命名互斥对象保证应用程序只有一个实例运行。
应用多线程编写网络聊天室程序。
Lesson16:
事件内核对象、关键代码段(临界区)的讲解,以及在多线程同步中的应用。
在Windows下编写基于消息的网络应用程序,掌握阻塞与非阻塞网络程序的编写,理解在Windows平台下,采用异步选择机制可以提高网络应用程序的性能。
Lesson17:
详细讲解进程间通讯的四种方式:
剪贴板、匿名管道、命名管道和邮槽。
并比较分析这几种进程间通信的优点和缺点。
Lesson18:
ActiveX控件的应用与工作原理。
ActiveX控件的编写,如何为控件安排属性,方法,事件,属性页,持久性存储,控件如何通知容器自身属性的改变。
如何注册控件与取消控件注册。
在VB和VC中访问ActiveX控件。
Lesson19:
动态链接库程序的编写。
静态库与动态库的区别,以及调用程序在链接静态库和动态库时的区别。
如何利用工具查看动态链接库输出的函数,Depends工具的使用,C++编译器名字改编技术对动态链接库输出函数的影响,extern"C"的用法,利用模块定义文件来解决C++名字改编的问题。
用typedef定义指向函数的指针类型,如何获得动态连接库里的函数的指针。
Lesson20:
Hook编程。
如何安装钩子过程,如何编写全局钩子,动态连接库里的全局变量数据共享问题分析。
ADO数据库编程。
在VB中利用ADO控件和ADO对象访问数据库,在VC中利用ADO技术访问数据库。
一、在视频Lesson2中,在介绍构造函数时,我说:
“构造函数最重要的作用是创建对象本身,对象内存的分配由构造函数来完成的”,这句话是错的,对象内存的分配和构造函数没有关系,对象内存的分配是由编译器来完成的,构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式,在类对象有虚表的情况下,构造函数还对虚表进行初始化。
另外,我说:
“C++又规定,如果一个类没有提供任何的构造函数,则C++提供一个默认的构造函数(由C++编译器提供)”,这句话也是错误的,正确的是:
如果一个类中没有定义任何的构造函数,那么编译器只有在以下三种情况,才会提供默认的构造函数:
1、如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
2、如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
3、在类中的所有非静态的对象数据成员,它们对应的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。
二、在视频Lesson4的Code中,画扇形用如下代码即可:
if(m_bDraw==TRUE)
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
}
带边线的扇形用如下代码即可:
if(m_bDraw==TRUE)
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.LineTo(m_ptOld);
m_ptOld=point;
}
三、在视频Lesson8中,关于在对话框上放置组合框的问题,我说“如果拖动的矩形较小,组合框的列表框部分将无法显示,此时也无法调整组合框的上下位置的大小了”。
实际上,组合框的上下位置还是可以调整的,调整的办法如下:
在对话框资源处于编辑状态时,将鼠标移动到组合框控件右边向下的箭头上,当鼠标变成上下箭头形状时,单击鼠标左键,此时可以看到举行框围绕着组合框。
将鼠标移动到该矩形框下端的蓝色小方块上,当鼠标变成上下箭头形状时,按住鼠标左键向下拖动,直到把组合框的下拉列表框范围拖动到合适的大小时松开鼠标左键。
四、在视频Lesson16的事件代码中,有一个问题,修改如下:
voidmain()
{
HANDLEhThread1;
HANDLEhThread2;
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//将CreateEvent()函数放置在这个位置
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
//g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//取消这个位置的CreateEvent()函数。
/*g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");
if(g_hEvent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"onlyinstancecanrun!
"<
return;
}
}*/
SetEvent(g_hEvent);
Sleep(4000);
CloseHandle(g_hEvent);
}
原因:
如果在线程产生之后调用CreateEvent(),假如线程提前被操作系统调度,那么线程里的WaitForSingleObject等待的将是一个空的g_hEvent,在这种情况下,WaitForSingleObject将返回WAIT_FAILED。
这个问题可以通过在CreateEvent()函数前添加Sleep(10)的调用来查看。
五、在视频Lesson16的采用事件的多线程同步代码中,有一个问题:
在线程1和线程2的代码中,有下面一段:
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
if(tickets>0)
{
Sleep
(1);
cout<<"thread1sellticket:
"<}
else
break;
SetEvent(g_hEvent);
}
这个代码有一个问题,当线程1或线程2卖完最后一张票时,调用SetEvent(g_hEvent);将事件对象设置为有信号状态,另一个线程等待到事件对象,开始执行代码,判断tickets不大于0,于是执行else语句下的break,退出循环,此时SetEvent(g_hEvent);就没有被执行,导致线程1或线程2一直等待,直到主线程终止运行,整个程序才退出。
应将SetEvent(g_hEvent);在if和else中分别调用,修改如下:
DWORDWINAPIFun1Proc(
LPVOIDlpParameter//threaddata
)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
//ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep
(1);
cout<<"thread1sellticket:
"<SetEvent(g_hEvent);
}
else
{
SetEvent(g_hEvent);
break;
}
}
return0;
}
DWORDWINAPIFun2Proc(
LPVOIDlpParameter//threaddata
)
{
while(TRUE)
{
WaitForSingleObject(g_hEvent,INFINITE);
//ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep
(1);
cout<<"thread2sellticket:
"<SetEvent(g_hEvent);
}
else
{
SetEvent(g_hEvent);
break;
}
}
return0;
}
六、在视频Lesson16的采用关键代码段的多线程同步代码中,有一个问题:
在视频讲解过程中,在线程1和线程2的代码中,有下面一段:
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep
(1);
if(tickets>0)
{
Sleep
(1);
cout<<"thread1sellticket:
"<}
else
break;
LeaveCriticalSection(&g_cs);
}
这个代码有一个问题,当线程1或线程2卖完最后一张票时,释放对临界区对象的所有权后,另外一个线程进入关键代码段,判断tickets不大于0,执行else语句下的break,退出循环,于是LeaveCriticalSection(&g_cs);就没有执行,导致线程1或线程2一直等待,直到主线程终止运行,整个程序才退出。
应将LeaveCriticalSection(&g_cs);在if和else中分别调用,修改如下:
DWORDWINAPIFun1Proc(
LPVOIDlpParameter//threaddata
)
{
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep
(1);
if(tickets>0)
{
Sleep
(1);
cout<<"thread1sellticket:
"<LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
return0;
}
DWORDWINAPIFun2Proc(
LPVOIDlpParameter//threaddata
)
{
while(TRUE)
{
EnterCriticalSection(&g_cs);
Sleep
(1);
if(tickets>0)
{
Sleep
(1);
cout<<"thread2sellticket:
"<LeaveCriticalSection(&g_cs);
}
else
{
LeaveCriticalSection(&g_cs);
break;
}
}
return0;
}七、在视频Lesson16的Code中,Chat的函数代码,OnSock函数忘记释放内存了,可以在出错判断的地方以及将要返回的地方加释放内存的语句(delete[]wsabuf.buf)。
voidCChatDlg:
:
OnSock(WPARAMwParam,LPARAMlParam)
{
switch(LOWORD(lParam))
{
caseFD_READ:
WSABUFwsabuf;
wsabuf.buf=newchar[200];
wsabuf.len=200;
DWORDdwRead;
DWORDdwFlag=0;
SOCKADDR_INaddrFrom;
intlen=sizeof(SOCKADDR);
CStringstr;
CStringstrTemp;
HOSTENT*pHost;
if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,
(SOCKADDR*)&addrFrom,len,NULL,NULL))
{
MessageBox("接收数据失败!
");
delete[]wsabuf.buf;//这里加一句释放内存的语句
return;
}
pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);
//str.Format("%s说:
%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);
str.Format("%s说:
%s",pHost->h_name,wsabuf.buf);
str+="\r\n";
GetDlgItemText(IDC_EDIT_RECV,strTemp);
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV,str);
delete[]wsabuf.buf;//这里加一句释放内存的语句
break;
}
}
八、在视频Lesson17的剪贴板编程的代码中,有一个问题,修改如下:
if(OpenClipboard())
{
if(IsClipboardFormatAvailable(CF_TEXT))
{
HANDLEhClip;
char*pBuf;
hClip=GetClipboardData(CF_TEXT);
pBuf=(char*)GlobalLock(hClip);
GlobalUnlock(hClip);
SetDlgItemText(IDC_EDIT_RECV,pBuf);
//CloseClipboard();//去掉这一句。
错误原因:
如果程序没有进入第二个if语句,那么剪贴板不会关闭。
}
CloseClipboard();//在这里添加关闭剪贴板的操作。
}
九、在视频Lesson18中,在OnIntervalChanged()函数中的代码逻辑上有一些问题,原先的代码如下:
voidCClockCtrl:
:
OnIntervalChanged()
{
//TODAddnotificationhandlercode
if(m_interval<0 m_interval>6000)
{
m_interval=1000;
}
else
{
m_interval=m_interval/1000*1000;
KillTimer
(1);
SetTimer(1,m_interval,NULL);
BoundPropertyChanged(0x1);
}
SetModifiedFlag();
}
应该改为:
voidCClockCtrl:
:
OnIntervalChanged()
{
//TODAddnotificationhandlercode
if(m_interval<0 m_interval>6000)
{
m_interval=1000;
}
else
{
m_interval=m_interval/1000*1000