目录
1、底层实现窗口:
(1)、SDK
(2)、API
(3)、窗口和句柄
(4)、消息和消息队列
(5)、WinMain函数
2、窗口实现具体的六个步骤:
(1)、设计窗口 WNDCLASS
(2)、注册窗口 Registerclass
(3)、创建窗口 creatWindow
(4)、显示和更新 showWindow updateWindow
(5)、通过循环取消息 MSG msg
(6)、窗口过程,消息处理(回调函数)
(7)、底层窗口实现全部代码
(8)、运行界面
3、利用MFC创建窗口:
4、消息映射:
(1)、具体代码实现
(2)、分界宏之间的消息映射的查找
(3)、椭圆的绘制过程
5、Windows字符集:
(1)、多字节转换为宽字节
(2)、TCHAR自适应编码转换
(3)、统计字符串长度
(4)、char *和CString之间的转换
软件开发工具包(第三方写好的东西,直接用)。
Windows操作系统提供给应用程序编程的接口。Windows应用程序API函数通过C语言实现,所有主要的Windows函数都在Window.h头文件中声明。
窗口:分为客户区和非客户区,窗口可以有父窗口。
句柄:在Windows程序中,各种各样的资源(窗口、图标、光标)都会在创建时候分配内存,然后返回一个标识,标识就是句柄(每个句柄都是独立的),句柄都是以H开头。
例如关闭一个文档,操作系统首先捕获到关闭消息,放入消息队列中,文档程序来接收先进先出的消息,也就是关闭消息。文档把这个消息给操作系统来处理。
底层实现,类似于C/C++的main函数。首先头文件Windows.h。
创建一个窗口,并且在窗口中实现键盘鼠标消息,程序的实现步骤为:
1)、WinMain函数的定义
2)、创建一个窗口
3)、进行消息循环(也就是消息和消息队列的循环)
4)、编写窗口过程函数。
首先包含头文件windows.h,然后进入程序入口也就是WinMain函数
#include //底层实现窗口的文件
//程序的入口
//WINAPI代表参数的传递顺序,从右到左,以此入栈,并且在函数返回前清空堆栈
int WINAPI WinMain
(
HINSTANCE hInstance, //应用程序实例句柄
HINSTANCE hPrevInstance, //上一个应用程序句柄,在win32环境下,参数一般为NULL,不起作用
LPSTR lpCmdLine, //当前程序中的指令个数
int nShowCmd //显示命令 最大化 最小化 正常
)
{
//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
*/
HWND 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);
写循环while(1),GetMessage == false 退出循环,翻译消息,分发消息
//5、通过循环取消息
/*
HWND hwnd;主窗口的句柄
UINT message;具体的消息名称
WPARAM wParam;附加消息,键盘消息
LPARAM lParam;附加消息,鼠标消息
DWORD time;消息产生出的时间
POINT pt;附加消息,鼠标消息x,y
*/
MSG msg;
while (1)
{
/*
_Out_ LPMSG lpMsg, 消息
_In_opt_ HWND hWnd,捕获窗口,NULL代表捕获所有的窗口
_In_ UINT wMsgFilterMin,最小和最大过滤的消息
_In_ UINT wMsgFilterMax 0代表捕获所有的消息
*/
if (GetMessage(&msg, NULL, 0, 0) == FALSE)
{
break;
}
//翻译消息,例如消息是ctrl+c组合键得先翻译。
TranslateMessage(&msg);
//不为false
//分发消息
DispatchMessage(&msg);
}
补充:
windows API中很多接口都是宽字节版本所以使用多字节编码
1、多字节字符串,使用单字节字符编码(ASCII / UTF-8)在C语言中以NULL结尾。
2、宽字节字符串,使用多字节字符编码(UTF-16 / UTF-32)在C中以null结尾。
3、多字节转换为宽字节:宽字节用TEXT("鼠标按下")或者可以用L"鼠标按下了"。
例如wchar_t wstr[] = L"你好!"(UTF-16编码)
处理宽字节字符串要以w开头例如wprintf
可以通过判断系统是否支持宽字节
#ifdef _UNICODE
//编译器支持宽字节
#else
//不支持宽字节
#endif
//6、处理消息(窗口过程)
//CALLBACK代表参数的传递顺序,从右到左,以此入栈,并且在函数返回前清空堆栈
LRESULT CALLBACK WindowProc
(
HWND hwnd,//消息所属的窗口句柄
UINT uMsg,//具体的消息名称 WM__XXX消息名称
WPARAM wParam,//键盘附加消息
LPARAM lparam//鼠标附加消息
)
{
switch (uMsg)
{
case WM_CLOSE:
//所有以xxxWindow为结尾的方式都不会进入到消息队列而是直接执行
DestroyWindow(hwnd);//发送另一个消息WM_DESTROY
break;
case WM_DESTROY://接收之后关闭窗口
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN://鼠标左键按下
{
int xPos = LOWORD(lparam);
int yPos = HIWORD(lparam);
char buf[1024];
//wsprintf用于宽字节字符串拼接。
wsprintf(buf,TEXT("x = %d,y = %d"), xPos, yPos);
MessageBox(hwnd, buf, TEXT("鼠标左键按下"), MB_OK);
//MessageBox需要接收宽字节的字符串作为参数
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);
}
#include //底层实现窗口的文件
//6、处理消息(窗口过程)
//CALLBACK代表参数的传递顺序,从右到左,以此入栈,并且在函数返回前清空堆栈
LRESULT CALLBACK WindowProc
(
HWND hwnd,//消息所属的窗口句柄
UINT uMsg,//具体的消息名称 WM__XXX消息名称
WPARAM wParam,//键盘附加消息
LPARAM lparam//鼠标附加消息
)
{
switch (uMsg)
{
case WM_CLOSE:
//所有以xxxWindow为结尾的方式都不会进入到消息队列而是直接执行
DestroyWindow(hwnd);//发送另一个消息WM_DESTROY
break;
case WM_DESTROY://接收之后关闭窗口
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN://鼠标左键按下
{
int xPos = LOWORD(lparam);
int yPos = HIWORD(lparam);
char buf[1024];
//wsprintf用于宽字节字符串拼接。
wsprintf(buf,TEXT("x = %d,y = %d"), xPos, yPos);
MessageBox(hwnd, buf, TEXT("鼠标左键按下"), MB_OK);
//MessageBox需要接收宽字节的字符串作为参数
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代表参数的传递顺序,从右到左,以此入栈,并且在函数返回前清空堆栈
int WINAPI WinMain
(
HINSTANCE hInstance, //应用程序实例句柄
HINSTANCE hPrevInstance, //上一个应用程序句柄,在win32环境下,参数一般为NULL,不起作用
LPSTR lpCmdLine, //当前程序中的指令个数
int nShowCmd //显示命令 最大化 最小化 正常
)
{
//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
*/
HWND 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、通过循环取消息
/*
HWND hwnd;主窗口的句柄
UINT message;具体的消息名称
WPARAM wParam;附加消息,键盘消息
LPARAM lParam;附加消息,鼠标消息
DWORD time;消息产生出的时间
POINT pt;附加消息,鼠标消息x,y
*/
MSG msg;
while (1)
{
/*
_Out_ LPMSG lpMsg, 消息
_In_opt_ HWND hWnd,捕获窗口,NULL代表捕获所有的窗口
_In_ UINT wMsgFilterMin,最小和最大过滤的消息
_In_ UINT wMsgFilterMax 0代表捕获所有的消息
*/
if (GetMessage(&msg, NULL, 0, 0) == FALSE)
{
break;
}
//翻译消息,例如消息是ctrl+c组合键得先翻译。
TranslateMessage(&msg);
//不为false
//分发消息
DispatchMessage(&msg);
}
return 0;
}
MFC由C++编写,用底层实现窗口采用C语言编写。编写MFC程序需要包含头文件#include
。 MFC创建窗口步骤
1、首先包含头文件#include
2、在头文件中声明俩个自定义类MyApp和MyFrame,MyApp继承于CWinApp应用程序类,MyFrame继承于CFrameWnd窗口框架类
3、MyApp包含重写程序的入口 virtual BOOL InitInstance();MyFrame构造函数来初始化窗口
4、在.cpp文件中定义。InitInstance函数创建窗口,显示更新,指针指向自定义窗口,返回正常的初始化。MyFrame构造函数定义初始化窗口。
mfc.h
#include //mfc的头文件
class MyApp:public CWinApp //继承于CWinApp应用程序类
{
public:
//程序入口
virtual BOOL InitInstance();
};
class MyFrame:public CFrameWnd//继承于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函数是CFrameWnd的函数
Create(NULL, TEXT("mfc"));//初始化窗口
}
消息映射是将消息和函数相关联的表,包含俩个内容,消息ID(独一无二),和对应处理函数。
消息映射的步骤:
1、声明宏写在.h框架类中。声明可以使用消息映射机制。宏为DECLARE_MESSAGE_MAP.
2、分界宏写在.cpp中。分为开始宏和结束宏,中间时要处理的消息映射宏。
开始宏为BEGIN_MESSAGE_MAP 将消息处理函数和窗口相关联
中间宏为待处理的消息映射宏,例如鼠标按下消息映射,键盘,绘图
结束宏为END_MESSAGE_MAP
3、找消息宏写到分界宏中间
4、把函数原型、声明写到.h中
5、函数的实现写到CPP中
6、鼠标键盘绘图等实现。
mfc.h
#include //mfc的头文件
class MyApp:public CWinApp //继承于CWinApp应用程序类
{
public:
//程序入口
virtual BOOL InitInstance();
};
class MyFrame:public CFrameWnd//继承于CFrameWnd窗口框架类
{
DECLARE_MESSAGE_MAP(); //声明宏, 提供消息机制
public:
MyFrame();//构造函数用来初始化窗口
//声明鼠标左键按下的消息处理函数
afx_msg void OnLButtonDown(UINT, CPoint point);
//afx_msg宏用于标记MyFrame中的OnLButtonDown函数,当窗口接收到鼠标左键按下的消息时,自动调用该函数
//UNIT消息的类型,这个函数我们不需要使用消息类型
//CPoint point 参数类型和名称,CPoint时MFC提供的一个类,用于表示二维坐标中的信息
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_SHOWNORMAL);
frame->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];//TCHAR是mfc中的数组
//将坐标信息格式化到buf里面。
wsprintf(buf, TEXT("x =%d,y = %d"), point.x, point.y);
MessageBox(buf);//显示坐标信息对话框
*/
//在mfc中封装了一个CString,类似C++ STL
CString str;
//str中的方法Format格式化方便信息传入MessageBox中。
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);
/*
x1 指定椭圆外接矩形左上角的X逻辑坐标。
y1 指定椭圆外接矩形左上角的Y逻辑坐标。
x2 指定椭圆外接矩形右下角的X逻辑坐标。
y2 指定椭圆外接矩形右下角的Y逻辑坐标。
*/
}
(例如查找绘图椭圆)
x1,y1和x2,y2俩个坐标确定的长方形中间生成的椭圆。
英文 1个字符对应一个字节称为多字节
中文 1个字符对应多个字节称为宽字节(Unicode ,utf-8 3个字节,GBK 2个字节)
多字节转换成宽字节L例如 TEXT("hello")也可以写成 L"hello"。
TCHAR:它是一个宏定义,在不同的环境下自动解释为char或者wchar_t,在Unicode编码环境下,TCHAR会被解释为wchar_t,在非Unicode环境下会被解释为char,这样实现代码可移植性。
在非Unicode编码环境下(也就是多字节编码):
int num = 0;
char *p = "aaaa";
num = strlen(p);
在Unicode编码环境下(也就是宽字节编码):
int num = 0;
wchar_t *p = L"bbbbb";//这里可以写成TEXT("bbbb")
num = wcslen(p);
1、首先在C++中有个string类型和char *类型的转换
string类型转换成char *类型
std::string str = "hello";
const char *cstr = str.c_str();
std::cout<< "cstr = "<< cstr << std::endl;
char*类型转换成string类型
const char *cstr = "hello";
std::string str(cstr)//使用拷贝构造,里面函数已经封装好了
char *类型转换成CString类型
char * str = "hello";
CString cstr(str);
CString类型转换成char *类型
CString str = "hello";
CStringA temp;
temp = str;
char *s = temp.GetBuffer();
总结string类型转换成CString类型
//string类型转换成char *
//char *类型转换成CString
string str = "hello"
CString cstr(str.c_str());
wcout << "cstr: " << (LPCTSTR)cstr << endl;
//LPCTSTR 类型转换操作符,把CString对象转换成const TCHAR*类型,用来支持中文输出
CString类型转换成string类型
//CString类型转换成char *
//char *转换成string类型
CString cstr = "hello";
CStringA temp;
temp = cstr;
char * c = temp.GetBuffer();