目录
资源的使用
带资源的.exe文件的编译方式
向窗口发送消息
菜单
加载菜单
菜单消息
图标
光标
快捷键
字符串
资源的使用
在VS2019中,点击视图下的其他窗口,资源视图,就可以看到本项目的所有资源
鼠标右键添加-资源,即可添加各种类型的资源
带资源的.exe文件的编译方式
不带资源的文件是如何编译的?
带资源的编译链接过程:
可以适应Editor查看到可执行文件的最后面就是.h的二进制文件
向窗口发送消息
SendMessage:将指定的消息发送到一个或多个窗口,它调用指定窗口的窗口过程,知道该窗口过程处理完该消息后才返回
LRESULT SendMessage(
[in] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
PostMessage:将消息投递到消息队列
BOOL PostMessageA(
[in, optional] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
区别:
- PostMessage :发送的消息进消息队列(阻塞式)
- SendMessage:发送的消息不进入消息队列,直接调用窗口过程函数。
参数说明:
- 参数1:指定窗口,是一个句柄
- 参数2:发送的消息
- 参数3:其他的消息特定信息。
- 参数4:其他的消息特定信息。
HWND hCalc = FindWindow("Notepad", NULL);
if (hCalc == NULL) {
return FALSE;
}
PostMessage(hCalc,WM_QUIT,0,NULL);
HWND hNotepad = FindWindow("Notepad", NULL);
if (hNotepad == NULL) {
return FALSE;
}
HWND hEdit = GetWindow(hNotepad, GW_CHILD);
PostMessage(hEdit, WM_KEYDOWN, 'A', 0);
PostMessage(hEdit, WM_KEYUP, 'B', 0);
PostMessage(hEdit, WM_KEYDOWN, 'C', 0);
HDC hdc = GetDC(hEdit);
while (TRUE) {
SetTextColor(hdc,RGB(255,0,0));
TextOut(hdc, 0, 0, "SB", 2);
}
ReleaseDC(hEdit, hdc);
菜单
加载菜单
方式一:通过代码创建菜单,子菜单
// 创建菜单资源
HMENU HMenu = CreateMenu();
// 给窗口添加菜单,向菜单中添加菜单项
BOOL ret;
ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
// 把菜单加载给窗口
SetMenu(hWnd, HMenu);
// 添加子菜单
HMENU hSubMenu = GetSubMenu(HMenu, 0);
ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));
// 把菜单加载给窗口
SetMenu(hWnd, HMenu);
AppendMenu()函数:将新项追加到指定菜单栏、下拉菜单、子菜单或快捷菜单的末尾。 可以使用此函数指定菜单项的内容、外观和行为。
BOOL AppendMenuA(
[in] HMENU hMenu,
[in] UINT uFlags,
[in] UINT_PTR uIDNewItem,
[in, optional] LPCSTR lpNewItem
);
参数说明:
- 参数1:菜单资源的句柄
- 参数2:控制新菜单项的外观和行为。MF_STRING:指定菜单项为文本字符串;MF_POPUP:指定菜单项打开下拉菜单或子菜单。
- 参数3:如果 uFlags 参数设置为 MF_POPUP,则为下拉菜单或子菜单的句柄。
- 参数4:如果有包含MF_STRING,指向以 null 结尾的字符串的指针。
返回值:如果该函数成功,则返回值为非零值。 如果函数失败,则返回值为零。
SetMenu()函数:将新菜单分配给指定的窗口。
BOOL SetMenu(
[in] HWND hWnd,
[in, optional] HMENU hMenu
);
参数说明:
- 参数1:要为其分配菜单的窗口的句柄。
- 参数2:新菜单的句柄
返回值:如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。
这种方式还需要手动声明宏定义
#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104
方式二:通过编辑菜单资源,给主窗口设计一个菜单,推荐使用
File菜单的属性中是没有ID的
下面的子菜单是有ID,可以通过快捷键 CTRL+O 打开
加载菜单
HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
SetMenu(hDlg, hMenu);
LoadMenu函数:获取菜单资源的句柄
HMENU LoadMenuA(
[in, optional] HINSTANCE hInstance,
[in] LPCSTR lpMenuName
);
参数说明:
- 参数1:该进程的实例句柄
- 参数2:MAKEINTRESOURCE(IDR_MENU1),加载资源
子窗口Open的ID是:ID_OPEN;子窗口Save的ID是:ID_SAVE
这种方式下,系统会在.h文件给出对应的宏定义,我们不需要管
菜单消息
WM_COMMAND:当点击菜单的时候,会响系统发送一条WM_COMMAND消息。当消息处理函数接受到WM_COMMAND消息的时候处理菜单选项内容。
作用:用于处理菜单或快捷键 都会发送COMMAND消息,有窗口过程函数处理,该消息的参数:
- 参数1:窗口句柄
- 参数2:消息ID,识别消息是来自于哪一个
- 参数3:低字可以拿到ID,高字可以拿到菜单的来源
- 参数4:控件句柄
处理WM_COMMAND消息实例
switch (uMsg) {
case WM_COMMAND:
if (LOWORD(wParam) == ID_SAVE) {
}
else if (LOWORD(wParam) == IDB_LOGIN) {
}
break;
通过LOWOED()拿到wParam参数的低字,这里面信息就是ID值,前面定义的ID_SAVE,ID_OPEN
这样可以根据不同的菜单选择给出对应的处理方式。
图标
图标资源
LoadIcon函数:获取图标资源的句柄
HICON LoadIconA(
[in, optional] HINSTANCE hInstance,
[in] LPCSTR lpIconName
);
参数说明:
- 参数1:DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的图标的文件。可以使用GetHandle();若要加载预定义的系统图标,请将此参数设置为 NULL。
- 参数2:
如果 hInstance 为非 NULL, 则 lpIconName 按名称或序号指定图标资源。 必须使用 MAKEINTRESOURCE 宏打包此序号;如果 hInstance 为 NULL, 则 lpIconName 将指定标识符
代码如下:
ws.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MN_ICON));
wc.hIcon = LoadIcon(NULL, IDI_HAND);
光标
光标资源
LoadCursor:获取光标资源的句柄
HCURSOR LoadCursorA(
[in, optional] HINSTANCE hInstance,
[in] LPCSTR lpCursorName
);
参数说明:
- 参数1:DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的游标的文件。使用GetMoudleHandle;请若要加载预定义的系统游标,请将此参数设置为 NULL。
- 参数2:如果 hInstance 为非 NULL, 则 lpCursorName 按名称或序号指定游标资源。 必须使用 MAKEINTRESOURCE 宏打包此序号;如果 hInstance 为 NULL, 则 lpCursorName 将指定标识符
返回值:如果函数成功,则返回值是新加载的游标的句柄。如果函数失败,则返回值为 NULL
代码如下:
ws.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_POINTER));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
快捷键
方式一:创建快捷键表
方式二:创建快捷键表
// 申请堆控件,因为快捷键需要全局访问
// 申请100字节的局部堆地址空间
// 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
// 申请局部堆地址
HLOCAL hMenu = LocalAlloc(LHND,100);
LPVOID lpMemory = LocalLock(hMenu);
LocalFree(hMenu);
// 申请全局堆地址
// 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
GlobalAlloc(LHND, 100);
// 申请堆空间
// 新的API,返回值是一个地址,前两个函数的底层是调用这个
//HeapAlloc();
// 申请堆地址空间,可以指定内存属性
//VirtualAlloc();
ACCEL *pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL)*2);
if (pAccelNews == nullptr) {
ShowErrorMsg();
return 0;
}
// 创建快捷键表
HACCEL hAccel = CreateAcceleratorTable(pAccelNews, 2);
if (hAccel == NULL) {
ShowErrorMsg();
return 0;
}
pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
pAccelNews[0].key = 'A';
pAccelNews[0].cmd = WM_COMMAND;
pAccelNews[1].fVirt = FALT | FCONTROL | FVIRTKEY;
pAccelNews[1].key = 'B';
pAccelNews[1].cmd = WM_COMMAND;
加载快捷键:
LoadAccelerators():加载指定的快捷键表。
HACCEL LoadAcceleratorsW(
[in, optional] HINSTANCE hInstance,
[in] LPCWSTR lpTableName
);
参数说明:
- 参数1:模块的句柄,其可执行文件包含要加载的加速器表。
- 参数2:要加载的快捷键表的名称。 或者,此参数可以在低序字中指定快捷键表资源的资源标识符,在高序字中指定零。 若要创建此值,请使用 MAKEINTRESOURCE 宏。
返回值:如果函数成功,则返回值是加载的加速器表的句柄;如果函数失败,则返回值为 NULL。
TranslateAccelerator():把按键消息转换成WM_COMMAND消息,处理菜单命令的快捷键。 如果指定快捷键表中) 键有条目,函数会将WM_KEYDOWN或WM_SYSKEYDOWN ( 消息转换为WM_COMMAND或WM_SYSCOMMAND消息,然后将WM_COMMAND或WM_SYSCOMMAND消息直接发送到指定的窗口过程。 在窗口过程处理完消息之前,TranslateAccelerator 不会返回
int TranslateAcceleratorA(
[in] HWND hWnd,
[in] HACCEL hAccTable,
[in] LPMSG lpMsg
);
参数说明:
- 参数1:要转换其消息的窗口的句柄。
- 参数2:快捷键表的句柄。 加速键表必须已通过对 LoadAccelerators 函数的调用加载或通过调用 CreateAcceleratorTable 函数创建。
- 参数3:指向 MSG 结构的指针,该结构包含使用 GetMessage 或 PeekMessage 函数从调用线程的消息队列检索到的消息信息。
返回值:如果该函数成功,则返回值为非零值;如果函数失败,则返回值为零。
代码如下:
HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));
BOOL bRet;
MSG msg;
// 消息循环是以线程为单位的
while ((bRet = GetMessage(&msg,NULL,0,0)) != 0) {
if (bRet == -1) {
break;
}
else {
if (!TranslateAccelerator(hDlg, hAccel, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
字符串
字符串资源:
LoadString():加载字符串资源
int LoadStringA(
[in, optional] HINSTANCE hInstance,
[in] UINT uID,
[out] LPSTR lpBuffer,
[in] int cchBufferMax
);
参数说明:
- 参数1:其可执行文件包含字符串资源的模块实例的句柄。 若要获取应用程序本身的句柄,请使用 NULL 调用 GetModuleHandle 函数。
- 参数2:要加载的字符串的标识符。
- 参数3:如果 cchBufferMax 为非零) ,则接收字符串的缓冲区 (;如果 cchBufferMax 为零) ,则为指向字符串资源本身 (只读指针。 必须具有足够的长度,才能将指针保留 (8 个字节) 。
- 参数4:缓冲区的大小(以字符为单位)。 如果字符串的长度超过指定的字符数,则字符串将被截断并以 null 结尾。 如果此参数为 0,则 lpBuffer 会收到指向字符串资源本身的只读指针。
示例代码
TCHAR szBuf[MAXBYTE];
TCHAR szTitle[MAXWORD];
LoadString(g_hInstance, IDS_SAVE, szBuf, sizeof(szBuf));
LoadString(g_hInstance, IDS_Title, szTitle, sizeof(szTitle));
MessageBox(hwndDlg, szBuf,szTitle, MB_OK);
总代码-1
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT OnMenu(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "Hello Win";
//设计窗口类
WNDCLASS ws;
ws.style = CS_HREDRAW | CS_VREDRAW;
ws.lpfnWndProc = &MyWndProc;
ws.cbClsExtra = NULL;
ws.cbWndExtra = NULL;
ws.hInstance = hInstance;
ws.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MN_ICON));
ws.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_POINTER));
ws.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
ws.lpszMenuName = NULL;//MAKEINTRESOURCE(MN_MAIN);
ws.lpszClassName = szAppName;
//注册窗口类
if (!RegisterClass(&ws))
{
MessageBox(NULL, "注册窗口失败", "错误提示:", MB_OK);
return 0;
}
//创建窗口
HWND HelloHwnd = CreateWindow(
szAppName,
"The Hello Program",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
LoadMenu(hInstance,MAKEINTRESOURCE(MN_MAIN)), //拿到资源的句柄
hInstance,
NULL,
);
//显示窗口
ShowWindow(HelloHwnd, iCmdShow);
//更新窗口,调用Begin使得画面有效
UpdateWindow(HelloHwnd);
SetTimer(HelloHwnd, 1, 100, NULL);
//加载快捷键资源
HACCEL haccel = LoadAccelerators(hInstance,MAKEINTRESOURCE(ACCEL_TEXT));
//建立消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(HelloHwnd,haccel,&msg))
{
TranslateMessage(&msg); //将msg结构传给Windows,进行一些键盘转换
DispatchMessage(&msg); //使得自动调用消息窗口句柄所属的窗口过程函数
}
}
return msg.wParam; //结构的wParam字段是传递给PostQuitMessage函数的值(通常是0)。然后return叙述将退出WinMain并终止程序。
}
//实现消息过程函数
LRESULT CALLBACK MyWndProc(
HWND hwnd, //
UINT message, //
WPARAM wParam, //参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏
LPARAM lParam //lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。
)
{
switch (message)
{
case WM_CREATE:
return 0;
case WM_COMMAND:
return OnMenu(hwnd, message, wParam, lParam);
case WM_DESTROY:
PostQuitMessage(0); //发送WM_QUIT以结束消息循环
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam); //操作系统待处理我们不处理的消息
}
//wParam 高位代表消息来源,地位代表ID
LRESULT OnMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//判断是否来自于菜单
if (HIWORD(wParam) == 0)
{
switch (LOWORD(wParam))
{
case MN_FILE_NEW:
MessageBox(NULL, "MN_FILE_NEW", "菜单选项:", MB_OK);
break;
case MN_FILE_OPEN:
MessageBox(NULL, "MN_FILE_OPEN", "菜单选项:", MB_OK);
break;
case MN_FILE_SAVE:
MessageBox(NULL, "MN_FILE_SAVE", "菜单选项:", MB_OK);
break;
case MN_EDT_REDO:
MessageBox(NULL, "MN_EDT_REDO", "菜单选项:", MB_OK);
break;
case MN_EDT_UNDO:
MessageBox(NULL, "MN_EDT_UNDO", "菜单选项:", MB_OK);
break;
default:
break;
}
}
//
else if (HIWORD(wParam) == 1)
{
switch (LOWORD(wParam))
{
case ACCEL_CTRL_A:
MessageBox(NULL, "ACCEL_CTRL_A", "菜单选项:", MB_OK);
break;
case ACCEL_CTRL_ALT_A:
MessageBox(NULL, "ACCEL_CTRL_ALT_A", "菜单选项:", MB_OK);
break;
default:
break;
}
}
return 0;
}
总代码-2
#include <windows.h>
#include <tchar.h>
#include <fstream>
#include <string>
#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104
using namespace std;
string g_Text;
TEXTMETRIC g_tm; // 字体信息
void ShowErrorMsg() {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
LocalFree(lpMsgBuf);
}
LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugString(_T("[51asm]: WM_Create\n"));
HDC hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
GetTextMetrics(hdc, &g_tm);
ReleaseDC(hwnd, hdc);
return TRUE;
}
LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugString(_T("[51asm]: WM_Close\n"));
DestroyWindow(hwnd);
return FALSE;
}
LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugString(_T("[51asm]: WM_Destory\n"));
PostMessage(hwnd, WM_QUIT, 0, NULL);
return TRUE;
}
LRESULT OnChar(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
TCHAR szBuf[MAXBYTE];
if ((char)wParam == '\r') {
g_Text += (char)wParam;
g_Text += '\n';
}
else if ((char)wParam == '\b') {
if (!g_Text.empty()) {
g_Text.pop_back();
}
}
else {
g_Text += (char)wParam;
}
wsprintf(szBuf, _T("[51asm] OnChar %s\n"), g_Text.data());
OutputDebugString(szBuf);
// // 获取窗口HDC,用API时一定要阅读文档
// // 获取一个新的句柄时,往往是需要释放的,否则该进程的内存会越来愈大
// //HDC hdc = GetWindowDC(hwnd); // 非客户区域
//
// HDC hdc = GetDC(hwnd);
//
// //TextOut(hdc, 0, 0, g_Text.data(), g_Text.length());
// // 获取窗口客户区域大小
// RECT rc;
// GetClientRect(hwnd, &rc);
//
// // 创建一个白色的刷子
// HGDIOBJ hBrushOld;
// HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
// //check,可能会失败,需要GetLastError
//
//
// // DC选择刷子
// hBrushOld = SelectObject(hdc, hBrush);
// //check
//
// // 绘制背景
// FillRect(hdc, &rc, hBrush);
// //check
//
// // 绘制文本
// DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
// //check
//
// // 还原默认刷子
// SelectObject(hdc, hBrushOld);
// //check
//
// // 释放刷子
// DeleteObject(hBrush);
// //check
//
// // 释放DC
// ReleaseDC(hwnd, hdc);
// //check
//
// SetCaretPos(g_tm.tmAveCharWidth * g_Text.length(), 0);
// ShowCaret(hwnd);
// 采用方式2:
RECT rc;
GetClientRect(hwnd, &rc);
// 把整个窗口设置为无效区域
InvalidateRect(hwnd, NULL, TRUE);
// 每当写入后就会产生WM_PAINT消息
return TRUE;
}
LRESULT OnSetFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnSetFocus\n"));
CreateCaret(hwnd, (HBITMAP)NULL, 1, g_tm.tmHeight);
SetCaretPos(0, 0);
ShowCaret(hwnd);
return TRUE;
}
LRESULT OnKillFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnKillFocus\n"));
DestroyCaret();
return TRUE;
}
// 绘制
LRESULT OnPaint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnPaint\n"));
// // 方式1
// HDC hdc = GetDC(hwnd);
//
// // 获取窗口客户区域大小
// RECT rc;
// GetClientRect(hwnd, &rc);
//
// // 绘制文本
// DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
// //check
//
// // 释放DC
// ReleaseDC(hwnd, hdc);
// //check
//
// // 将无效区域设置为有效区域
// ValidateRect(hwnd,&rc);
// 方式2:推荐
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
EndPaint(hwnd, &ps); // 自动把无效区域设置为有效区域
return TRUE;
// 无效区域,有变化的区域,系统需要重新绘制,WM_PAINT来了
// 有效区域,不需要变化,系统不需要冲洗绘制
// 设置无效区域为有效区域属于GDI的API
return TRUE;
}
// 擦除背景
LRESULT OnEraseBackground(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnEraseBackground\n"));
DestroyCaret();
return TRUE;
}
LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnCommand\n"));
WORD wID = LOWORD(wParam);
switch (wID) {
case IDM_OPEN:
MessageBox(NULL, "打开", "51asm", MB_OK);
break;
case IDM_EXIT:
PostQuitMessage(0); // 给自己投递QUIT消息
break;
}
return TRUE;
}
// 消息处理
// 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
// 先创建非客户区,再创建客户区,还有创建窗口等很多消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
LRESULT lResult = FALSE;
switch (uMsg) {
case WM_CREATE:
lResult = OnCreate(hwnd, uMsg, wParam, lParam);
break;
case WM_CLOSE:
lResult = OnClose(hwnd, uMsg, wParam, lParam);
break;
case WM_DESTROY:
lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
break;
case WM_CHAR:
lResult = OnChar(hwnd, uMsg, wParam, lParam);
break;
case WM_SETFOCUS:
lResult = OnSetFocus(hwnd, uMsg, wParam, lParam);
break;
case WM_KILLFOCUS:
lResult = OnKillFocus(hwnd, uMsg, wParam, lParam);
break;
case WM_ERASEBKGND: // 刷背景,最大化时候会刷背景,最小化不会刷
lResult = OnEraseBackground(hwnd, uMsg, wParam, lParam);
break;
case WM_PAINT: //绘制消息,窗口(界面)发生变化就会产生这个消息
lResult = OnPaint(hwnd, uMsg, wParam, lParam);
break;
case WM_COMMAND: //绘制消息,窗口(界面)发生变化就会产生这个消息
lResult = OnCommand(hwnd, uMsg, wParam, lParam);
break;
}
if (!lResult) {
return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
}
return lResult;
}
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd
) {
// 骚操作,在本窗口给别的窗口发消息
// 启动一个记事本,发送退出消息
// HWND hCalc = FindWindow("Notepad", NULL);
// if (hCalc == NULL) {
// return FALSE;
// }
// // 需要让GetMassage()拿到这个消息
// //SendMessage(hCalc,WM_QUIT,0,NULL); 调用对方过程函数,消息没进消息队列,处理不到
// //PostMessage();
// PostMessage(hCalc,WM_QUIT,0,NULL);
// HWND hNotepad = FindWindow("Notepad", NULL);
// if (hNotepad == NULL) {
// return FALSE;
// }
// HWND hEdit = GetWindow(hNotepad, GW_CHILD);
// PostMessage(hEdit, WM_KEYDOWN, 'A', 0);
// PostMessage(hEdit, WM_KEYUP, 'B', 0);
// PostMessage(hEdit, WM_KEYDOWN, 'C', 0);
//
// HDC hdc = GetDC(hEdit);
// while (TRUE) {
// SetTextColor(hdc,RGB(255,0,0));
// TextOut(hdc, 0, 0, "SB", 2);
// }
// ReleaseDC(hEdit, hdc);
// 创建窗口实例
TCHAR szWndClassName[] = { _T("CR41WndClassName") };
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_HAND);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wc.lpszMenuName = NULL;
wc.lpszClassName = szWndClassName;
// 注册窗口
if (RegisterClassEx(&wc) == 0) {
ShowErrorMsg();
return 0;
}
// 创建窗口
TCHAR szWndName[] = { _T("51asm") };
HWND hWnd = CreateWindowEx(0,
szWndClassName,
szWndName,
WS_OVERLAPPEDWINDOW, //组合属性,可拉伸窗口
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
if (hWnd == NULL) {
ShowErrorMsg();
return 0;
}
// 菜单
HMENU hMenu = CreateMenu();
// 弹出菜单
BOOL ret;
ret = AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hMenu, "文件(&F)");
ret = AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hMenu, "编辑(&E)");
SetMenu(hWnd, hMenu);
// 添加子菜单
HMENU hSubMenu = GetSubMenu(hMenu, 0);
ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, "打开(&O)");
ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, "保存(&O)");
ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, "退出(&O)");
SetMenu(hWnd, hMenu);
RECT rc;
GetClientRect(hWnd, &rc);
// 控件:带有特殊功能的窗口
// 按钮 编辑框
// HWND hEdit = CreateWindowEx(0,
// _T("Edit"),
// NULL,
// WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE, //组合属性,可拉伸窗口 最后一个表示支持多行
// 0,
// 0,
// rc.right - rc.left,
// rc.bottom - rc.top,
// hWnd,
// NULL,
// hInstance,
// NULL);
// 显示,更新窗口
ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
//ShowWindow(hChild, SW_SHOWNORMAL); 非子窗口需要单独show
UpdateWindow(hWnd); // 产生WM_PAINT
SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));
// 消息循环
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1) {
break;
}
else {
TranslateMessage(&msg);// 转换键盘消息
DispatchMessage(&msg); // 派发消息
}
}
return msg.wParam;
}
总代码-3:
#include <windows.h>
#include <tchar.h>
#include <fstream>
#include <string>
#define IDM_OPEN 102
#define IDM_SAVE 103
#define IDM_EXIT 104
using namespace std;
string g_Text;
TEXTMETRIC g_tm; // 字体信息
void ShowErrorMsg() {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
LocalFree(lpMsgBuf);
}
LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: WM_Create\n"));
return TRUE;
}
// 当你关闭窗口点击确定后就会向窗口发送WM_Destroy消息,就意味要销毁窗口,
LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: WM_Close\n"));
return FALSE;
}
// 这个工作可以交给系统处理,也可以自己处理
LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: WM_Destory\n"));
return TRUE;
}
LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm] OnCommand\n"));
WORD wID = LOWORD(wParam);
switch (wID) {
case IDM_OPEN:
MessageBox(NULL, _T("打开"), _T("51asm"), MB_OK);
break;
case IDM_EXIT:
PostQuitMessage(0); // 给自己投递QUIT消息
break;
}
return TRUE;
}
LRESULT OnKeyDown(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: OnKeyDown\n"));
return TRUE;
}
LRESULT OnKeyUp(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
OutputDebugString(_T("[51asm]: OnKeyUp\n"));
return TRUE;
}
// 消息处理
// 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
// 先创建非客户区,再创建客户区,还有创建窗口等很多消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
LRESULT lResult = FALSE;
switch (uMsg) {
case WM_CREATE:
lResult = OnCreate(hwnd, uMsg, wParam, lParam); // 表示处理WM_CREATE消息的函数
break;
case WM_CLOSE:
lResult = OnClose(hwnd, uMsg, wParam, lParam);
break;
case WM_DESTROY:
lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
break;
case WM_COMMAND: //绘制消息,窗口(界面)发生变化就会产生这个消息
lResult = OnCommand(hwnd, uMsg, wParam, lParam);
break;
case WM_KEYDOWN: //绘制消息,窗口(界面)发生变化就会产生这个消息
lResult = OnKeyDown(hwnd, uMsg, wParam, lParam);
break;
case WM_KEYUP: //绘制消息,窗口(界面)发生变化就会产生这个消息
lResult = OnKeyUp(hwnd, uMsg, wParam, lParam);
break;
}
if (!lResult) {
return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
}
return lResult;
}
/*
客户区(用户区)与非客户区(系统的) NC
*/
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd
) {
// 申请堆控件,因为快捷键需要全局访问
// 申请100字节的局部堆地址空间
// 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
// 申请局部堆地址
HLOCAL hMenu = LocalAlloc(LHND,100);
LPVOID lpMemory = LocalLock(hMenu);
LocalFree(hMenu);
// 申请全局堆地址
// 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
GlobalAlloc(LHND, 100);
// 申请堆空间
// 新的API,返回值是一个地址,前两个函数的底层是调用这个
//HeapAlloc();
// 申请堆地址空间,可以指定内存属性
//VirtualAlloc();
ACCEL *pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL)*2);
if (pAccelNews == nullptr) {
ShowErrorMsg();
return 0;
}
// 创建快捷键表
HACCEL hAccel = CreateAcceleratorTable(pAccelNews, 2);
if (hAccel == NULL) {
ShowErrorMsg();
return 0;
}
pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
pAccelNews[0].key = 'A';
pAccelNews[0].cmd = WM_COMMAND;
pAccelNews[1].fVirt = FALT | FCONTROL | FVIRTKEY;
pAccelNews[1].key = 'B';
pAccelNews[1].cmd = WM_COMMAND;
// 创建窗口实例
TCHAR szWndClassName[] = { _T("CR41WndClassName") };
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_HAND);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wc.lpszMenuName = NULL;
wc.lpszClassName = szWndClassName;
// 注册窗口
if (RegisterClassEx(&wc) == 0) {
ShowErrorMsg();
return 0;
}
// 创建窗口
TCHAR szWndName[] = { _T("51asm") };
HWND hWnd = CreateWindowEx(0,
szWndClassName,
szWndName,
WS_OVERLAPPEDWINDOW, //组合属性,可拉伸窗口
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
if (hWnd == NULL) {
ShowErrorMsg();
return 0;
}
// 菜单
HMENU HMenu = CreateMenu();
// 弹出菜单
BOOL ret;
ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
SetMenu(hWnd, HMenu);
// 添加子菜单
HMENU hSubMenu = GetSubMenu(HMenu, 0);
ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));
SetMenu(hWnd, HMenu);
// RECT rc;
// GetClientRect(hWnd, &rc);
// 显示,更新窗口
ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
//ShowWindow(hChild, SW_SHOWNORMAL); 非子窗口需要单独show
UpdateWindow(hWnd); // 产生WM_PAINT
SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));
// 消息循环
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1) {
break;
}
else {
// 转换快捷键消息 WM_COMMAND
if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
TranslateMessage(&msg);// 转换键盘消息
DispatchMessage(&msg); // 派发消息
}
}
}
// 删除快捷键表
DestroyAcceleratorTable(hAccel);
HeapFree(GetProcessHeap(), 0, pAccelNews);
return msg.wParam;
}