第一集:课程安排
第二集:学习的内容如下图。
说明,windows应用程序API函数是通过C语言实现的,所以创建底层程序时是用.c为后缀的。这一集的内容是名词解释,windows消息处理机制(重点,可以查看其它资料深入理解,其整个过程下面有图片展示),窗口程序必须包含的头文件,程序的入口winMain及它的参数和创建一个有图形窗口程序的六个步骤。
第三集:学习的内容是上面六个步骤的前五个步骤。
代码如下(下面的代码包括第六个步骤的):
#include //底层实现窗口的头文件
//6.处理窗口过程
//CALLBACK 代表__stdcall参数的传递的顺序:从右到左以此入栈,并且在函数返回前清空堆栈
LRESULT CALLBACK WindowProc(
HWND hwnd, //消息所属窗口句柄
UINT uMsg, //具体消息名称,WM_XXX 消息名
WPARAM wParam, //键盘附加消息
LPARAM lParam //鼠标附加消息
)
{
switch(uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd); //DestroyWindow 发送另一个消息 WM_DESTROY
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN: //鼠标左键按下
{
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
char buf[1024];
wsprintf((LPTSTR)buf, TEXT("x = %d,y = %d"), xPos, yPos);
MessageBox(hwnd, (LPTSTR)buf, TEXT("鼠标左键按下"), MB_OK);
break;
}
case WM_KEYDOWN://键盘
MessageBox(hwnd, TEXT("键盘按下"), TEXT("键盘按下"), MB_OK);
break;
case WM_PAINT://绘图
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 100, 100, TEXT("HELLO"), strlen("HELLO"));
EndPaint(hwnd, &ps);
break;
}
}
//返回值用默认处理方式
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//程序入口函数
//WINAPI 代表__stdcall参数的传递顺序:从右到左以此入栈
int WINAPI WinMain(
HINSTANCE hInstance, //应用程序实例句柄,用来跟踪资源的指针
HINSTANCE hPrevInstance,//上一个运用程序实例句柄,在win32环境下参数一般为NULL,不起作用
LPSTR lpCmdLine,//char * argv[]
int nShowCmd) //窗口的显示命令;有最大化、最小化和正常三种
{
//1.设计窗口
//2.注册窗口
//3.创建窗口
//4.显示和更新
//5.通过循环取消息
//6.处理消息
HWND hwnd;
/*MSG结构体成员变量的意义
HWND hwnd; 主窗口句柄
UINT message; 具体消息名称
WPARAM wParam; 附加消息,比如键盘的具体消息
LPARAM lParam; 附加消息;鼠标消息,判断左右键
DWORD time; 消息产生的时间
POINT pt; 附加消息,确定鼠标的位置(x,y)
*/
MSG msg;
//1.设计窗口
WNDCLASS wc;
wc.cbClsExtra = 0; //类额外的内存
wc.cbWndExtra = 0; //窗口额外的内存
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//设置背景
wc.hCursor = LoadCursor(NULL, IDC_HAND); //设置光标,如果第一个参数为NULL,代表使用系统提供的光标
wc.hIcon = LoadIcon(NULL, IDI_ERROR); //图标,如果第一个参数为NULL,代表使用系统提供的图标
wc.hInstance = hInstance; //运用程序实例句柄,传入WinMain中的形参即可
wc.lpfnWndProc = WindowProc; //回调函数 窗口过程(名字可以任意,只要和定义的函数名相同)
wc.lpszClassName = TEXT("WIN"); //指定窗口类的名称
wc.lpszMenuName = NULL; //菜单名称
wc.style = 0; //显示风格 0表示默认风格
//2.注册窗口类
RegisterClass(&wc);
//3.创建窗口
/*
lpClassName, 类名
lpWindowName, 标题名
dwStyle, WS_OVERLAPPEDWINDOW 风格
x, y, 显示坐标 CW_USEDEFAULT默认值
nWidth, nHeight, 宽和高
hWndParent, 父窗口 NULL
hMenu, 菜单 NULL
hInstance, 实例句柄 hInstance
lpParam 附加值 NULL
*/
hwnd = CreateWindow(wc.lpszClassName, TEXT("WINDOWS"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
//4.显示和更新
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
//5.通过循环取消息
while(1)
{
/*
_Out_ LPMSG lpMsg, 消息
_In_opt_ HWND hWnd, 捕获窗口 填NULL代表捕获所有窗口
_In_ UINT wMsgFilterMin, 最小和最大的过滤的消息 一般填0
_In_ UINT wMsgFilterMax 填0代表捕获所有消息
*/
if(GetMessage(&msg, NULL, 0, 0) == FALSE)
{
break;
}
//翻译消息
TranslateMessage(&msg);
//分发消息
DispatchMessage(&msg);
}
//6.处理窗口过程
return 0;
}
说明:最重的是不懂的函数要学会查帮助文档。用windows应用程序API方法编码的时候可以查看MSDN(离线两个多G,可以查看在线文档)。在消息处理机制中,从应用程序到操作系统之间,有个分发信息,但是在分发信息之前需要进行信息的翻译,翻译后经过传递又会去消息队列排队。之所以需要翻译,是因为用户在用键盘输入的时候会用到组合键,比如Ctrl+C/V等,需要翻译成对应操作,而不是单纯的字符。
第四集:内容是写出处理窗口过程代码,即写出在窗口设计注释掉的那行代码的回调函数(回调函数的名字可以是其他,只要保证申明定义和调用一致就行)。具体代码如上面。
窗口过程如下:
部分代码截图如下:
case WM_CLOSE:
//所有以xxxWindow为结尾的方法都不会进入消息队列中,而是直接执行
DestroyWindow(hwnd); //DestroyWindow 发送另一个消息 WM_DESTROY
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
说明:上面有注释的代码是重点。它的完整过程是这样的:点击窗口的有上面的叉叉按钮,windows系统接收到消息,按照流程第一步、第二步、这个窗口的应用程序处理这个信息,运行到switch()中,DestroyWindow(hwnd)方法运行,发送一个WM_DESTROY的信息,windows系统接收到它,但现在并不去消息列队中排序,而是直接运行第四步窗口过程。(这个只是我自己想的,有待证实)
第五集:开始真的MFC学习了,现在使用的是C++,内容如下:
利用mfc创建窗口:
代码如下:
说明:在写MFC程序时,可能有些函数不懂如何使用,可以下载“VC++之MFC中文类库中文手册”。在查询的具体方法时,可能会找不到,这时候就需要确定该方法的作用域了,有时候可能要确定父类。(需要注意:右键项目 属性->常规->MFC的使用,选择在共享DLL使用MFC)
代码如下:
//mfc.h文件
#include //mfc头文件
//应用程序类CWinApp,派生类(子类)
class MyApp:public CWinApp
{
public:
//基类的虚函数,派生类只是重写
//MFC程序的入口函数
virtual BOOL InitInstance();
};
//框架类CFrameWnd,派生类(子类)
class MyFrame:public CFrameWnd
{
public:
MyFrame(); //构造函数
};
//mfc.cpp文件
#include"mfc.h" //对应类声明的头文件
MyApp app; //全局应用程序对象,有且仅有一个
BOOL MyApp::InitInstance()
{
//创建窗口
MyFrame *frame = new MyFrame;
//显示和更新
frame->ShowWindow(SW_SHOWNORMAL);
frame->UpdateWindow();
m_pMainWnd = frame; //保存指向应用程序的主窗口的指针
return TRUE; //返回正常初始化
}
MyFrame::MyFrame()
{
Create(NULL, TEXT("mfc")); //创建窗口
}
第六集:消息映射机制
mfc.h代码如下:
#include//mfc头文件
//应用程序类CWinApp,派生类(子类)
class MyApp:public CWinApp
{
public:
//基类的虚函数,派生类只是重写
//MFC程序的入口函数
virtual BOOL InitInstance();
};
//框架类CFrameWnd,派生类(子类)
class MyFrame:public CFrameWnd
{
public:
MyFrame();//构造函数
//声明消息映射,必须用在类声明中
//声明宏 提供消息机制
DECLARE_MESSAGE_MAP()
//鼠标左击处理函数申明
afx_msg void OnLButtonDown(UINT,CPoint);
//键盘点击处理函数申明
afx_msg void OnChar(UINT, UINT, UINT);
//绘图
afx_msg void OnPaint();
};
mfc.cpp文件代码如下:
#include"mfc.h" //对应类声明的头文件
MyApp app;//全局应用程序对象,有且仅有一个
BOOL MyApp::InitInstance()
{
//创建窗口
MyFrame* frame = new MyFrame;
//显示和更新
frame->showWindow(SW_SHDWNDRMAL);
fram->UpdateWindow();
m_pMainWnd = frame; //保存指向应用程序的主窗口的指针
return TRUE; //返回正常初始化
}
//定义消息宏,必须用在类实现中
//分界宏
BEGIN_MESSAGE_MAP(MyFrame,CFrameWnd)
ON_WM_LBUTTONDOWN() //鼠标左键按下
ON_WM_CHAR() //键盘
ON_WM_PAINT() //绘图
END_MESSAGE_MAP()
MyFrame::MyFrame()
{
Create(NULL,TEXT("mfc");//创建窗口
}
//鼠标左击处理函数的定义
void MyFrame::OnLButtonDown(UINT,CPoint point)
{
/*TCHAR buf[1024];
wspprintf(buf,TEXT("x = %d, y = %d"), point.x, point.y);
MessageBox(buf);
*/
//mfc中的字符串 CString
CString str;
str.Format(TEXT("x = %d,,, y = %d"), point.x, point.y);
MessageBox(str);
}
void MyFrame::OnChar(UINT key, UINT, UINT)
{
CString str;
str.Format(TEXT("按下了%c键"), key);
MessageBox(str);
}
void MyFrame::OnPaint()
{
CPaintDC dc(this);
dc.TextOutW(100,100,TEXT("为了部落"));
//画椭圆
dc.Ellipse(10,10,100,100);
}
第七集:windows字符集
代码如下:
//字母 1个字符对应一个1字节 多字节
//中文 1个字符对应多个字节 宽字节 (编码不同占用字节也就不同)
//MessageBox("aaa");//这样会报错,因为“aaa”是多字节,而一般vs使用的Unicode编码的(即用的宽字节)
//MessageBox(L"aaa");//就能通过行,和MessageBox(TEXT("aaa"))一样的效果
//TEXT()和TCHER()是自适应编码的转换
//统计字符串长度
int num = 0;
char *p = "aaaa";
num = strlen(p);
//统计宽字节的字符串长度
wchar_t *p2 = L"bbbb";
num = wcslen(p2);
//char * 与 CString 之间的装换. char * 是string和CString之间的桥梁
//char* -> CString
char *p3 = "ccc";
CString str = CString(p3);
//CString -> char *
CStringA tmp;
tmp = str;
char *pp = tmp.GetBuffer();
第八集:利用向导创建Mfc项目
第10,11集:模态(有阻塞)和非模态
基于对话框学习控件:
这次创建的是基于对话框的MFC,所以向导上有所不同,如下:
效果如下:
部分代码如下:
//弹出模态对话框
void CCDialog01Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CDlgExec dlg;
dlg.DoModal();
}
//弹出非模态对话框
void CCDialog01Dlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
//CDlgShow dlg; //如果写到这里,窗口会一闪而过。所以需要放到CDialog01Dlg.h文件中
//dlg.Create(IDD_SHOW); //只能创建一次,多次就会崩掉,所以需要放到OnInitDialog()函数中
dlg.ShowWindow(SW_SHOWNORMAL);
}