win32工程实例(深入理解win32九)(1)

前言

在上一节中我们对资源表进行了解析,在这一节里面我们来了解通用控件。

准备工作

首先新建一个资源脚本

win32工程实例(深入理解win32九)(2)

创建一个dialog

win32工程实例(深入理解win32九)(3)

在父窗口里新建两个窗口

win32工程实例(深入理解win32九)(4)

添加一些按钮,修改字体后如下图所示

win32工程实例(深入理解win32九)(5)

然后添加Dialog的相关函数

win32工程实例(深入理解win32九)(6)

win32工程实例(深入理解win32九)(7)

然后大体的框架就已经搭建了出来

win32工程实例(深入理解win32九)(8)

标准控件和通用控件

对于windows标准的控件总是可用的

Static Group Box Button Check Box Radio Button Edit ComboBox ListBox

因为如果把所有的控件在编译的时候全部加进exe就会使exe变得十分大,所以windows把通用控件放在了Comctrl32.dll里面

新建一个列表控件作为进程的显示窗口

win32工程实例(深入理解win32九)(9)

再新建一个列表控件作为模块的显示窗口

win32工程实例(深入理解win32九)(10)

这里再把样式改为报告

win32工程实例(深入理解win32九)(11)

因为通用控件在dll里,所以还需要包含头文件

#include <commctrl.h> #pragma comment(lib,"comctl32.lib")

win32工程实例(深入理解win32九)(12)

另外我们需要给windows指定我们需要使用哪一个通用控件,就需要通过INITCOMMONCONTROLSEX进行初始化

INITCOMMONCONTROLSEX

typedef struct tagINITCOMMONCONTROLSEX { DWORD dwSize; DWORD dwICC; } InitCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

第一个参数为INITCOMMONCONTROLSEX的大小,第二个参数就是使用哪一个通用控件

win32工程实例(深入理解win32九)(13)

例如这里我应该使用的是ICC_LISTVIEW_CLASSES这个控件,但是每次找的话都会很麻烦,这里windows给我们提供了一个包含常用的通用控件的类型,即ICC_WIN95_CLASSES

INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&icex);

即可使用通用控件生成界面

win32工程实例(深入理解win32九)(14)

这里再设置列,添加如下代码

void InitProcessListView(HWND hDlg) { LV_COLUMN lv; HWND hListProcess; //初始化 memset(&lv,0,sizeof(LV_COLUMN)); //获取IDC_LIST_PROCESS句柄 hListProcess = GetDlgItem(hDlg,IDC_LIST_PROCESS); //设置整行选中 SendMessage(hListProcess,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); //第一列 lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; lv.pszText = TEXT("进程"); //列标题 lv.cx = 200; //列宽 lv.iSubItem = 0; //ListView_InsertColumn(hListProcess, 0, &lv); SendMessage(hListProcess,LVM_INSERTCOLUMN,0,(DWORD)&lv); //第二列 lv.pszText = TEXT("PID"); lv.cx = 100; lv.iSubItem = 1; //ListView_InsertColumn(hListProcess, 1, &lv); SendMessage(hListProcess,LVM_INSERTCOLUMN,1,(DWORD)&lv); //第三列 lv.pszText = TEXT("镜像基址"); lv.cx = 100; lv.iSubItem = 2; ListView_InsertColumn(hListProcess, 2, &lv); //第四列 lv.pszText = TEXT("镜像大小"); lv.cx = 100; lv.iSubItem = 3; ListView_InsertColumn(hListProcess, 3, &lv); } case WM_INITDIALOG: { //设置ProcerssListView的风格 InitProcessListView(hDlg); break; }

win32工程实例(深入理解win32九)(15)

这里如果要显示数据的话应该用遍历进程的方法,但是这里还没有涉及到,就自己写代码添加进程进去显示

VOID EnumProcess(HWND hListProcess) { LV_ITEM vitem; //初始化 memset(&vitem,0,sizeof(LV_ITEM)); vitem.mask = LVIF_TEXT; vitem.pszText = "csrss.exe"; vitem.iItem = 0; vitem.iSubItem = 0; //ListView_InsertItem(hListProcess, &vitem); SendMessage(hListProcess, LVM_INSERTITEM,0,(DWORD)&vitem); vitem.pszText = TEXT("448"); vitem.iItem = 0; vitem.iSubItem = 1; ListView_SetItem(hListProcess, &vitem); vitem.pszText = TEXT("56590000"); vitem.iItem = 0; vitem.iSubItem = 2; ListView_SetItem(hListProcess, &vitem); vitem.pszText = TEXT("000F0000"); vitem.iItem = 0; vitem.iSubItem = 3; ListView_SetItem(hListProcess, &vitem); vitem.pszText = TEXT("winlogon.exe"); vitem.iItem = 1; vitem.iSubItem = 0; //ListView_InsertItem(hListProcess, &vitem); SendMessage(hListProcess, LVM_INSERTITEM,0,(dwORD)&vitem); vitem.pszText = TEXT("456"); vitem.iSubItem = 1; ListView_SetItem(hListProcess, &vitem); vitem.pszText = TEXT("10000000"); vitem.iSubItem = 2; ListView_SetItem(hListProcess, &vitem); vitem.pszText = TEXT("000045800"); vitem.iSubItem = 3; ListView_SetItem(hListProcess, &vitem); }

再编译生成即可发现已经生成

win32工程实例(深入理解win32九)(16)

ListView的使用

这里提一个点,使用ListView_InsertColumn跟SendMessage的效果相同

ListView_InsertColumn(hListProcess, 1, &lv); SendMessage(hListModules,LVM_INSERTCOLUMN,1,(DWORD)&lv);

这里再写一个模块函数InitProcessListView

VOID InitModulesListView(HWND hDlg) { LV_COLUMN lv; HWND hListModules; //初始化 memset(&lv,0,sizeof(LV_COLUMN)); //获取IDC_LIST_PROCESS句柄 hListModules = GetDlgItem(hDlg,IDC_LIST_MOUDLE); //设置整行选中 SendMessage(hListModules,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); //第一列 lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; lv.pszText = TEXT("模块名称"); //列标题 lv.cx = 238; //列宽 lv.iSubItem = 0; //ListView_InsertColumn(hListProcess, 0, &lv); SendMessage(hListModules,LVM_INSERTCOLUMN,0,(DWORD)&lv); //第二列 lv.pszText = TEXT("模块位置"); lv.cx = 238; lv.iSubItem = 1; //ListView_InsertColumn(hListProcess, 1, &lv); SendMessage(hListModules,LVM_INSERTCOLUMN,1,(DWORD)&lv); }

调用函数初始化

win32工程实例(深入理解win32九)(17)

即可得到界面

win32工程实例(深入理解win32九)(18)

WM_NOTIFY

该消息类型与WM_COMMAND类型相似,都是由子窗口向父窗口发送的消息。WM_NOTIFY可以包含比WM_COMMAND更丰富的信息,Windows通用组件中有很多消息,都是通过WM_NOTIFY来描述的。一般标准控件在父窗口执行使用WM_COMMAND,通用控件在父窗口执行使用WM_NOTIFY

WM_NOTIFY消息中的参数如下:

wParam:控件ID

lParam:指向一个结构

typedef struct tagNMHDR { HWND hwndFrom; //发送通知消息的控制窗口句柄 UINT idFrom; //发送通知消息的控制ID值 UINT code; //通知码,如LVM_SELCHANGED } NMHDR;

这个结构体能满足一般的要求,但能描述的信息还是有限的,如果不能满足要求还可以使用另外更复杂的结构进行标识(注意这里windows会自动帮我们替换)

以下结构体都有一个共同的特点,第一个成员都是NMHDR,即WM_NOTIFY这个通用结构体,这里体现了C 继承的思想

typedef struct tagNMLVCACHEHINT { NMHDR hdr; int iFrom; int iTo; } NMLVCACHEHINT, *PNMLVCACHEHINT; typedef struct tagLVDISPINFO { NMHDR hdr; LVITEM item; } NMLVDISPINFO, FAR *LPNMLVDISPINFO; typedef struct _NMLVFINDITEM { NMHDR hdr; int iStart; LVFINDINFO lvfi; } NMLVFINDITEM, *PNMLVFINDITEM;

这里首先写一个获取InitProcessListView这个模块进程的PID的函数

首先对从堆栈分配空间的缓冲区进行初始化操作

win32工程实例(深入理解win32九)(19)

然后选择行,这里使用SendMessage()赋值给dwRowId,然后判断dwRowId的值即可得到选取的行

当我选取第一行的时候dwRowId的值为0,选取第二行的时候dwRowId的值为1

win32工程实例(深入理解win32九)(20)

win32工程实例(深入理解win32九)(21)

而当我没有选择有数据的行的时候,dwRowId的值就为-1

win32工程实例(深入理解win32九)(22)

所以这里就可以写一个if语句进行条件的判断,当dwRowId的值为-1的时候直接弹出Error的弹窗

win32工程实例(深入理解win32九)(23)

然后获取选中行的PID,要得到PID首先要确定PID所在的列,然后指定存储缓冲区的位置和大小,然后使用SendMessage和MessageBox实现弹窗

win32工程实例(深入理解win32九)(24)

效果如下

win32工程实例(深入理解win32九)(25)

win32工程实例(深入理解win32九)(26)

完整代码如下

VOID EnumMoudules(HWND hListProcess,WPARAM wParam,LPARAM lParam) { DWORD dwRowId; TCHAR szPid[0x20]; LV_ITEM lv; //初始化 memset(&lv, 0 , sizeof(LV_ITEM)); memset(szPid, 0, 0x20); //获取选择行 dwRowId = SendMessage(hListProcess, LVM_GETNEXTITEM, -1, LVNI_SELECTED); if (dwRowId == -1) { MessageBox(NULL, TEXT("Please choose process!"), TEXT("Error"), MB_OK); return; } //获取PID lv.iSubItem = 1; //获取列 lv.pszText = szPid; //指定存储查询结果的缓冲区 lv.cchTextMax = 0x20; //指定缓冲区大小 SendMessage(hListProcess, LVM_GETITEMTEXT, dwRowId, (DWORD)&lv); MessageBox(NULL, szPid, TEXT("PID"), MB_OK); }

本文由Drunkmars原创发布转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/265424安全客 - 有思想的安全新媒体

,