大家都知道,windows操作系统是基于消息的,所有的操作都可以用发送消息来完成;这个项目大概的图形界面是这样的:
文章重点说的是点击事件如何被响应;
先创建一个win32的应用程序,再。RC文件下添加一个Dlalog资源。你们可能会认为Dlalog是MFC专属的资源,mfc是微软提供的一个类库,是为了更好的响应这些资源。如果没有自动添加resource文件,需要在头文件中添加现有项。这些工作做好之后,就可以开始写代码了;
如果想要自己画窗口,需要在winmain函数下完成
- 为class赋值
- 注册窗口句柄
- 创建窗口
- 提供消息处理函数
- 消息循环接收
如果使用Dlalog,那就只需要创建窗口
//创建对话框函数原型 INT_PTR Dialog( HINSTANCE hInstance, //imagebase LPCTRSR lpTemplate, //模板,就是创建的Dlalog的ID HWND hWndParent, //父窗口 DLGPROC lpDialogFunc //函数定义窗口 ) //处理消息的函数 BOOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { BOOL bRet = FALSE; switch (uMsg) { case WM_INITDIALOG: //窗口初始化就会触发这个事件 case WM_NOTIFY: //* case WM_COMMAND: //* case WM_CLOSE: //在WinMain中调用函数 DialogBox(hInstance,(MAKEINTRESOURCE)IDC_DIALOG_MAIN,NULL,MainDlgProc); //进行强制类型转换的原因是第二个参数可以给定一个指针 也可以给定一个序号,如果是一个序号,就用MAKEINTRESOURCE宏进行转换;
现在窗口就可以显示出来了,但是没有内容;需要添加按钮,这时候就要说一下标准控件和通用控件了;
标准控件像Static,Button,Edit等等都是,都是可用的;但是通用代码包含在Comctrl32.dll里,像ComboBoxEx,Header,ImageList这些需要做俩件事
#include<commctrl.h> #pragma comment(lib,"comctl32.lib") //除了这个头文件还要告诉windows加载器需要加载的lib; //这些代码可以放到WinMain下 INITCOMMONCONTROLSEX icex; icex.dwSize=sizeof(INITCOMMONCONTROLSEX); icex.dwICC=ICC_WIN95_CLASSES; InitCommonControlsEx(&icex);
这是在老版本事这样,vs2019能直接用,不需要这些。界面需要用到这个控件:
在画完界面之后就需要添加列 process pid user size 上代码:
VOID InitProcessLostView(HWND hDlg) { LV_COLUMN lv; HWND hListProcess;//hwnd memset(&lv, 0, sizeof(LV_COLUMN));//初始化局部变量 hListProcess = GetDlgItem(hDlg, IDC_LIST_PROCESS); //获取窗口句柄,hDlg是父窗口句柄,另一个是子空间ID; //整行选中 SendMessage(hListProcess, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); //在windows中一定要发送消息才能干活; //1 lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; lv.pszText = (LPWSTR)TEXT("process");//标题 lv.cx = 150;//列宽 lv.iSubItem = 0;//所在第几列,从0开始 SendMessage(hListProcess, LVM_INSERTCOLUMN, 0, (DWORD)&lv); //2 lv.pszText = (LPWSTR)TEXT("pid"); lv.cx = 120; lv.iSubItem = 1; SendMessage(hListProcess, LVM_INSERTCOLUMN, 1, (DWORD)&lv); //3 lv.pszText = (LPWSTR)TEXT("user"); lv.cx = 100; lv.iSubItem = 2; ListView_InsertColumn(hListProcess, 2, &lv);//这个宏和SendMessage的效果是一样的; //4 lv.pszText = (LPWSTR)TEXT("size"); lv.cx = 100; lv.iSubItem = 3; ListView_InsertColumn(hListProcess, 3, &lv); }
这里有俩个结构需要说一下,
LV_COLUMN用于定义报表方式下的“列”的结构;LV_ITEM用于定义“项”的结构。下面会用到另一个;
到了这一步,就能够显示成这样:
现在需要在process项下添加元素,需要用到另外一个结构体LV_ITEM;
//遍历系统进程 int TraProcess(HWND hDlg) { LVITEM vitem; HWND hListProcess; memset(&vitem, 0, sizeof(LVITEM)); vitem.mask = LVIF_TEXT; hListProcess = GetDlgItem(hDlg, IDC_LIST_PROCESS); SendMessage(hListProcess, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hSnapshot)return 0; PROCESSENTRY32 pi; pi.dwSize = sizeof(PROCESSENTRY32); BOOL bRet = Process32First(hSnapshot, &pi); IMAGE_SECTION_HEADER* pISH = (IMAGE_SECTION_HEADER*)pi.th32ProcessID; while(bRet) { bRet = Process32Next(hSnapshot, &pi); vitem.pszText = pi.szExeFile; vitem.iItem = 0;//第几行,抱歉,这里应该用循环,因为行数在增加,但是效果一样 vitem.iSubItem = 0;//所在列 SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem); } return 0; }
后来的版本用LVITEM代替了LV_ITEM,中间的函数都是获取系统进程的win32函数,需要自己去查,程序员一定要会自己解决问题,才能成为好的程序员;
界面基本就写完了,现在要说一个知识;当点击按钮时,是响应函数是父窗口提供的。通过Command事件可以完成响应。但是对于List Contral控件需要响应的消息太多了,如果都用case WM_.....代码就比较难看了,所以window提供了一个结构响应消息,
case WM_NOTIFY://通知消息
该消息和WM_COMMAND消息类型相似,都是向父窗口发送消息,但是WM_NOTIFY比WM_COMMAND消息类型更丰富;
//WM_NOTIFY消息中带有俩个参数 wParam:是控件ID; lParam:指向一个结构体 //结构体类型: typedef struct tagNMHDR{ HWND hwndFrom;//发送消息的句柄 UINT idFrom; //发送消息的ID UINT code; //通知码,是左键消息还是右键 }NMHDR
但是这个结构体还是不能全部包括,windows又对每种不同用途的消息创建了结构体,这些结构体都可以讲子窗口的消息带到父窗口,lParam就指向这些结构;这些结构有一个共同特点:第一个参数都指向第tarNMHDR;
在C++中,继承机制就是 子类会将父类先调用,之后初始化自己的类;这样,无论之后在补充多少东西,继承之后都能将消息带到父类;来看一下实际应用;
在第二个List控件中,想要通过第一个LIST的Pid拿到user和size,就需要通过WM_NOTIFY消息,
//获取进程模块信息 void TraModulse(HWND hListProcess, WPARAM wParam, LPARAM lParam) { DWORD dwRowId; TCHAR szPid[0x20];//缓冲区 LV_ITEM lv; memset(&lv, 0, sizeof(LV_ITEM)); memset(szPid, 0, 0x20); //获取选中的行 if (Pid != -1) { //获取PID vit.iSubItem = 1; vit.pszText = sizePid; vit.cchTextMax = 0x20; SendMessage(hListProcess, LVM_GETITEMTEXT, Pid, (DWORD)&vit); } else { MessageBox(NULL, TEXT("Please close process"), TEXT("ERROR"), MB_OK); return; } } //放到处理消息的响应还是MainDlgProc里 case WM_NOTIFY://响应点击进程信息 { NMHDR* p = (NMHDR*)lParam; if (p->code == NM_CLICK&&wParam == IDC_LIST_PROCESS) {//判断是哪个List 和鼠标按键 TraModulse(GetDlgItem(hDlg, IDC_LIST_PROCESS), wParam, lParam); } break; }
项目代码可以到我资源文件下载