windows SDK chapter 10
菜单与资源
资源
图标光标等资源不是重点,重点在菜单
图标,光标,菜单,对话框都是资源类型.保存在.exe或者.dll等文件中 .程序使用函数显式或者隐式地将资源加载进入内存使用,比如LoadIcon和LoadCursor
注意到资源是保存在exe或者dll文件中的,这意味着,不需要另外保存bmp或者ico等文件格式了,要啥直接放到到exe文件中,万事不求人了
资源脚本
资源脚本以.RC为后缀
image-20220821171827617
在visual studio解决方案视图下,它是这样的
image-20220821171915530
如果用其他文本编辑器比如vscode打开ICONDEMO.rc,他实际上时类似于头文件的代码
它不存放任何资源文件,但是指出这些文件的路径还有ID编号
其中菜单的结构最明显
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_INVOKED #include "targetver.h" #endif #define APSTUDIO_HIDDEN_SYMBOLS #include "windows.h" #undef APSTUDIO_HIDDEN_SYMBOLS #undef APSTUDIO_READONLY_SYMBOLS #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE 4 , 2 IDI_ICONDEMO ICON "ICONDEMO.ico" IDI_SMALL ICON "small.ico" IDC_ICONDEMO MENU BEGIN POPUP "文件(&F)" BEGIN MENUITEM "退出(&X)" , IDM_EXIT END POPUP "帮助(&H)" BEGIN MENUITEM "关于(&A) ..." , IDM_ABOUT END END ...
资源脚本.rc文件引用了Resource.h
头文件,which是给资源编号的,每个资源有唯一的编号方便过程函数按图索骥
Resource.h允许c源程序和.rc资源描述文件引用相同的符号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #define IDS_APP_TITLE 103 #define IDR_MAINFRAME 128 #define IDD_ICONDEMO_DIALOG 102 #define IDD_ABOUTBOX 103 #define IDM_ABOUT 104 #define IDM_EXIT 105 #define IDI_ICONDEMO 107 #define IDI_SMALL 108 #define IDC_ICONDEMO 109 #define IDC_MYICON 2 #ifndef IDC_STATIC #define IDC_STATIC -1 #endif #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 130 #define _APS_NEXT_RESOURCE_VALUE 129 #define _APS_NEXT_COMMAND_VALUE 32771 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif
书上给出的忠告是,不要直接修改资源描述文件和资源头文件,让visual
studio维护这些东西
visual
studio使用RC.EXE工具编译资源在编译阶段把ICONDEMO.rc编译,然后在连接阶段随obj和lib进入exe文件
图标
怎么绘制图标无所谓,只要是.ico格式的都能当成图标,关键看程序怎么用这个图标
在程序中使用自定义图标,不再使用Load(NULL,IDI_APPLICATION)指定的系统默认图标
应该这样写:
1 wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ;
意思是往本应用程序实例中加载一个编号为IDI_ICON的图标
在visualstudio项目中可以添加资源
image-20220821173846421
添加Icon图标资源
image-20220821173911820
选择32*32
,32位色,然后就可以在32*32
的格格矩阵上画画了
image-20220821173956694
visual studio顶部会有工具栏
image-20220821174601043
画完了还得把以前的图标删了,让自己的图标顶置
image-20220822085630711
然后去资源视图改ID,在画画这里找一年也找不到
image-20220821175726126
给他改成ID=IDI_ICON1,visual
studio会自动在rc文件和资源头文件中修改变化的,此时在资源头文件Resource.h中可以看到IDI_ICON1的宏定义了
此时在创建窗口类的时候再写
1 wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON1)) ;
就可以加载图标了
这个图标是放在项目根目录下的,如果删了他,对运行已经编译链接好的程序没有影响,但是再编译时会报错找不到文件
LoadIcon
1 2 3 4 HICON LoadIconA ( [in, optional] HINSTANCE hInstance, [in] LPCSTR lpIconName ) ;
hInstance指定图标的文件来源,
NULL则为系统图标
本应用程序实例句柄则为本exe文件中包含的图标
lpIconName指定要加载的图标名.
可以使用字符串,也可以使用MAKEINTERESOURCE宏指定图标的宏定义
1 2 #define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i)))) #define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
i先强制转化为WORD类型,这意味着i的有效范围是16位,更高位直接扬了
资源就到此为止吧,再学就不礼貌了,该点菜了
菜单
一开始我还想直接看菜单不看前面的图标光标这种资源,看到资源脚本直接蒙蔽了
概念
主菜单,顶级菜单:标题栏下面,客户区顶的常驻菜单
子菜单,下拉菜单,弹出菜单:主菜单点击后下拉的菜单.这种菜单可以嵌套多层
状态:菜单有启用,禁用状态,启用时有选中和非选中状态.
菜单结构
每个菜单项有三个特征定义,分别是菜单显式什么,菜单ID或者句柄,属性
定义菜单
visual studio中可以向项目添加资源,其中就包括菜单
image-20220821163145869
编辑菜单的方式是交互式的,右侧的菜单编辑器可以调制菜单属性(幸好一个菜单的属性稀松)
image-20220821163233880
菜单属性
能够编辑的菜单属性有12个(实际11个,最下边那个提示是MFC编程用的)
image-20220821163420651
弹出菜单为TRUE则本菜单可以弹出子菜单
灰显指菜单栏变灰且不活动,即禁用了,点击不会产生WM_COMMAND消息
描述文字就是菜单键上的文字
如果描述文字前面加上一个&符号,则该菜单项第一个字符会带上下划线,方便Alt更改菜单时索引他就像这样
已勾选为TRUE则该菜单左侧有一个对号,这是复选标记
image-20220821163918622
已启用和灰显作用类似,但是只是点了没反应,字体不会变灰
分隔就是把该菜单作为分割线,比如1123和3菜单中间这个杠
image-20220821164249794
关键是ID,这是窗口过程给该菜单设置逻辑的唯一凭证
这里的ID是宏定义,实际上的数值visualstudio给我们放到Resource.h中了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #define IDC_MYICON 2 #define IDD_CHAPTER10_DIALOG 102 #define IDS_APP_TITLE 103 #define IDD_ABOUTBOX 103 #define IDM_ABOUT 104 #define IDM_EXIT 105 #define IDI_CHAPTER10 107 #define IDI_SMALL 108 #define IDC_CHAPTER10 109 #define IDR_MAINFRAME 128 #define IDR_MENU1 129 #define ID_32771 32771 #define ID_32772 32772 #define ID_32773 32773 #define ID_32774 32774 #define ID_32775 32775 #define ID_32776 32776 #define ID_32777 32777 #define ID_32778 32778 #define IDC_STATIC -1
如果需要更多的资源则自己添加宏定义
程序中使用菜单
有多个可以指定菜单的地方,
最初可以指定菜单是在注册窗口类时
1 wndclass.lpszMenuName = MAKEINTRESOURCEW(IDC_ICONDEMO);
然后在从创建窗口实例的时候可以指定菜单覆盖窗口类的菜单
1 2 HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0 , CW_USEDEFAULT, 0 , nullptr, hMenu, hInstance, nullptr);
这里hMenu句柄可以是LoadMenu打开的菜单资源
如果CreateWindow这里不指定菜单, 则默认使用窗口类的菜单.否则覆盖之
窗口实例创建之后还有改变菜单的方法
菜单和消息
当鼠标在菜单上移动时产生
LOWORD(wParam)
弹出菜单索引或者菜单ID
HIWORD(wParam) 选择标记
Value
Meaning
MF_BITMAP 0x00000004L
Item displays a bitmap.
MF_CHECKED 0x00000008L
Item is checked.
MF_DISABLED 0x00000002L
Item is disabled.
MF_GRAYED 0x00000001L
Item is grayed.
MF_HILITE 0x00000080L
Item is highlighted.
MF_MOUSESELECT 0x00008000L
Item is selected with the mouse.
MF_OWNERDRAW 0x00000100L
Item is an owner-drawn item.
MF_POPUP 0x00000010L
Item opens a drop-down menu or
submenu.
MF_SYSMENU 0x00002000L
Item is contained in the window menu. The
lParam parameter contains a handle to the menu associated with
the message.
lParam 包含所选项的菜单句柄
该消息用于暂时高亮菜单,显式完整文本描述
WM_COMMAND
最重要
表示点击了某个启用的菜单
LOWORD(wParam)菜单ID
下面的三个消息几乎用不到
要显示弹出菜单时的消息
wParam弹出菜单的句柄
LOWORD(lParam)弹出菜单的索引
HIWORD(lParam)1系统菜单,0其他菜单
WM_SYSCOMMAND
点击了系统菜单某个启用的菜单项
LWORD(wParam)菜单ID
LOWORD(lParam)鼠标x坐标,屏幕坐标
HIWORD(lParam)鼠标y坐标
用户按下了Alt和不对应任何菜单项字符键.
或者弹出菜单时按下不对应任何菜单项的字符键
用于程序捕获并提醒快捷键不存在
LOWORD(wParam) 字符码
HIWORD(wParam) 选择吗
lParam 菜单句柄
该消息直接发送给DefWindowProc会导致操作系统MessageBeep
例程
菜单结构
visual studio中编辑菜单
使用文本编辑器打开MENUDEMO.rc文件,其菜单结构是按照BEGIN,END分块的
BEGIN,END块可以嵌套,约外层的菜单嵌套层数越低
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 IDC_MENUDEMO MENU BEGIN POPUP "文件(&F)" BEGIN MENUITEM "&New" , IDM_FILE_NEW MENUITEM "&Open" , IDM_FILE_OPEN MENUITEM "&Save" , IDM_FILE_SAVE MENUITEM "Save &As" , IDM_FILE_SAVE_AS MENUITEM "E&xit" , IDM_APP_EXIT MENUITEM SEPARATOR END POPUP "编辑(&E)" BEGIN MENUITEM "&Undo" , IDM_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "C&ut" , IDM_EDIT_CUT MENUITEM "&Copy" , IDM_EDIT_COPY MENUITEM "&Paste" , IDM_EDIT_PASTE MENUITEM "De&lete" , IDM_EDIT_CLEAR END POPUP "背景(&B)" BEGIN MENUITEM "&White" , IDM_BKGND_WHITE, CHECKED MENUITEM "&Light Gray" , IDM_BKGND_LTGRAY MENUITEM "&Gray" , IDM_BKGND_GRAY MENUITEM "&Dark Gray" , IDM_BKGND_DKGRAY MENUITEM "&Black" , IDM_BKGND_BLACK END POPUP "计时(&T)" BEGIN MENUITEM "&Start" , IDM_TIMER_START MENUITEM "S&top" , IDM_TIMER_STOP, GRAYED END POPUP "帮助(&H)" BEGIN MENUITEM "&Help..." , IDM_APP_HELP MENUITEM "&About MenuDmo..." , IDM_APP_ABOUT END END
菜单项前面加&,是为了加下划线,方便使用快捷键
整个菜单的ID表示在一开始列出了,是IDC_MENUDEMO,该值供注册窗口类或者实例化窗口时引用
这个ID是多少不重要,只要保证每个资源有唯一的ID就可以了,我们只管给资源ID规定宏定义,visualstudio会自动帮我们分配一个正整数的
此后顶级菜单,即弹出菜单,都会有POPUP声明,然后跟着BEGIN,END作用块
比如POPUP "文件(&F)"
,这就声明了一个文件弹出菜单.其后面紧跟着的BEGIN,END包裹的块就是它的弹出菜单项
1 2 3 4 POPUP "文件(&F)" BEGIN ...弹出菜单项 END
所有弹出菜单都不能有ID标识,只能是普通菜单项MENUITEM可以有ID标识,比如
1 MENUITEM "&New" , IDM_FILE_NEW
因为弹出菜单只负责展示其菜单项,不能安排其他行为
程序中使用菜单
visual studio默认建立的资源都是使用ID索引的,没有使用字符串
刚才建立的菜单其ID为IDC_MENUDEMO
,怎样才能让窗口使用这个菜单呢?
可以在创建窗口类的时候使用MAKEINTRESOURCE宏
1 wndclass.lpszMenuName = MAKEINTRESOURCE(IDC_MENUDEMO);
也可以在创建窗口实例的时候才指定菜单
1 2 3 4 5 6 7 8 wndclass.lpszMenuName = NULL ; ... HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDC_MENUDEMO)); hwnd = CreateWindow(szAppName, TEXT("Menu Demonstration" ), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL , hMenu, hInstance, NULL );
菜单消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 switch (message){ case WM_COMMAND: hMenu = GetMenu(hwnd); switch (LOWORD(wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: MessageBeep(0 ); return 0 ; case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0 , 0 ); return 0 ; case IDM_EDIT_UNDO: case IDM_EDIT_CUT: case IDM_EDIT_COPY: case IDM_EDIT_PASTE: case IDM_EDIT_CLEAR: MessageBeep(0 ); return 0 ; case IDM_BKGND_WHITE: case IDM_BKGND_LTGRAY: case IDM_BKGND_GRAY: case IDM_BKGND_DKGRAY: case IDM_BKGND_BLACK: CheckMenuItem(hMenu, iSelection, MF_UNCHECKED); iSelection = LOWORD(wParam); CheckMenuItem(hMenu, iSelection, MF_CHECKED); SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(idColor[LOWORD(wParam) - IDM_BKGND_WHITE])); InvalidateRect(hwnd, NULL , TRUE); return 0 ; case IDM_TIMER_START: if (SetTimer(hwnd, ID_TIMER, 1000 , NULL )) { EnableMenuItem(hMenu, IDM_TIMER_START, MF_GRAYED); EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_ENABLED); } return 0 ; case IDM_TIMER_STOP: KillTimer(hwnd, ID_TIMER); EnableMenuItem(hMenu, IDM_TIMER_START, MF_ENABLED); EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_GRAYED); return 0 ; case IDM_APP_HELP: MessageBox(hwnd, TEXT("Help not yet implemented!" ), szAppName, MB_ICONEXCLAMATION | MB_OK); return 0 ; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("Menu Demonstration Program\n" ) TEXT("(c) Charles Petzold, 1998" ), szAppName, MB_ICONINFORMATION | MB_OK); return 0 ; } break ;
所有点击活动菜单的消息都有一个WM_COMMAND命令
此时其LOWORD(lParam)保存的是击中菜单的ID,如果击中的是弹出菜单则系统负载该逻辑,自动展开菜单,不需要我们指定弹出菜单的行为(事实上弹出菜单不允许拥有ID,这就意味着在程序中无法索引到它,无法安排行为)
复用关闭消息
1 2 case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0 , 0 );
当点击菜单项项时,直接发送一体哦啊WM_CLOSE消息即可借助已有代码完成功能
改变勾选状态
1 2 3 4 5 DWORD CheckMenuItem ( HMENU hMenu, UINT uIDCheckItem, UINT uCheck ) ;
1 2 3 4 CheckMenuItem(hMenu, iSelection, MF_UNCHECKED); iSelection = LOWORD(wParam); CheckMenuItem(hMenu, iSelection, MF_CHECKED); SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(idColor[LOWORD(wParam) - IDM_BKGND_WHITE]));
点选新的背景颜色之后,首先弃选先前的背景颜色,然后修改为新的背景颜色
之后修使用SetClassLong改程序的背景颜色
1 2 3 4 5 DWORD SetClassLongA ( [in] HWND hWnd, [in] int nIndex, [in] LONG dwNewLong ) ;
菜单快捷键
右键菜单
在处理右键消息的时候可以安排上右键快捷菜单
1 2 3 4 5 6 7 8 9 BOOL TrackPopupMenu ( [in] HMENU hMenu, [in] UINT uFlags, [in] int x, [in] int y, [in] int nReserved, [in] HWND hWnd, [in, optional] const RECT *prcRect ) ;
在指定的位置展示一个快捷菜单,并且追踪在该菜单上的选择.
该快捷菜单可以出现在任何地方
1 Displays a shortcut menu at the specified location and tracks the selection of items on the menu. The shortcut menu can appear anywhere on the screen.
1 2 3 4 5 6 7 8 9 10 case WM_CREATE: hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDC_MENUDEMO)); hMenu = GetSubMenu(hMenu, 0 ); return 0 ; case WM_RBUTTONUP: point.x = LOWORD(lParam); point.y = HIWORD(lParam); ClientToScreen(hwnd, &point); TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y, 0 , hwnd, NULL ); return 0 ;
键盘加速键
键盘加速键不是处理WM_KEYDOWN或者WM_CHAR复制键盘功能,当然这样写也能达到相同的目标
但是键盘加速键可以省去其逻辑
此处略去一大堆优点,因为有些优点现在我也体会不到
键盘加速键应当是Shift,Ctrl,Alt带领的跨界见,避免使用Tab,Enter,Esc,Space作键盘加速键
比如Ctrl+Z 撤销,Ctrl+X剪切,Del删除
加速键表
加速键表也是资源的一种,可以在visual studio中添加Accelerator资源
一方通行
加速键表长这样
ID就是该加速键的编号,键对应键盘动作,类型要么是虚拟键VIRTKEY,要么是字符CHAR
image-20220823114110566
如果键盘加速键想要和菜单关联起来,那么一个键盘加速键的ID需要设置成一个菜单项的ID
整个加速键表类似菜单一样,也有自己的一个ID标识,这个可以在资源视图看到
image-20220823145812581
通常方便使用,加速键表名和程序名,菜单名都相同
有了这个加速键表ID我们就可以在表的层面上和程序打交道,而不是需要负责每个加速键的逻辑.在程序中使用LoadAccelerators函数加载加速键表并获得句柄
1 HANDLE hAccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(IDC_MENUDEMO));
现在程序中就有加速键表句柄了,下面就是如何使用该句柄了
在消息循环翻译分派消息之前先尝试翻译成加速键消息
1 2 3 4 5 6 while (GetMessage(&msg, NULL , 0 , 0 )) { if (!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessageW(&msg); } }
对于每个消息都会先经过一个加速键翻译,TranslateAccelerator函数确定保存在msg中的消息是否是键盘消息(包括虚拟键消息和字符消息),如果是,并且hAccel是一个有效的加速键表句柄,下面就调用hwnd指向窗口的窗口过程,向其发送同键盘加速键ID对应的菜单项按下的WM_COMMAND或者WM_SYSCOMMAND消息
显然这个翻译后的消息还会进入消息队列,但是翻译后的消息是WM_COMMAND消息,不是WM_KEYDOWN,WM_CHAR消息,不会再被TranslateAccelerator翻译,因此通过条件判断,正式进入消息循环处理
注意到TranslateAccelerator(hwnd, hAccel, &msg)
这里指定了一个主窗口句柄,但是一个程序可以有多个窗口,这就会导致所有加速键消息发往主窗口.
如果想让一个消息发往现在的焦点窗口,则可以使用msg.hwnd,这个hwnd是目前消息的窗口句柄
处理WM_COMMAND消息
在处理WM_COMMAND消息时,加速键,菜单,控件消息的参数含义:
image-20220823151633079
如此看来,如果加速键ID和功能表ID故意设置成相同值,就不用大费周折了
例程
菜单表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 IDC_MENUDEMO2 MENU BEGIN POPUP "&File" BEGIN MENUITEM "&New" , IDM_FILE_NEW MENUITEM "&Open" , IDM_FILE_OPEN MENUITEM "&Save" , IDM_FILE_SAVE MENUITEM "Save &As" , IDM_FILE_SAVE_AS MENUITEM SEPARATOR MENUITEM "&Print" , IDM_FILE_PRINT MENUITEM SEPARATOR MENUITEM "E&xit" , IDM_APP_EXIT END POPUP "&Edit" BEGIN MENUITEM "&Undo\tCtrl+Z" , IDM_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t\tCtrl+X" , IDM_EDIT_CUT MENUITEM "&Copy\tCtrl+C" , IDM_EDIT_COPY MENUITEM "&Paste\tCtrl+V" , IDM_EDIT_PASTE MENUITEM "De&lete\tDel" , IDM_EDIT_DELETE MENUITEM SEPARATOR MENUITEM "&Select All" , IDM_EDIT_SELECT_ALL END POPUP "&Help" BEGIN MENUITEM "&Help...\tF1" , IDM_HELP_HELP MENUITEM "&About..." , IDM_APP_ABOUT END END
加速键表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 IDC_MENUDEMO2 ACCELERATORS BEGIN "8" , IDM_UNDO, VIRTKEY, ALT, NOINVERT VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT VK_DELETE, IDM_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT VK_F1, IDM_HELP_HELP, VIRTKEY, NOINVERT VK_INSERT, IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT VK_INSERT, IDM_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT "^C" , IDM_EDIT_COPY, ASCII, NOINVERT "^V" , IDM_EDIT_PASTE, ASCII, NOINVERT "^X" , IDM_EDIT_CUT, ASCII, NOINVERT "^Z" , IDM_EDIT_UNDO, ASCII, NOINVERT "^A" , IDM_EDIT_SELECT_ALL, ASCII, NOINVERT END
菜单名和加速键表ID都是IDC_MENUDEMO2,可以使用MAKEINTRESOURCE宏加载资源
使用父窗口控制编辑控件子窗口,大大减轻自己写编辑逻辑的工作量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 #include <windows.h> #include "MENUDEMO2.h" #define ID_EDIT 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; TCHAR szAppName[] = TEXT("PopPad2" ); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HACCEL hAccel; HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance,szAppName); wndclass.hCursor = LoadCursor(NULL , IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = MAKEINTRESOURCE(IDC_MENUDEMO2); wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL , TEXT("This program requires Windows NT!" ), szAppName, MB_ICONERROR); return 0 ; } hwnd = CreateWindow( szAppName, szAppName, WS_OVERLAPPEDWINDOW, GetSystemMetrics(SM_CXSCREEN) / 4 , GetSystemMetrics(SM_CYSCREEN) / 4 , GetSystemMetrics(SM_CXSCREEN) / 2 , GetSystemMetrics(SM_CYSCREEN) / 2 , NULL , NULL , hInstance, NULL ); ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MENUDEMO2)); while (GetMessage(&msg, NULL , 0 , 0 )) { if (!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } int AskConfirmation (HWND hwnd) { return MessageBox(hwnd, TEXT("Really want to close PopPad2?" ), szAppName, MB_YESNO | MB_ICONQUESTION); } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; int iSelect, iEnable; switch (message) { case WM_CREATE: hwndEdit = CreateWindow(TEXT("edit" ), NULL , WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0 , 0 , 0 , 0 , hwnd, (HMENU)ID_EDIT, ((LPCREATESTRUCT)lParam)->hInstance, NULL ); return 0 ; case WM_SETFOCUS: SetFocus(hwndEdit); return 0 ; case WM_SIZE: MoveWindow(hwndEdit, 0 , 0 , LOWORD(lParam), HIWORD(lParam),TRUE); return 0 ; case WM_INITMENUPOPUP: if (lParam == 1 ) { EnableMenuItem( (HMENU)wParam,IDM_EDIT_UNDO, SendMessage(hwndEdit, EM_CANUNDO, 0 , 0 ) ? MF_ENABLED : MF_GRAYED ); EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE, IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED); iSelect = SendMessage(hwndEdit, EM_GETSEL,0 , 0 ); if (HIWORD(iSelect) == LOWORD(iSelect)) iEnable = MF_GRAYED; else iEnable = MF_ENABLED; EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable); return 0 ; } break ; case WM_COMMAND: if (lParam) { if (LOWORD(lParam) == ID_EDIT && (HIWORD(wParam) == EN_ERRSPACE || HIWORD(wParam) == EN_MAXTEXT)) MessageBox(hwnd, TEXT("Edit control out of space." ), szAppName, MB_OK | MB_ICONSTOP); return 0 ; } else switch (LOWORD(wParam)) { case IDM_FILE_NEW: case IDM_FILE_OPEN: case IDM_FILE_SAVE: case IDM_FILE_SAVE_AS: case IDM_FILE_PRINT: MessageBeep(0 ); return 0 ; case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0 , 0 ); return 0 ; case IDM_EDIT_UNDO: SendMessage(hwndEdit, WM_UNDO, 0 , 0 ); return 0 ; case IDM_EDIT_CUT: SendMessage(hwndEdit, WM_CUT, 0 , 0 ); return 0 ; case IDM_EDIT_COPY: SendMessage(hwndEdit, WM_COPY, 0 , 0 ); return 0 ; case IDM_EDIT_PASTE: SendMessage(hwndEdit, WM_PASTE, 0 , 0 ); return 0 ; case IDM_EDIT_CLEAR: SendMessage(hwndEdit, WM_CLEAR, 0 , 0 ); return 0 ; case IDM_EDIT_SELECT_ALL: SendMessage(hwndEdit, EM_SETSEL, 0 , -1 ); return 0 ; case IDM_HELP_HELP: MessageBox(hwnd, TEXT("Help not yet implemented!" ), szAppName, MB_OK | MB_ICONEXCLAMATION); return 0 ; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("dustball's 卑鄙 notepad" ), szAppName, MB_OK | MB_ICONINFORMATION); return 0 ; } break ; case WM_CLOSE: if (IDYES == AskConfirmation(hwnd)) DestroyWindow(hwnd); return 0 ; case WM_QUERYENDSESSION: if (IDYES == AskConfirmation(hwnd)) return 1 ; else return 0 ; case WM_DESTROY: PostQuitMessage(0 ); return 0 ; } return DefWindowProc(hwnd, message, wParam, lParam); }
未实现的保存,打开,新建等功能需要等到学了对话框再说了