孙鑫C++视频教程-VC深入详解自学笔记(几乎完全免费)

更新时间:2023-05-04 20:18:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

VC++深入详解学习笔记

孙鑫C++视频教程

(2010-10整理)

孙鑫C++视频教程--VC++深入详解笔记

目录

Lesson 1 Windows程序运行原理及程序编写流程 (1)

Lesson 2 掌握C++基本语法 (7)

Lesson 3 MFC框架程序剖析 (8)

Lesson 4 简单绘图 (11)

Lesson 5 文本编程 (14)

Lesson 6 菜单编程 (17)

Lesson 7 对话框编程 (22)

Lesson 8 逃跑按钮的巧妙实现 (29)

Lesson 9 定制应用程序的外观 (30)

Lesson 10 绘图控制 (33)

Lesson 11 图形的保存和重绘 (35)

Lesson 12 文件操作 (37)

Lesson 13 文档串行化 (41)

Lesson 14 网络编程 (43)

Lesson 15 多线程 (51)

Lesson 16 线程同步与异步套接字 (57)

Lesson 17 进程间通信 (67)

Lesson 18 Active控件 (75)

Lesson 19 动态链接库DLL (79)

Lesson 20 Hook与数据编程 (85)

孙鑫C++视频教程--VC++深入详解笔记

Lesson 1 Windows程序运行原理及程序编写流程

窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与Lessonecl调用规范的比较,初学者常犯错误及注意事项。

1.Windows API与Win32 SDK

操作系统提供了各种方便开发Windows应用程序的编程接口,所的函数都在Windows。

h头文件中声明。Win32 SDK(Software Development Kit): 即Windows 32位平台下的软件开发包,包括API函数,帮助文档,微软提供的一些辅助开发工具。

2.窗口与句柄

窗口是是屏幕上一块矩形区域,是Windows应用程序与用户进行交互的接口。窗口分为客户区和非客户区。

在Windows应用程序中,窗口是通过窗口句柄(HWND)来标识的,要对某个窗口进行操作,首先就要得到这个窗口的句柄。其它各种资源(窗口,图标,光标等),系统在创建这些资源时会为它们分配内在,并返回标识这些资源的标识号,即句柄。-->光标句柄(HCURSOR),图标句柄(HICON)。

3.消息与消息队列

Windows程序设计是一种基于消息的事件驱动方式的程序设计模式。

第 1 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

消息:在Windows中由结构体MSG来表示,

typedef struct tagMSG{

HWND hwnd;//消息所属的窗口句柄

UINT message;//消息本身标识符,由一数值表示,系统对消息定//义为

WM_XXX宏(WM为Windows Message缩写)

WPARAM wParam; //随消息的不同附加信息也不同

LPARAM lParam; //消息的附加参数

DWORD time; //消息投递的时间

POINT pt; //鼠标当前位置

}

消息队列:每当一个Windows应用程序创建后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序一的窗口的消息,消息产生后被投递到消息队列中,应用程序通过一个消息循环不断的消息队列中取出消息进行响应。响应过程由系统向应用程序发送消息,实际就是调用应用程序的消息处理函数。

4.创建一个完整的Win32程序,该程序实现创建一个窗口,其中主要步骤为

A.WinMain函数的定义

B.创建一个窗口创建一个完整的窗口的四个步骤SDK,1设计窗口类,2注

册窗口类,3创建窗口,4显示窗口

C.进行消息循环

D.编写窗口过程函数

回调函数的实现机制:

(1)定义一个回调函数

(2)提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者

(3)当特定的事件或条件发和的时候,调用使用函数指针调用回调函数对事件进行

处理

针对Windows的消息处理机制,窗口过程函数被调用的过程如下:

A.在设计窗口类的时候,将窗口赛程函数的地址赋值给lpfnWndProc成员变

B.调用RegisterClass(&wndclass)注册窗口类,那么系统就有了我们所编写的

窗口过程函数的地址

C.当应用程序接收到某一窗口的消息,调用DispatchMessage(&msg)将消息

加值给系统。系统则利用先前注册窗口类时得到函数指针,调用窗口过程

函数对消息进行处理。

第 2 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

第 3 页 共 101 页

HICON LoadIcon(HINSTANCE hInstance , LPCTSTR lpIconName);

//加载窗图标,返回系统分配给该图标的句柄, LPCTSTR 被定义为CONST CHAR *(指向常量的字符指针),图标的ID 是一个常数,要使用MAKEINTRESOUCE 宏把资源ID 标识转换为需要的LPCTSTR 类型

5. sprintf 格式化字符,其头文件为stdio 。h ,在MFC 中格式化字符用CString 。Format

6. GetDC()与ReleaseDC()要成对使用,否则会内存泄漏。同样,BeginPaint()与 EndPaint(),这两个Parint 只能在影响WM_PAINT 消息中调用。

7. GetStockObject()得到画笔、画刷、字体、调色板的句柄,使用时必须用类型 转换。

如:hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH )//创建空画刷

8. 什么时候用NULL ,什么时候用0。答,对指针赋值时用NULL ,对变量赋值 时用0。

9.

什么是野指针?答:将指针指向的变量的内存释放后,此指针即变成野指针! 如何避免野指针?答:将此指针指向NULL 即可。p=NULL;

#include

#include

#include

using namespace std;

//回调函数原型声明,返回长整形的结果码,CALLBACK 是表示stdcall 调用

LRESULT CALLBACK WinProc(

HWND hwnd, // handle to window

UINT uMsg, // message identifier

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

);

//(1) WinMain 函数,程序入口点函数

int WINAPI WinMain(

HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance

孙鑫C++视频教程--VC++深入详解笔记

LPSTR lpCmdLine, // command line

int nCmdShow // show state

){

//(2)

//一.设计一个窗口类,类似填空题,使用窗口结构体

WNDCLASS wnd;

wnd.cbClsExtra = 0; //类的额外内存

wnd.cbWndExtra = 0; //窗口的额外内存

wnd.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);//创建一个空画刷填充背景

//加载游标,如果是加载标准游标,则第一个实例标识设置为空

wnd.hCursor = LoadCursor(NULL, IDC_CROSS);

wnd.hIcon = LoadIcon(NULL, IDI_ERROR);

wnd.hInstance = hInstance;//实例句柄赋值为程序启动系统分配的句柄值

wnd.lpfnWndProc = WinProc;//消息响应函数

wnd.lpszClassName = "gaojun";//窗口类的名子,在注册时会使用到

wnd.lpszMenuName = NULL;//默认为NULL没有标题栏

wnd.style = CS_HREDRAW | CS_VREDRAW;//定义为水平和垂直重画

//二.注册窗口类

RegisterClass(&wnd);

//三.根据定制的窗口类创建窗口

HWND hwnd;//保存创建窗口后的生成窗口句柄用于显示

//如果是多文档程序,则最后一个参数lParam必须指向一个CLIENTCREATESTRUCT结构体hwnd = CreateWindow("gaojun", "WIN32应用程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL); //四.显示窗口

ShowWindow(hwnd, SW_SHOWDEFAULT);

//五.更新窗口

UpdateWindow(hwnd);

//(3).消息循环

MSG msg;//消息结构体

//如果消息出错,返回值是-1,当GetMessage从消息队列中取到是WM_QUIT消息时,返回值是0 //也可以使用PeekMessage函数从消息队列中取出消息

BOOL bSet;

while((bSet = GetMessage(&msg, NULL, 0, 0)) != 0){

if (-1 == bSet)

{

return -1;

}

else{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

第 4 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

return 0;//程序结束,返回0

}

//消息循环中对不同的消息各类进行不同的响应

LRESULT CALLBACK WinProc(

HWND hwnd, // handle to window

UINT uMsg, // message identifier

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

){

switch (uMsg)

{

case WM_CHAR://字符按键消息

char szChar[20];

sprintf(szChar, "char is %d;", wParam);//格式化操作,stdio.h

MessageBox(hwnd, szChar, "gaojun", 0);//输出操作windows.h中

break;

case WM_LBUTTONDOWN://鼠标左键按下消息

MessageBox(hwnd, "this is click event!", "点击", 0);

HDC hdc;

hdc = GetDC(hwnd);//获取设备上下文句柄,用来输出文字

//在x=0,y=50(像素)的地方输出文字

TextOut(hdc, 0, 50, "响应WM_LBUTTONDONW消息!",

strlen("响应WM_LBUTTONDONW消息!"));

ReleaseDC(hwnd, hdc);//在使用完DC后一定要注意释放

break;

case WM_PAINT://窗口重给时报消息响应

HDC hDc;

PAINTSTRUCT ps;

hDc = BeginPaint(hwnd, &ps);

TextOut(hDc, 0, 0, "这是一个Paint事件!", strlen("这是一个Paint事件!"));

EndPaint(hwnd, &ps);

break;

case WM_CLOSE://关闭消息

if (IDYES == MessageBox(hwnd, "确定要关闭当前窗口?", "提示", MB_YESNO))

{

DestroyWindow(hwnd);//销毁窗口

}

break;

case WM_DESTROY:

PostQuitMessage(0);//在响应消息后,投递一个退出的消息使用程序安全退出

break;

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);//调用缺省的消息处理过程函数

第 5 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

}

return 0;

}

第 6 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

Lesson 2 掌握C++基本语法

1.C++主要特点:

封装性(Encapsulation):把数据与操作数据的函数组织在一起,使程序结构更加紧凑,提高类内部数据的安全性。

继承性(Inheritance):增加了软件的可扩充性及代码重用性;

多态性(Polymorphism):使设计人员在设计程序时可以对问题进行更好的抽象,有利于代码的维护和可重用

2.C++中几个特性的实现技术和其它要注意的地方:

构造函数,析构函数,覆盖,隐藏,重载,继承,多态(迟绑定)等技术,算法

类的编写与应用

以下是部分资料:

1。定义结构体和类时。例如Class Point{int x;int y;};要注意一定加上分号,结构体定义默认成员是public,而Class定义默认为private

2。#include 与#include "xxx。h"的区别:<>不查找运行时目录,""查找运行时目录!(#include引入是源文件,要用上using namespace xxx;)

3。类的定义中,如果未指明成员类型,则缺省为private。而结构体中则缺省为public。

4。引用:引用经常用在函数的传参上。另外数值交换函数也经常用引用。例

change(int &x,int &y){int temp;temp=x;x=y;y=x}调用时即可以用int a=3;int b=4;change(a,b);一般不用指针来作为参数进行数值交换。因为会引起歧义。

5。通常将类的定义放。h文件,而将其实现放在cpp文件中,别忘记了在cpp文件中#include "xxx。h"

6。如何防止类的重复定义?

用#inndef Point_H_H

#define Point_H_H

class Point{};

#endif来防止

7。源文件cpp文件单独编译成obj文件。最后由链接器将与将要使用到的C++标准库类链接成exe文件,头文件不参加编译。所以在cpp文件中别忘记了加入#include "xxx。

h"

8。函数的覆盖,在子类中重写父类的函数,此时采用早期绑定的方法。如果加入了virtual,则将采用迟绑定的技术,在运行时根据对象的类型确定调用哪一个函数。此迟绑定技术是MFC的类的继承的精髓。

9。强制类型转换。如果CFish从CAnimal派生而来。则可以将鱼的对象转换为CAnimal 的对象,而反之则不行。从现实中理解也是正常的,鱼可以是动物,而动物却不是鱼。

再如int可以强制转换成char型。而反之则出错。

第7 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

第 8 页 共 101 页

Lesson 3 MFC 框架程序剖析

1. MFC 简介:MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发

工作所开发的一套C++类的集合,是一套面向对象的函数库,以为的方式提供给用户使用.利用这些类,可以有效发帮助程序员完成Windows 应用程序的开发

MFC AppWizard 是一个辅助生成源代码的向导工具,它可以帮助我们自动生成基于MFC 框架的源代码.在向导的每一个步骤中,我们可以根据需要来选择各种特性,从而实现定制应用程序.

2. 窗口类,窗口对象与窗口关系

窗口类中定义一个类型为HWND 成员变量,用来保存与之相关的窗口句柄值,可以用一个窗口类的实例即窗口对象来对应一个创建的窗口(是一种资源),窗口对象与窗口之间的关系是:

C++窗口类对象与窗口并不是一回事,它们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄.窗口销毁时,与之对应的C++窗口类对象销毁与否,要看其生命周期是否结束,但C++窗口类对象销毁时,与之相关的窗口将销毁,因为它们之间的纽带(m_hWnd)已经断了,因此这时要回收窗口资源.

窗口销毁时调用DestroyWindow 函数,窗口类对象销毁即将m_hWnd 变量设置为NULL. VC 6.0一些常用操作快捷方式:

功能分类

快捷键 说明

File Ctrl+N

New 新建工程 Ctrl+O

Open 打开文件 Find (查找) Alt+F3/Ctrl+F

弹出查找对话框 F3

查找下一个 Shift+F3

查找上一个 Ctrl+H

替换 Ctrl+]/Ctrl+E

寻找下一半括弧 F4

寻找下一个错误/警告位置 Shift+F4

寻找上一个错误/警告位置 格式 Ctrl+U

将选定区域转换成小写 Ctrl+Shift+U

将选定区域转换成大写 Alt+F8

自动格式重排 Build F7

Build (编绎并链接成exe 文件) Ctrl+F7 Compile (编译)

孙鑫C++视频教程--VC++深入详解笔记

第 9 页 共 101 页 (建立)

Ctrl+F5

Execute (编译+链接+运行) Ctrl+Shift+F5

Restarts the program (重新运行程序) Debug (调试) F5

Go (顺序执行) F11

step into (顺序执行,进入循环或函数) F10

step over (顺序执行,不进入循环或函数) Ctrl+F10

Run to cursor (自动执行到用户光标所指的语句前) Shift+F5

Stop Debugging (停止调试) F9

Insert/Remove breakpoint (在当前行插入/去掉断点) Ctrl+Shift+F9 去掉所有断点

1.在main 或WinMain 之前,全局变量已经被分配内存并初始化了。

2.在MFC 中在WinMain 之前有个theApp 全局变量先被构造并被初始化,而由于子类构造函数执行前,其父类的构造函数先被执行,所以CTestApp 的父类CWinAPP 的构造函数先执行。产生了theApp 对象后,在WinMain()中的指针*pThread 和*pApp 就有了内容。

3.MFC 大致流程:

CTestApp theApp;//构造全局对象

WinMain()

{

AfxWinMain();//调用下面的函数

}

AfxWinMain()

{

pThread->Initinstance();//初始化工作和注册窗口类,窗口显示和更新

pThread->Run();//消息循环

}

而在BOOL CTestApp::InitInstance()中的代码

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CTestDoc),

RUNTIME_CLASS(CMainFrame), // main SDI frame window

RUNTIME_CLASS(CTestView));

AddDocTemplate(pDocTemplate);

完成了将这三个类关联起来的工作。

4.如何在单文档文件中显示一个CButton 的对象?

在CMainFrame::OnCreate()中定义一个CButton 的对象btn;然后调用btn.Create("维新",WS_DISABLED |WS_CHILD | WS_VISIBLE | BS_AUTO3STA TE,

CRect(0,0,300,100),/*GetParent(),*/this,123);

注意点:

(1).此处btn 不能是局部变量,否则它的生命周期太短,将不能显示。

(2).在CBUTTON 类的Create 函数的第二个参数中加入WS_VISIBLE 参数才行。否则

孙鑫C++视频教程--VC++深入详解笔记

必须调用ShowWindow也可以在view的OnCreate消息响应函数中加入

(3).CButton类的定义头文件在afxwin.h中,而stdafx.h包含了afxwin.h,所以可以直接使用。因为MFC中的每一个类中都有#include "stdafx.h"的声明。

第10 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

Lesson 4 简单绘图

1.在单文档中view挡在MainFrame的前面。此时如果编写针对MainFrame的mouseClick

事件,将不会有反应。因为MFC视类窗口是覆盖在框架窗口上的,因此框架窗口不能感到鼠标消息.

2.MFC的消息映射机制:

在每个能接收和处理消息的类中,定义一个消息和消息函数对照表,即消息映射表.在消息映射表中,消息与对应的消息处理函数指针成对出现.某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中.当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理此消息.如果能处理该消息,则同样依照静态表很容易找到并调用对应的消息处理函数.

MFC消息映射机制是针对能接受消息和处理消息的类来定义对应的消息映射表,而不是由父类来定义所有消息对应的虚函数,由子类来覆盖其函数实现,因为这样做会使程序背着一个很大的虚拟函数表的包袱运行,对内存是一种浪费.

MFC工程中一个消息映射在三处添加代码:

(1): CDrawView视类的头文件.h

//{{AFX_MSG(CDrawView)

afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

两个AFX_MSG注释宏(因为加了注释符)之间,afx_msg是限定符(也是宏),表明函数是一个消息响应函数的声明,如果是用户自定义的消息函数响应声明则在注释宏下, DECLARE_MESSAGE_MAP之上加写代码

(2): CDrawView的cpp(源文件)的BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之

间,定义了CDrawView类的消息映射表,其中ON_WM_LBUTTONDOWN映射宏就是将鼠标左键按下消息(WM_LBUTTONDOWN)与一个消息响应函数(OnLButtonDown)关联.

BEGIN_MESSAGE_MAP(CDrawView, CView)

//{{AFX_MSG_MAP(CDrawView)

ON_WM_LBUTTONDOWN()

//}}AFX_MSG_MAP

// Standard printing commands

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP()

如果添加自定义的消息映射,使用ON_MESSAGE(用户定义消息,消息响应函数名)无”;”

结尾

(3): 是CDrawView的cpp(源文件)中有函数实现。

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)

{

// TOD Add your message handler code here and/or call default

m_ptOrigin=m_ptOld=point;

第11 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

m_bDraw=TRUE;

CView::OnLButtonDown(nFlags, point);

}

通过分析MFC消息响应函数在程序中有三处属地省:函数原型,用来关联消息和消息响应函数的宏和函数实现.

3.以下绘图程序,参考代码的注释可解决部分绘图问题

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)

{

// TODO: Add your message handler code here and/or call default

//作图

/*调用SDK函数获取设备上下文句柄

HDC hdc;

hdc = ::GetDC(m_hWnd);//这个m_hWnd是CWnd类中的保护成员, 保存窗口句柄,

//而CLesson3View类是从CWnd类继承来的,所以也有这个成员MoveToEx(hdc, m_ptnOrigin.x, m_ptnOrigin.y, NULL);

LineTo(hdc, point.x, point.y);

::ReleaseDC(m_hWnd, hdc); // 在使用完设备上下文句柄后一定注意释放*/

/*使用CDC(MFC)关于作图对HDC一个封装*/

// CDC *pDc;

// pDc = GetDC();

// pDc->MoveTo(m_ptnOrigin);

// pDc->LineTo(point);

// ReleaseDC(pDc);

/*使用客户区绘图类,这个是比较常用的*/

//CClientDC dc(this);//CClientDC的构造函数,使用当前窗口句柄值做为参数

//CClientDC dc(GetParent());//得到关于父类窗口一个设备上下文

// dc.MoveTo(m_ptnOrigin);

// dc.LineTo(point);

// CClientDC类在构造时调用GetDC,然后在释放时又调用ReleaseDC所以不用手动释放

//利用MFC的CWindowDC绘图

/好处是可以访问整个窗口区域,包括框架窗口客户区和非客户区,桌面等,

// CWindowDC dc(this);

// CWindowDC dc(GetParent());

// dc.MoveTo(m_ptnOrigin);

// dc.LineTo(point);

// CWindowDC dc(GetDesktopWindow());//这个可以画到桌面上其它地方

// dc.MoveTo(m_ptnOrigin);

// dc.LineTo(point);

//以上所画的线条颜色都是黑色的,因为在设备描述表中使用默认的画笔(黑色),

//要改变线条颜色则需要自己生成一个新的画笔对象,

//将它选到设备描述表中,再画就使用新画笔来绘图

第12 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

// CPen m_pen(PS_DASH, 2, RGB(255, 0, 0));//生成新的画笔

// CClientDC dc(this);

// CPen *pOldPen = dc.SelectObject(&m_pen);//选择进设备描述表中

// dc.MoveTo(m_ptnOrigin);

// dc.LineTo(point);

// dc.SelectObject(pOldPen);//在使用完新的画笔后,要将原来的画笔重新选择时设备描述表//使用画刷来填充矩形

// CBrush m_brush(RGB(120, 0, 23));

// CClientDC dc(this);

// dc.FillRect(CRect(m_ptnOrigin, point), &m_brush);

//使用位图画刷来填充矩形

//创建一个位图对象

// CBitmap m_bitmap;

// m_bitmap.LoadBitmap(IDB_MyBitmap);

// CBrush m_Brush(&m_bitmap);

// CClientDC dc(this);

// dc.FillRect(CRect(m_ptnOrigin, point), &m_Brush);

//透明画刷

//首先使用Win32的API函数GetStockObject来获取一个NULL_BRUSH画刷

CClientDC dc(this);

CBrush *pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

//是静态成员函数,从句柄获取对象的指针

CBrush *pOldBrush = dc.SelectObject(pBrush);

dc.Rectangle(CRect(m_ptnOrigin, point));

dc.SelectObject(pOldBrush);

bIsMouseDown = FALSE;

CView::OnLButtonUp(nFlags, point);

}

类的静态成员函数可以由类名直接调用,也可以由对象调用。可以认为静态成员函数并不属于某个对象,它属于类本身。程序运行伊始,即使没有实例化类的对象,静态成员函数和静态成员变量已然有其内存空间。静态成员函数不能访问非静态成员变量!

静态成员变量必须在类的外部初始化。当然如果并不打算用到静态成员变量,此时你可以不初始它。

4.理解代码区,数据区,堆,栈!(91f7164383c4bb4cf7ecd1cf/server/j_server/J_1010.Html)

对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

第13 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

Lesson 5 文本编程

1,创建插入符:

void CreateSolidCaret( int nWidth, int nHeight );//创建插入符

void CreateCaret( CBitmap* pBitmap );//创建位图插入符

void ShowCaret( );//显示插入符

void HideCaret( );//隐藏插入符

static void PASCAL SetCaretPos( POINT point );//移动插入符号

说明:

1)创建插入符要在窗口创建完成之后,CreateSolidCaret函数创建的插入符被初始化为隐藏,所以需要调用ShowCaret()将其显示。

2)使用CreateCaret函数创建位图插入符的时候,不能使用局部的位图对象关联位图资源。(与资源相关联的C++对象,当它析构的时候会同时把与它相关联的资源销毁。)

2,获取当前字体信息的度量:CDC::GetTextMetrics

BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics ) const;

说明:

typedef struct tagTEXTMETRIC { /* tm */

int tmHeight;//字体高度。Specifies the height (ascent + descent) of characters.

int tmAscent;//基线以上的字体高度

int tmDescent;//基线以下的字体高度

int tmInternalLeading;

int tmExternalLeading;

int tmAveCharWidth;//字符平均宽度

int tmMaxCharWidth;

int tmWeight;

BYTE tmItalic;

BYTE tmUnderlined;

BYTE tmStruckOut;

BYTE tmFirstChar;

BYTE tmLastChar;

BYTE tmDefaultChar;

BYTE tmBreakChar;

BYTE tmPitchAndFamily;

BYTE tmCharSet;

int tmOverhang;

int tmDigitizedAspectX;

int tmDigitizedAspectY;

} TEXTMETRIC;

//创建设备描述表

CClientDC dc(this);

//定义文本信息结构体变量

TEXTMETRIC tm;

第14 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

//获得设备描述表中的文本信息

dc.GetTextMetrics(&tm);

//根据字体大小,创建合适的插入符

CreateSolidCaret(tm.tmAveCharWidth / 8, tm.tmHeight);

ShowCaret();

3,OnDraw函数:

virtual void OnDraw( CDC* pDC )

当窗口(从无到有或尺寸大小改变等)要求重绘的时候,会发送WM_PAIN消息,调用OnDraw 函数进行重绘。在客户区的绘图如果想保持不变也可以在这个函数中进行编写,每次重给的时候会再次执行代码,生成绘图.

4,获取某字符串的高度和宽度(区别字符串的长度,长度表示字符个数):

CDC::GetTextExtent

CSize GetTextExtent( LPCTSTR lpszString, int nCount ) const;

CSize GetTextExtent( const CString& str ) const;

说明:

//The CSize class is similar to the Windows SIZE structure。

typedef struct tagSIZE {

int cx;//the x-extent

int cy;//the y-extent

} SIZE;

5,路径层:

BOOL BeginPath( );//CDC中函数

//在这作图定义路径层剪切区域

BOOL EndPath( );

BOOL SelectClipPath( int nMode );//调用这个函数来使当前路径层剪切区域与新剪切区域进行互操作。

//在这覆盖作图(包含前定义的路径层区域)定义新的剪切区域

说明:

1)SelectClipPath Selects the current path as a clipping region for the device context, combining the new region with any existing clipping region by using the specified mode. The device context identified must contain a closed path.

2)应用:当作图的时候,如果想要在整幅图形其中的某个部分和其它部分有所区别,我们可以把这部分图形放到路径层当中,然后指定调用指定互操作模式调用SelectClipPath( int nMode )函数来使路径层和覆盖在其上新绘图剪切区域进行互操作,达到特殊效果。

6,关于文本字符串一些函数:

COLORREF GetBkColor( ) const;//得到背景颜色

virtual COLORREF SetBkColor( COLORREF crColor );//设置背景颜色

BOOL SetTextBkColor( COLORREF cr );//设置文本背景颜色

virtual COLORREF SetTextColor( COLORREF crColor );//设置文本颜色

virtual BOOL TextOut( int x, int y, LPCTSTR lpszString, int nCount );//输出文本

BOOL TextOut( int x, int y, const CString& str );//在x,y所指定坐标处输出str

CString Left( int nCount ) const;//得到字符串左边nCount个字符

int GetLength( ) const;//得到字符串长度strlen()

第15 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

7,字体CFont::CFont

CFont( );//构造函数

//Constructs a CFont object. The resulting object must be initialized with CreateFont, CreateFontIndirect, CreatePointFont, or CreatePointFontIndirect before it can be used.

选用字体事例代码组:

CClientDC dc(this);

CFont font;//构造字体对象

font.CreatePointFont(300,"华文行楷",NULL);//初始化字体对象,与字体资源相关联

CFont *pOldFont=dc.SelectObject(&font);//将新字体选入DC

...

dc.SelectObject(pOldFont);//恢复原字体

说明:

1)构造字体对象时候,必须初始化。(初始化是将字体对象与字体资源相关联)。

2)初始化对象时候,选用的字体也可以是系统字体,但不一定都有效,据测试选用。

8,在MFC中CEditView 和cRichEditView类已经完成了初步的文字处理。可以让应用程序的View类以CEditView 和cRichEditView类为基类。在创建工程中的第六步可以选择. 9,平滑变色

CDC::TextOut()是一个字母一个字母的输出,达不到平滑效果。

CDC::DrawText():将文字的输出局限于一个矩形区域,超出矩形区域的文字都被截断。利用这一特点,可每隔些时间增加矩形大小,从而可实现人眼中的平滑效果。CWnd::SetTimer():设置定时器。按设定的时间定时发送WM_TIMER消息。

说明:

UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );

//nIDEvent定时器标示,nElapse消息发送间隔时间,void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD)设置回调函数,如果设置则由设置的回调函数处理WM_TIMER消息,如果没有设置回调函数设为NULL,这发送的WM_TIMER消息压入消息队列,交由相关联的窗口处理(添加WM_TIMER消息处理函数OnTimer())。

afx_msg void OnTimer( UINT nIDEvent );

//响应WM_TIMER消息,nIDEvent为消息对应定时器标示(可以设置不同的定时器发送WM_TIMER消息)

问题:

1,在CCareView类中添加WM_CREATE消息响应函数OnCreate(),WM_CREATE消息是在什么时候被检查到而被响应的呢?

(猜测:添加WM_CREA TE消息后,消息被压入消息队列,然后经过消息循环进行分发到具体窗口,从而进行响应)

2,现有一文本文件内容已经读入串STR中,要求在视图客户区按原先文本文件中的格式输出。

问题是,利用CDC的TextOut()来在CView类派生类窗口中输出串时,忽略了串中的TAB、回车换行等格式,无论串有多长均在一行上输出。

这其中是CDC类成员函数TextOut()忽略串中格式的,还是CView类派生类窗口设置从中做怪呢?怎么解决

第16 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

Lesson 6 菜单编程

1.MFC中的顶层菜单默认为弹出菜单(Pop-up),它是不能用来作命令响应的,当取消

Pop-up选项后可接受命令响应。

2.消息的分类:标准消息,命令消息,通告消息。

[标准消息]:除WM_COMMAND之外,所有以WM_开头的消息。从CWnd类派生的类都可以接收到这一消息

[命令消息]:来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND 呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。从CCmdTarget(CWnd的父类)派生的类都可以接收到这一类消息

[通告消息]:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND 形式呈现。从CCmdTarget(CWnd的父类)派生的类都可以接收到这一类消息

总结:凡是从CWnd派生的类,既可以接收标准消息,也要以接收命令消息和通告消息。

而对于那些从CCmdTarget派生的类,则只能接收命令消息和通告消息,不能接收标准消息。

3.MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响

应,则菜单消息传递顺序:View类--Doc类--CMainFrame类--App类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。

菜单消息与前面介绍的标准消息的实现机制是相类似的,都是在消息响应函数原型(头文件),消息映射宏(源文件)和消息函数实现(源文件)中添加代码。注意:文档类与应用程序类都是由CCmndTarget类派生,所以可以接收菜单命令消息,但不能接收标准消息(只能由CWnd类派生才可以)。

具体消息路由过程:

当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。

4.一个菜单栏可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。对菜单栏的子

菜单由左至右建立从0开始的索引。对特定子菜单的菜单项由上至下建立了从0开始的索引。访问子菜单和菜单项均可以通过其索引或标识(如果有标识的话)进行。

相关重要函数:

CMenu* GetMenu( ) ;//CWnd::GetMenu得到窗口菜单栏对象指针。

CMenu* GetSubMenu( ) ;//CMenu::GetSubMenu获得指向弹出菜单对象指针

UINT CheckMenuItem( );//CMenu::CheckMenuItem 添加选中标识

BOOL SetDefaultItem();//CMenu::SetDefaultItem 为指定菜单设置缺省菜单项

BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 设置位图标题菜单。

UINT EnableMenuItem();//CMenu::EnableMenuItem使菜单项有效,无效,或变灰。

第17 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在当前窗口上设置新菜单或移除菜单。

HMENU Detach( );//CMenu::Detach;断开一个菜单资源与相关的类对象句柄关联,可以定义局部对象,在使用完后调用Detach函数,则不会因为局部对象影响使用说明:

1)在计算子菜单菜单项的索引的时候,分隔栏符也算索引的。

2)int GetSystemMetrics()获取系统信息度量。可以用它来获取菜单标题的尺寸(后面还会使用到获取屏目尺寸)从而设置位图标题菜单中位图的大小。

3)在MFC中MFC为我们提供了一套命令更新机制,所有菜单项的更新都是由这套机制来完成的。所以要想利用CMenu::EnableMenuItem来自己控制菜单使用或不使用变灰等,必须要在CMainFrame的构造函数中将变量m_bAutoMenuEnable设置为FALSE。

EXAMPLE:

CMenu menu;//定义为局部对象

menu.LoadMenu(IDR_MAINFRAME);

SetMenu(&menu);

menu.Detach();// 这里menu对象作为一个局部对象。使用Detach()从menu对象中分离窗口菜单句柄,从而当menu对象析构的时候窗口菜单资源不随之销毁。

5.命令更新机制:

菜单项状态的维护是依赖于CN_UPDA TE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。

在后台操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd 接管并创建一个CCmdUI对象和第一个菜单项相关联,调用对象成员函数DoUpdate()(注:这个函数在MSDN中没有找到说明)发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。此后同一个CCmdUI对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。

更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。

说明:

1)可以手工或用ClassWizard来给菜单项添加UPDA TE_COMMAND_UI消息响应,利用响应函数中传进来的CCmdUI对象指针可完成设置菜单项可使用,不可使用,变灰,设置标记菜单等操作。

6,如果要想让工具栏上的某个图标与菜单项的某个菜单相关联,只需要将图标的ID设置为该菜单项的ID。

工具栏图标的索引记数顺序是:从做至右从0开始,分隔符也算索引号。

7,利用向项目中添加VC的POPMENU控件:Project->Add to Project->Components and Controls..

系统增加的内容:A,一个菜单资源;B,在派生View类中增加了OnContextMenu()函数

说明:

1)CWnd::OnContextMenu Called by the framework when the user has clicked the right mouse button (right clicked) in the window. You can process this message by displaying a context menu using the TrackPopupMenu.

第18 页共101 页

孙鑫C++视频教程--VC++深入详解笔记

2)BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );

//CMenu::TrackPopupMenu Displays a floating pop-up menu at the specified location and tracks the selection of items on the pop-up menu. A floating pop-up menu can appear anywhere on the screen.

8,利用调用TrackPopupMenu函数,手工添加弹出菜单:

1)用资源管理器添加一个菜单资源

2)在鼠标右键消息响应函数中,加载菜单资源,并获得要显示的子菜单指针,并用该指针调用TrackPopupMenu函数便完成任务(但要注意:鼠标响应函数传进来的坐标是客户区坐标,而TrackPopupMenu函数中使用的是屏幕坐标,在调用TrackPopupMenu 前要调用ClientToScreen客户区坐标到屏幕坐标的转换)

事例代码:

CMenu menu;

menu.LoadMenu(IDR_MENU1);

CMenu *pPopup=menu.GetSubMenu(0);

ClientToScreen(&point);

pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);

说明:

CWnd::ClientToScreen(..);//将一个坐标点或一个矩形区域坐标转换成屏幕坐标。CMenu::TrackPopupMenu(..);//在指定位置以指定的方式显示弹出菜单。

CWnd::ScreenToClient(..);

//Converts the screen coordinates of a given point or rectangle on the display to client coordinates.

9,当弹出菜单属于框架窗口的时候(可在TrackPopupMenu函数参数中设置),弹出菜单上的消息,在路由的时候,仍然遵循View-DOC-MainFrame-APP的响应顺序。

10,动态菜单编程:

所有的资源对象都有一个数据成员保存了资源的句柄。

CMenu::AppendMenu //Appends a new item to the end of a menu.

CMenu::CreatePopupMenu //Creates an empty pop-up menu and attaches it to a CMenu object.

CMenu::InsertMenu

//Inserts a new menu item at the position specified by nPosition and moves other items down the menu.

CMenu::GetSubMenu //Retrieves a pointer to a pop-up menu.

CWnd::GetMenu //Retrieves a pointer to the menu for this window.

CMenu::DeleteMenu //Deletes an item from the menu.

11,手动给动态菜单项添加响应函数:

在Resource.h中可以添加资源的ID

在头文件中写消息函数原型

第19 页共101 页

本文来源:https://www.bwwdw.com/article/084e.html

Top