• SDK 资源


    目录

    资源的使用

    带资源的.exe文件的编译方式

    向窗口发送消息

    菜单

    加载菜单

    菜单消息

    图标

    光标

    快捷键

    字符串


    资源的使用

    VS2019中,点击视图下的其他窗口,资源视图,就可以看到本项目的所有资源

    鼠标右键添加-资源,即可添加各种类型的资源

    带资源的.exe文件的编译方式

    不带资源的文件是如何编译的?

    带资源的编译链接过程:

    可以适应Editor查看到可执行文件的最后面就是.h的二进制文件

    向窗口发送消息

    SendMessage:将指定的消息发送到一个或多个窗口,它调用指定窗口的窗口过程,知道该窗口过程处理完该消息后才返回

    1. LRESULT SendMessage(
    2. [in] HWND hWnd,
    3. [in] UINT Msg,
    4. [in] WPARAM wParam,
    5. [in] LPARAM lParam
    6. );

    PostMessage:将消息投递到消息队列

    1. BOOL PostMessageA(
    2. [in, optional] HWND hWnd,
    3. [in] UINT Msg,
    4. [in] WPARAM wParam,
    5. [in] LPARAM lParam
    6. );

    区别:

    • PostMessage :发送的消息进消息队列(阻塞式)
    • SendMessage:发送的消息不进入消息队列,直接调用窗口过程函数。

    参数说明:

    • 参数1:指定窗口,是一个句柄
    • 参数2:发送的消息
    • 参数3:其他的消息特定信息。
    • 参数4:其他的消息特定信息。
    1. HWND hCalc = FindWindow("Notepad", NULL);
    2. if (hCalc == NULL) {
    3. return FALSE;
    4. }
    5. PostMessage(hCalc,WM_QUIT,0,NULL);
    6. HWND hNotepad = FindWindow("Notepad", NULL);
    7. if (hNotepad == NULL) {
    8. return FALSE;
    9. }
    10. HWND hEdit = GetWindow(hNotepad, GW_CHILD);
    11. PostMessage(hEdit, WM_KEYDOWN, 'A', 0);
    12. PostMessage(hEdit, WM_KEYUP, 'B', 0);
    13. PostMessage(hEdit, WM_KEYDOWN, 'C', 0);
    14. HDC hdc = GetDC(hEdit);
    15. while (TRUE) {
    16. SetTextColor(hdc,RGB(255,0,0));
    17. TextOut(hdc, 0, 0, "SB", 2);
    18. }
    19. ReleaseDC(hEdit, hdc);

    菜单

    加载菜单

    方式一:通过代码创建菜单,子菜单

    1. // 创建菜单资源
    2. HMENU HMenu = CreateMenu();
    3. // 给窗口添加菜单,向菜单中添加菜单项
    4. BOOL ret;
    5. ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
    6. ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
    7. // 把菜单加载给窗口
    8. SetMenu(hWnd, HMenu);
    9. // 添加子菜单
    10. HMENU hSubMenu = GetSubMenu(HMenu, 0);
    11. ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
    12. ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
    13. ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));
    14. // 把菜单加载给窗口
    15. SetMenu(hWnd, HMenu);

    AppendMenu()函数:将新项追加到指定菜单栏、下拉菜单、子菜单或快捷菜单的末尾。 可以使用此函数指定菜单项的内容、外观和行为。

    1. BOOL AppendMenuA(
    2. [in] HMENU hMenu,
    3. [in] UINT uFlags,
    4. [in] UINT_PTR uIDNewItem,
    5. [in, optional] LPCSTR lpNewItem
    6. );

    参数说明:

    • 参数1:菜单资源的句柄
    • 参数2:控制新菜单项的外观和行为。MF_STRING:指定菜单项为文本字符串;MF_POPUP:指定菜单项打开下拉菜单或子菜单。 
    • 参数3:如果 uFlags 参数设置为 MF_POPUP,则为下拉菜单或子菜单的句柄。
    • 参数4:如果有包含MF_STRING,指向以 null 结尾的字符串的指针。

    返回值:如果该函数成功,则返回值为非零值。 如果函数失败,则返回值为零。 

    SetMenu()函数:将新菜单分配给指定的窗口。

    1. BOOL SetMenu(
    2. [in] HWND hWnd,
    3. [in, optional] HMENU hMenu
    4. );

    参数说明:

    • 参数1:要为其分配菜单的窗口的句柄。
    • 参数2:新菜单的句柄

    返回值:如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。

    这种方式还需要手动声明宏定义

    1. #define IDM_OPEN 102
    2. #define IDM_SAVE 103
    3. #define IDM_EXIT 104

    方式二:通过编辑菜单资源,给主窗口设计一个菜单,推荐使用

    File菜单的属性中是没有ID的

    下面的子菜单是有ID,可以通过快捷键 CTRL+O 打开

    加载菜单

    1. HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
    2. SetMenu(hDlg, hMenu);

    LoadMenu函数:获取菜单资源的句柄

    1. HMENU LoadMenuA(
    2. [in, optional] HINSTANCE hInstance,
    3. [in] LPCSTR lpMenuName
    4. );

    参数说明:

    • 参数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消息实例

    1. switch (uMsg) {
    2. case WM_COMMAND:
    3. if (LOWORD(wParam) == ID_SAVE) {
    4. }
    5. else if (LOWORD(wParam) == IDB_LOGIN) {
    6. }
    7. break;

    通过LOWOED()拿到wParam参数的低字,这里面信息就是ID值,前面定义的ID_SAVE,ID_OPEN

    这样可以根据不同的菜单选择给出对应的处理方式。

    图标

    图标资源

    LoadIcon函数:获取图标资源的句柄

    1. HICON LoadIconA(
    2. [in, optional] HINSTANCE hInstance,
    3. [in] LPCSTR lpIconName
    4. );

    参数说明:

    • 参数1:DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的图标的文件。可以使用GetHandle();若要加载预定义的系统图标,请将此参数设置为 NULL
    • 参数2:

      如果 hInstance 为非 NULL, 则 lpIconName 按名称或序号指定图标资源。 必须使用 MAKEINTRESOURCE 宏打包此序号;如果 hInstance 为 NULL, 则 lpIconName 将指定标识符

    代码如下:

    1. ws.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MN_ICON));
    2. wc.hIcon = LoadIcon(NULL, IDI_HAND);

    光标

    光标资源

    LoadCursor:获取光标资源的句柄

    1. HCURSOR LoadCursorA(
    2. [in, optional] HINSTANCE hInstance,
    3. [in] LPCSTR lpCursorName
    4. );

    参数说明:

    • 参数1:DLL 或可执行文件 (.exe 模块的句柄,) 包含要加载的游标的文件。使用GetMoudleHandle;请若要加载预定义的系统游标,请将此参数设置为 NULL
    • 参数2:如果 hInstance 为非 NULL, 则 lpCursorName 按名称或序号指定游标资源。 必须使用 MAKEINTRESOURCE 宏打包此序号;如果 hInstance 为 NULL, 则 lpCursorName 将指定标识符

    返回值:如果函数成功,则返回值是新加载的游标的句柄。如果函数失败,则返回值为 NULL

    代码如下:

    1. ws.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_POINTER));
    2. wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    快捷键

    方式一:创建快捷键表

    方式二:创建快捷键表

    1. // 申请堆控件,因为快捷键需要全局访问
    2. // 申请100字节的局部堆地址空间
    3. // 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
    4. // 申请局部堆地址
    5. HLOCAL hMenu = LocalAlloc(LHND,100);
    6. LPVOID lpMemory = LocalLock(hMenu);
    7. LocalFree(hMenu);
    8. // 申请全局堆地址
    9. // 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
    10. GlobalAlloc(LHND, 100);
    11. // 申请堆空间
    12. // 新的API,返回值是一个地址,前两个函数的底层是调用这个
    13. //HeapAlloc();
    14. // 申请堆地址空间,可以指定内存属性
    15. //VirtualAlloc();
    16. ACCEL *pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL)*2);
    17. if (pAccelNews == nullptr) {
    18. ShowErrorMsg();
    19. return 0;
    20. }
    21. // 创建快捷键表
    22. HACCEL hAccel = CreateAcceleratorTable(pAccelNews, 2);
    23. if (hAccel == NULL) {
    24. ShowErrorMsg();
    25. return 0;
    26. }
    27. pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
    28. pAccelNews[0].key = 'A';
    29. pAccelNews[0].cmd = WM_COMMAND;
    30. pAccelNews[1].fVirt = FALT | FCONTROL | FVIRTKEY;
    31. pAccelNews[1].key = 'B';
    32. pAccelNews[1].cmd = WM_COMMAND;

    加载快捷键:

    LoadAccelerators():加载指定的快捷键表。

    1. HACCEL LoadAcceleratorsW(
    2. [in, optional] HINSTANCE hInstance,
    3. [in] LPCWSTR lpTableName
    4. );

    参数说明:

    • 参数1:模块的句柄,其可执行文件包含要加载的加速器表。
    • 参数2:要加载的快捷键表的名称。 或者,此参数可以在低序字中指定快捷键表资源的资源标识符,在高序字中指定零。 若要创建此值,请使用 MAKEINTRESOURCE 宏。

    返回值:如果函数成功,则返回值是加载的加速器表的句柄;如果函数失败,则返回值为 NULL。

    TranslateAccelerator():把按键消息转换成WM_COMMAND消息,处理菜单命令的快捷键。 如果指定快捷键表中) 键有条目,函数会将WM_KEYDOWN或WM_SYSKEYDOWN ( 消息转换为WM_COMMAND或WM_SYSCOMMAND消息,然后将WM_COMMANDWM_SYSCOMMAND消息直接发送到指定的窗口过程。 在窗口过程处理完消息之前,TranslateAccelerator 不会返回 

    1. int TranslateAcceleratorA(
    2. [in] HWND hWnd,
    3. [in] HACCEL hAccTable,
    4. [in] LPMSG lpMsg
    5. );

    参数说明:

    • 参数1:要转换其消息的窗口的句柄。
    • 参数2:快捷键表的句柄。 加速键表必须已通过对 LoadAccelerators 函数的调用加载或通过调用 CreateAcceleratorTable 函数创建。
    • 参数3:指向 MSG 结构的指针,该结构包含使用 GetMessage 或 PeekMessage 函数从调用线程的消息队列检索到的消息信息。

    返回值:如果该函数成功,则返回值为非零值;如果函数失败,则返回值为零。

    代码如下:

    1. HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));
    2. BOOL bRet;
    3. MSG msg;
    4. // 消息循环是以线程为单位的
    5. while ((bRet = GetMessage(&msg,NULL,0,0)) != 0) {
    6. if (bRet == -1) {
    7. break;
    8. }
    9. else {
    10. if (!TranslateAccelerator(hDlg, hAccel, &msg)) {
    11. TranslateMessage(&msg);
    12. DispatchMessage(&msg);
    13. }
    14. }
    15. }

    字符串

    字符串资源:

    LoadString():加载字符串资源

    1. int LoadStringA(
    2. [in, optional] HINSTANCE hInstance,
    3. [in] UINT uID,
    4. [out] LPSTR lpBuffer,
    5. [in] int cchBufferMax
    6. );

    参数说明:

    • 参数1:其可执行文件包含字符串资源的模块实例的句柄。 若要获取应用程序本身的句柄,请使用 NULL 调用 GetModuleHandle 函数。
    • 参数2:要加载的字符串的标识符。
    • 参数3:如果 cchBufferMax 为非零) ,则接收字符串的缓冲区 (;如果 cchBufferMax 为零) ,则为指向字符串资源本身 (只读指针。 必须具有足够的长度,才能将指针保留 (8 个字节) 。
    • 参数4:缓冲区的大小(以字符为单位)。 如果字符串的长度超过指定的字符数,则字符串将被截断并以 null 结尾。 如果此参数为 0,则 lpBuffer 会收到指向字符串资源本身的只读指针。

    示例代码

    1. TCHAR szBuf[MAXBYTE];
    2. TCHAR szTitle[MAXWORD];
    3. LoadString(g_hInstance, IDS_SAVE, szBuf, sizeof(szBuf));
    4. LoadString(g_hInstance, IDS_Title, szTitle, sizeof(szTitle));
    5. MessageBox(hwndDlg, szBuf,szTitle, MB_OK);

    总代码-1

    1. #include <windows.h>
    2. #include "resource.h"
    3. LRESULT CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM);
    4. LRESULT OnMenu(HWND, UINT, WPARAM, LPARAM);
    5. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    6. PSTR szCmdLine, int iCmdShow)
    7. {
    8. static char szAppName[] = "Hello Win";
    9. //设计窗口类
    10. WNDCLASS ws;
    11. ws.style = CS_HREDRAW | CS_VREDRAW;
    12. ws.lpfnWndProc = &MyWndProc;
    13. ws.cbClsExtra = NULL;
    14. ws.cbWndExtra = NULL;
    15. ws.hInstance = hInstance;
    16. ws.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(MN_ICON));
    17. ws.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_POINTER));
    18. ws.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    19. ws.lpszMenuName = NULL;//MAKEINTRESOURCE(MN_MAIN);
    20. ws.lpszClassName = szAppName;
    21. //注册窗口类
    22. if (!RegisterClass(&ws))
    23. {
    24. MessageBox(NULL, "注册窗口失败", "错误提示:", MB_OK);
    25. return 0;
    26. }
    27. //创建窗口
    28. HWND HelloHwnd = CreateWindow(
    29. szAppName,
    30. "The Hello Program",
    31. WS_OVERLAPPEDWINDOW,
    32. CW_USEDEFAULT,
    33. CW_USEDEFAULT,
    34. CW_USEDEFAULT,
    35. CW_USEDEFAULT,
    36. NULL,
    37. LoadMenu(hInstance,MAKEINTRESOURCE(MN_MAIN)), //拿到资源的句柄
    38. hInstance,
    39. NULL,
    40. );
    41. //显示窗口
    42. ShowWindow(HelloHwnd, iCmdShow);
    43. //更新窗口,调用Begin使得画面有效
    44. UpdateWindow(HelloHwnd);
    45. SetTimer(HelloHwnd, 1, 100, NULL);
    46. //加载快捷键资源
    47. HACCEL haccel = LoadAccelerators(hInstance,MAKEINTRESOURCE(ACCEL_TEXT));
    48. //建立消息循环
    49. MSG msg;
    50. while (GetMessage(&msg, NULL, 0, 0))
    51. {
    52. if (!TranslateAccelerator(HelloHwnd,haccel,&msg))
    53. {
    54. TranslateMessage(&msg); //将msg结构传给Windows,进行一些键盘转换
    55. DispatchMessage(&msg); //使得自动调用消息窗口句柄所属的窗口过程函数
    56. }
    57. }
    58. return msg.wParam; //结构的wParam字段是传递给PostQuitMessage函数的值(通常是0)。然后return叙述将退出WinMain并终止程序。
    59. }
    60. //实现消息过程函数
    61. LRESULT CALLBACK MyWndProc(
    62. HWND hwnd, //
    63. UINT message, //
    64. WPARAM wParam, //参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏
    65. LPARAM lParam //lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。
    66. )
    67. {
    68. switch (message)
    69. {
    70. case WM_CREATE:
    71. return 0;
    72. case WM_COMMAND:
    73. return OnMenu(hwnd, message, wParam, lParam);
    74. case WM_DESTROY:
    75. PostQuitMessage(0); //发送WM_QUIT以结束消息循环
    76. return 0;
    77. }
    78. return DefWindowProc(hwnd, message, wParam, lParam); //操作系统待处理我们不处理的消息
    79. }
    80. //wParam 高位代表消息来源,地位代表ID
    81. LRESULT OnMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    82. {
    83. //判断是否来自于菜单
    84. if (HIWORD(wParam) == 0)
    85. {
    86. switch (LOWORD(wParam))
    87. {
    88. case MN_FILE_NEW:
    89. MessageBox(NULL, "MN_FILE_NEW", "菜单选项:", MB_OK);
    90. break;
    91. case MN_FILE_OPEN:
    92. MessageBox(NULL, "MN_FILE_OPEN", "菜单选项:", MB_OK);
    93. break;
    94. case MN_FILE_SAVE:
    95. MessageBox(NULL, "MN_FILE_SAVE", "菜单选项:", MB_OK);
    96. break;
    97. case MN_EDT_REDO:
    98. MessageBox(NULL, "MN_EDT_REDO", "菜单选项:", MB_OK);
    99. break;
    100. case MN_EDT_UNDO:
    101. MessageBox(NULL, "MN_EDT_UNDO", "菜单选项:", MB_OK);
    102. break;
    103. default:
    104. break;
    105. }
    106. }
    107. //
    108. else if (HIWORD(wParam) == 1)
    109. {
    110. switch (LOWORD(wParam))
    111. {
    112. case ACCEL_CTRL_A:
    113. MessageBox(NULL, "ACCEL_CTRL_A", "菜单选项:", MB_OK);
    114. break;
    115. case ACCEL_CTRL_ALT_A:
    116. MessageBox(NULL, "ACCEL_CTRL_ALT_A", "菜单选项:", MB_OK);
    117. break;
    118. default:
    119. break;
    120. }
    121. }
    122. return 0;
    123. }

    总代码-2

    1. #include <windows.h>
    2. #include <tchar.h>
    3. #include <fstream>
    4. #include <string>
    5. #define IDM_OPEN 102
    6. #define IDM_SAVE 103
    7. #define IDM_EXIT 104
    8. using namespace std;
    9. string g_Text;
    10. TEXTMETRIC g_tm; // 字体信息
    11. void ShowErrorMsg() {
    12. LPVOID lpMsgBuf;
    13. FormatMessage(
    14. FORMAT_MESSAGE_ALLOCATE_BUFFER |
    15. FORMAT_MESSAGE_FROM_SYSTEM |
    16. FORMAT_MESSAGE_IGNORE_INSERTS,
    17. NULL,
    18. GetLastError(),
    19. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    20. (LPTSTR)&lpMsgBuf,
    21. 0,
    22. NULL
    23. );
    24. MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
    25. LocalFree(lpMsgBuf);
    26. }
    27. LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    28. {
    29. OutputDebugString(_T("[51asm]: WM_Create\n"));
    30. HDC hdc = GetDC(hwnd);
    31. SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
    32. GetTextMetrics(hdc, &g_tm);
    33. ReleaseDC(hwnd, hdc);
    34. return TRUE;
    35. }
    36. LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    37. {
    38. OutputDebugString(_T("[51asm]: WM_Close\n"));
    39. DestroyWindow(hwnd);
    40. return FALSE;
    41. }
    42. LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    43. {
    44. OutputDebugString(_T("[51asm]: WM_Destory\n"));
    45. PostMessage(hwnd, WM_QUIT, 0, NULL);
    46. return TRUE;
    47. }
    48. LRESULT OnChar(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    49. {
    50. TCHAR szBuf[MAXBYTE];
    51. if ((char)wParam == '\r') {
    52. g_Text += (char)wParam;
    53. g_Text += '\n';
    54. }
    55. else if ((char)wParam == '\b') {
    56. if (!g_Text.empty()) {
    57. g_Text.pop_back();
    58. }
    59. }
    60. else {
    61. g_Text += (char)wParam;
    62. }
    63. wsprintf(szBuf, _T("[51asm] OnChar %s\n"), g_Text.data());
    64. OutputDebugString(szBuf);
    65. // // 获取窗口HDC,用API时一定要阅读文档
    66. // // 获取一个新的句柄时,往往是需要释放的,否则该进程的内存会越来愈大
    67. // //HDC hdc = GetWindowDC(hwnd); // 非客户区域
    68. //
    69. // HDC hdc = GetDC(hwnd);
    70. //
    71. // //TextOut(hdc, 0, 0, g_Text.data(), g_Text.length());
    72. // // 获取窗口客户区域大小
    73. // RECT rc;
    74. // GetClientRect(hwnd, &rc);
    75. //
    76. // // 创建一个白色的刷子
    77. // HGDIOBJ hBrushOld;
    78. // HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
    79. // //check,可能会失败,需要GetLastError
    80. //
    81. //
    82. // // DC选择刷子
    83. // hBrushOld = SelectObject(hdc, hBrush);
    84. // //check
    85. //
    86. // // 绘制背景
    87. // FillRect(hdc, &rc, hBrush);
    88. // //check
    89. //
    90. // // 绘制文本
    91. // DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
    92. // //check
    93. //
    94. // // 还原默认刷子
    95. // SelectObject(hdc, hBrushOld);
    96. // //check
    97. //
    98. // // 释放刷子
    99. // DeleteObject(hBrush);
    100. // //check
    101. //
    102. // // 释放DC
    103. // ReleaseDC(hwnd, hdc);
    104. // //check
    105. //
    106. // SetCaretPos(g_tm.tmAveCharWidth * g_Text.length(), 0);
    107. // ShowCaret(hwnd);
    108. // 采用方式2
    109. RECT rc;
    110. GetClientRect(hwnd, &rc);
    111. // 把整个窗口设置为无效区域
    112. InvalidateRect(hwnd, NULL, TRUE);
    113. // 每当写入后就会产生WM_PAINT消息
    114. return TRUE;
    115. }
    116. LRESULT OnSetFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    117. OutputDebugString(_T("[51asm] OnSetFocus\n"));
    118. CreateCaret(hwnd, (HBITMAP)NULL, 1, g_tm.tmHeight);
    119. SetCaretPos(0, 0);
    120. ShowCaret(hwnd);
    121. return TRUE;
    122. }
    123. LRESULT OnKillFocus(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    124. OutputDebugString(_T("[51asm] OnKillFocus\n"));
    125. DestroyCaret();
    126. return TRUE;
    127. }
    128. // 绘制
    129. LRESULT OnPaint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    130. OutputDebugString(_T("[51asm] OnPaint\n"));
    131. // // 方式1
    132. // HDC hdc = GetDC(hwnd);
    133. //
    134. // // 获取窗口客户区域大小
    135. // RECT rc;
    136. // GetClientRect(hwnd, &rc);
    137. //
    138. // // 绘制文本
    139. // DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
    140. // //check
    141. //
    142. // // 释放DC
    143. // ReleaseDC(hwnd, hdc);
    144. // //check
    145. //
    146. // // 将无效区域设置为有效区域
    147. // ValidateRect(hwnd,&rc);
    148. // 方式2:推荐
    149. PAINTSTRUCT ps;
    150. HDC hdc = BeginPaint(hwnd, &ps);
    151. RECT rc;
    152. GetClientRect(hwnd, &rc);
    153. DrawText(hdc, g_Text.data(), g_Text.length(), &rc, DT_LEFT);
    154. EndPaint(hwnd, &ps); // 自动把无效区域设置为有效区域
    155. return TRUE;
    156. // 无效区域,有变化的区域,系统需要重新绘制,WM_PAINT来了
    157. // 有效区域,不需要变化,系统不需要冲洗绘制
    158. // 设置无效区域为有效区域属于GDI的API
    159. return TRUE;
    160. }
    161. // 擦除背景
    162. LRESULT OnEraseBackground(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    163. OutputDebugString(_T("[51asm] OnEraseBackground\n"));
    164. DestroyCaret();
    165. return TRUE;
    166. }
    167. LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    168. OutputDebugString(_T("[51asm] OnCommand\n"));
    169. WORD wID = LOWORD(wParam);
    170. switch (wID) {
    171. case IDM_OPEN:
    172. MessageBox(NULL, "打开", "51asm", MB_OK);
    173. break;
    174. case IDM_EXIT:
    175. PostQuitMessage(0); // 给自己投递QUIT消息
    176. break;
    177. }
    178. return TRUE;
    179. }
    180. // 消息处理
    181. // 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
    182. // 先创建非客户区,再创建客户区,还有创建窗口等很多消息
    183. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    184. LRESULT lResult = FALSE;
    185. switch (uMsg) {
    186. case WM_CREATE:
    187. lResult = OnCreate(hwnd, uMsg, wParam, lParam);
    188. break;
    189. case WM_CLOSE:
    190. lResult = OnClose(hwnd, uMsg, wParam, lParam);
    191. break;
    192. case WM_DESTROY:
    193. lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
    194. break;
    195. case WM_CHAR:
    196. lResult = OnChar(hwnd, uMsg, wParam, lParam);
    197. break;
    198. case WM_SETFOCUS:
    199. lResult = OnSetFocus(hwnd, uMsg, wParam, lParam);
    200. break;
    201. case WM_KILLFOCUS:
    202. lResult = OnKillFocus(hwnd, uMsg, wParam, lParam);
    203. break;
    204. case WM_ERASEBKGND: // 刷背景,最大化时候会刷背景,最小化不会刷
    205. lResult = OnEraseBackground(hwnd, uMsg, wParam, lParam);
    206. break;
    207. case WM_PAINT: //绘制消息,窗口(界面)发生变化就会产生这个消息
    208. lResult = OnPaint(hwnd, uMsg, wParam, lParam);
    209. break;
    210. case WM_COMMAND: //绘制消息,窗口(界面)发生变化就会产生这个消息
    211. lResult = OnCommand(hwnd, uMsg, wParam, lParam);
    212. break;
    213. }
    214. if (!lResult) {
    215. return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
    216. }
    217. return lResult;
    218. }
    219. int WINAPI wWinMain(
    220. HINSTANCE hInstance,
    221. HINSTANCE hPrevInstance,
    222. LPWSTR lpCmdLine,
    223. int nShowCmd
    224. ) {
    225. // 骚操作,在本窗口给别的窗口发消息
    226. // 启动一个记事本,发送退出消息
    227. // HWND hCalc = FindWindow("Notepad", NULL);
    228. // if (hCalc == NULL) {
    229. // return FALSE;
    230. // }
    231. // // 需要让GetMassage()拿到这个消息
    232. // //SendMessage(hCalc,WM_QUIT,0,NULL); 调用对方过程函数,消息没进消息队列,处理不到
    233. // //PostMessage();
    234. // PostMessage(hCalc,WM_QUIT,0,NULL);
    235. // HWND hNotepad = FindWindow("Notepad", NULL);
    236. // if (hNotepad == NULL) {
    237. // return FALSE;
    238. // }
    239. // HWND hEdit = GetWindow(hNotepad, GW_CHILD);
    240. // PostMessage(hEdit, WM_KEYDOWN, 'A', 0);
    241. // PostMessage(hEdit, WM_KEYUP, 'B', 0);
    242. // PostMessage(hEdit, WM_KEYDOWN, 'C', 0);
    243. //
    244. // HDC hdc = GetDC(hEdit);
    245. // while (TRUE) {
    246. // SetTextColor(hdc,RGB(255,0,0));
    247. // TextOut(hdc, 0, 0, "SB", 2);
    248. // }
    249. // ReleaseDC(hEdit, hdc);
    250. // 创建窗口实例
    251. TCHAR szWndClassName[] = { _T("CR41WndClassName") };
    252. WNDCLASSEX wc = { 0 };
    253. wc.cbSize = sizeof(WNDCLASSEX);
    254. wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
    255. wc.lpfnWndProc = WindowProc;
    256. wc.hInstance = hInstance;
    257. wc.hIcon = LoadIcon(NULL, IDI_HAND);
    258. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    259. wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
    260. wc.lpszMenuName = NULL;
    261. wc.lpszClassName = szWndClassName;
    262. // 注册窗口
    263. if (RegisterClassEx(&wc) == 0) {
    264. ShowErrorMsg();
    265. return 0;
    266. }
    267. // 创建窗口
    268. TCHAR szWndName[] = { _T("51asm") };
    269. HWND hWnd = CreateWindowEx(0,
    270. szWndClassName,
    271. szWndName,
    272. WS_OVERLAPPEDWINDOW, //组合属性,可拉伸窗口
    273. CW_USEDEFAULT,
    274. CW_USEDEFAULT,
    275. CW_USEDEFAULT,
    276. CW_USEDEFAULT,
    277. NULL,
    278. NULL,
    279. hInstance,
    280. NULL);
    281. if (hWnd == NULL) {
    282. ShowErrorMsg();
    283. return 0;
    284. }
    285. // 菜单
    286. HMENU hMenu = CreateMenu();
    287. // 弹出菜单
    288. BOOL ret;
    289. ret = AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hMenu, "文件(&F)");
    290. ret = AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT_PTR)hMenu, "编辑(&E)");
    291. SetMenu(hWnd, hMenu);
    292. // 添加子菜单
    293. HMENU hSubMenu = GetSubMenu(hMenu, 0);
    294. ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, "打开(&O)");
    295. ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, "保存(&O)");
    296. ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, "退出(&O)");
    297. SetMenu(hWnd, hMenu);
    298. RECT rc;
    299. GetClientRect(hWnd, &rc);
    300. // 控件:带有特殊功能的窗口
    301. // 按钮 编辑框
    302. // HWND hEdit = CreateWindowEx(0,
    303. // _T("Edit"),
    304. // NULL,
    305. // WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE, //组合属性,可拉伸窗口 最后一个表示支持多行
    306. // 0,
    307. // 0,
    308. // rc.right - rc.left,
    309. // rc.bottom - rc.top,
    310. // hWnd,
    311. // NULL,
    312. // hInstance,
    313. // NULL);
    314. // 显示,更新窗口
    315. ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
    316. //ShowWindow(hChild, SW_SHOWNORMAL); 非子窗口需要单独show
    317. UpdateWindow(hWnd); // 产生WM_PAINT
    318. SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));
    319. // 消息循环
    320. BOOL bRet;
    321. MSG msg;
    322. while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    323. {
    324. if (bRet == -1) {
    325. break;
    326. }
    327. else {
    328. TranslateMessage(&msg);// 转换键盘消息
    329. DispatchMessage(&msg); // 派发消息
    330. }
    331. }
    332. return msg.wParam;
    333. }

    总代码-3:

    1. #include <windows.h>
    2. #include <tchar.h>
    3. #include <fstream>
    4. #include <string>
    5. #define IDM_OPEN 102
    6. #define IDM_SAVE 103
    7. #define IDM_EXIT 104
    8. using namespace std;
    9. string g_Text;
    10. TEXTMETRIC g_tm; // 字体信息
    11. void ShowErrorMsg() {
    12. LPVOID lpMsgBuf;
    13. FormatMessage(
    14. FORMAT_MESSAGE_ALLOCATE_BUFFER |
    15. FORMAT_MESSAGE_FROM_SYSTEM |
    16. FORMAT_MESSAGE_IGNORE_INSERTS,
    17. NULL,
    18. GetLastError(),
    19. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    20. (LPTSTR)&lpMsgBuf,
    21. 0,
    22. NULL
    23. );
    24. MessageBox(NULL, (LPCTSTR)lpMsgBuf, _T("ERROR"), MB_OK | MB_ICONINFORMATION);
    25. LocalFree(lpMsgBuf);
    26. }
    27. LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    28. OutputDebugString(_T("[51asm]: WM_Create\n"));
    29. return TRUE;
    30. }
    31. // 当你关闭窗口点击确定后就会向窗口发送WM_Destroy消息,就意味要销毁窗口,
    32. LRESULT OnClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    33. OutputDebugString(_T("[51asm]: WM_Close\n"));
    34. return FALSE;
    35. }
    36. // 这个工作可以交给系统处理,也可以自己处理
    37. LRESULT OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    38. OutputDebugString(_T("[51asm]: WM_Destory\n"));
    39. return TRUE;
    40. }
    41. LRESULT OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    42. OutputDebugString(_T("[51asm] OnCommand\n"));
    43. WORD wID = LOWORD(wParam);
    44. switch (wID) {
    45. case IDM_OPEN:
    46. MessageBox(NULL, _T("打开"), _T("51asm"), MB_OK);
    47. break;
    48. case IDM_EXIT:
    49. PostQuitMessage(0); // 给自己投递QUIT消息
    50. break;
    51. }
    52. return TRUE;
    53. }
    54. LRESULT OnKeyDown(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    55. OutputDebugString(_T("[51asm]: OnKeyDown\n"));
    56. return TRUE;
    57. }
    58. LRESULT OnKeyUp(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    59. OutputDebugString(_T("[51asm]: OnKeyUp\n"));
    60. return TRUE;
    61. }
    62. // 消息处理
    63. // 可以下断点debug调试分析消息,在监视这里可以 uMsg.wm 可以以看到
    64. // 先创建非客户区,再创建客户区,还有创建窗口等很多消息
    65. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    66. LRESULT lResult = FALSE;
    67. switch (uMsg) {
    68. case WM_CREATE:
    69. lResult = OnCreate(hwnd, uMsg, wParam, lParam); // 表示处理WM_CREATE消息的函数
    70. break;
    71. case WM_CLOSE:
    72. lResult = OnClose(hwnd, uMsg, wParam, lParam);
    73. break;
    74. case WM_DESTROY:
    75. lResult = OnDestroy(hwnd, uMsg, wParam, lParam);
    76. break;
    77. case WM_COMMAND: //绘制消息,窗口(界面)发生变化就会产生这个消息
    78. lResult = OnCommand(hwnd, uMsg, wParam, lParam);
    79. break;
    80. case WM_KEYDOWN: //绘制消息,窗口(界面)发生变化就会产生这个消息
    81. lResult = OnKeyDown(hwnd, uMsg, wParam, lParam);
    82. break;
    83. case WM_KEYUP: //绘制消息,窗口(界面)发生变化就会产生这个消息
    84. lResult = OnKeyUp(hwnd, uMsg, wParam, lParam);
    85. break;
    86. }
    87. if (!lResult) {
    88. return DefWindowProc(hwnd, uMsg, wParam, lParam); // 默认窗口过程处理函数,包括销毁窗口
    89. }
    90. return lResult;
    91. }
    92. /*
    93. 客户区(用户区)与非客户区(系统的) NC
    94. */
    95. int WINAPI wWinMain(
    96. HINSTANCE hInstance,
    97. HINSTANCE hPrevInstance,
    98. LPWSTR lpCmdLine,
    99. int nShowCmd
    100. ) {
    101. // 申请堆控件,因为快捷键需要全局访问
    102. // 申请100字节的局部堆地址空间
    103. // 这样设计的原因,内存空间申请释放,产生了很多碎片,通过把这些碎片整理得到一个更大的内存空间,这是地址变化了
    104. // 申请局部堆地址
    105. HLOCAL hMenu = LocalAlloc(LHND,100);
    106. LPVOID lpMemory = LocalLock(hMenu);
    107. LocalFree(hMenu);
    108. // 申请全局堆地址
    109. // 原因:16位系统,有全局堆和局部堆;32位,两者合并,只有一个堆
    110. GlobalAlloc(LHND, 100);
    111. // 申请堆空间
    112. // 新的API,返回值是一个地址,前两个函数的底层是调用这个
    113. //HeapAlloc();
    114. // 申请堆地址空间,可以指定内存属性
    115. //VirtualAlloc();
    116. ACCEL *pAccelNews = (ACCEL*)HeapAlloc(GetProcessHeap(), 0, sizeof(ACCEL)*2);
    117. if (pAccelNews == nullptr) {
    118. ShowErrorMsg();
    119. return 0;
    120. }
    121. // 创建快捷键表
    122. HACCEL hAccel = CreateAcceleratorTable(pAccelNews, 2);
    123. if (hAccel == NULL) {
    124. ShowErrorMsg();
    125. return 0;
    126. }
    127. pAccelNews[0].fVirt = FALT | FCONTROL | FVIRTKEY;
    128. pAccelNews[0].key = 'A';
    129. pAccelNews[0].cmd = WM_COMMAND;
    130. pAccelNews[1].fVirt = FALT | FCONTROL | FVIRTKEY;
    131. pAccelNews[1].key = 'B';
    132. pAccelNews[1].cmd = WM_COMMAND;
    133. // 创建窗口实例
    134. TCHAR szWndClassName[] = { _T("CR41WndClassName") };
    135. WNDCLASSEX wc = { 0 };
    136. wc.cbSize = sizeof(WNDCLASSEX);
    137. wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
    138. wc.lpfnWndProc = WindowProc;
    139. wc.hInstance = hInstance;
    140. wc.hIcon = LoadIcon(NULL, IDI_HAND);
    141. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    142. wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
    143. wc.lpszMenuName = NULL;
    144. wc.lpszClassName = szWndClassName;
    145. // 注册窗口
    146. if (RegisterClassEx(&wc) == 0) {
    147. ShowErrorMsg();
    148. return 0;
    149. }
    150. // 创建窗口
    151. TCHAR szWndName[] = { _T("51asm") };
    152. HWND hWnd = CreateWindowEx(0,
    153. szWndClassName,
    154. szWndName,
    155. WS_OVERLAPPEDWINDOW, //组合属性,可拉伸窗口
    156. CW_USEDEFAULT,
    157. CW_USEDEFAULT,
    158. CW_USEDEFAULT,
    159. CW_USEDEFAULT,
    160. NULL,
    161. NULL,
    162. hInstance,
    163. NULL);
    164. if (hWnd == NULL) {
    165. ShowErrorMsg();
    166. return 0;
    167. }
    168. // 菜单
    169. HMENU HMenu = CreateMenu();
    170. // 弹出菜单
    171. BOOL ret;
    172. ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("文件(&F)"));
    173. ret = AppendMenu(HMenu, MF_STRING | MF_POPUP, (UINT_PTR)HMenu, _T("编辑(&E)"));
    174. SetMenu(hWnd, HMenu);
    175. // 添加子菜单
    176. HMENU hSubMenu = GetSubMenu(HMenu, 0);
    177. ret = AppendMenu(hSubMenu, MF_STRING, IDM_OPEN, _T("打开(&O)"));
    178. ret = AppendMenu(hSubMenu, MF_STRING, IDM_SAVE, _T("保存(&O)"));
    179. ret = AppendMenu(hSubMenu, MF_STRING, IDM_EXIT, _T("退出(&O)"));
    180. SetMenu(hWnd, HMenu);
    181. // RECT rc;
    182. // GetClientRect(hWnd, &rc);
    183. // 显示,更新窗口
    184. ShowWindow(hWnd, SW_SHOWNORMAL); // 调用Show时候父子窗口都会被调用
    185. //ShowWindow(hChild, SW_SHOWNORMAL); 非子窗口需要单独show
    186. UpdateWindow(hWnd); // 产生WM_PAINT
    187. SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_CROSS));
    188. // 消息循环
    189. BOOL bRet;
    190. MSG msg;
    191. while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    192. {
    193. if (bRet == -1) {
    194. break;
    195. }
    196. else {
    197. // 转换快捷键消息 WM_COMMAND
    198. if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
    199. TranslateMessage(&msg);// 转换键盘消息
    200. DispatchMessage(&msg); // 派发消息
    201. }
    202. }
    203. }
    204. // 删除快捷键表
    205. DestroyAcceleratorTable(hAccel);
    206. HeapFree(GetProcessHeap(), 0, pAccelNews);
    207. return msg.wParam;
    208. }

  • 相关阅读:
    python容器之字典
    Grafana安装和配置Prometheus数据源教程
    微信小程序 java在线租房-房屋租赁系统springboot
    TCP为什么需要三次握手和四次挥手?
    百度文心一言
    vivado查看报告和消息4
    Dubbo 与 Spring Cloud 完美结合
    细聊init进程和Linux系统启动流程
    docker-ip与服务器ip冲突问题
    源码级别+操作系统级别解密RocketMQ 存储原理
  • 原文地址:https://blog.csdn.net/qq_61553520/article/details/134086006