深入理解win32(九)

 

前言

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

 

准备工作

首先新建一个资源脚本

创建一个dialog

在父窗口里新建两个窗口

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

然后添加Dialog的相关函数

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

 

标准控件和通用控件

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

Static
Group Box
Button
Check Box
Radio Button
Edit
ComboBox
ListBox

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

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

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

这里再把样式改为报告

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

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

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

INITCOMMONCONTROLSEX

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

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

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

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

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

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

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;
        }

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

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);                        

}

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

 

ListView的使用

这里提一个点,使用ListView_InsertColumnSendMessage的效果相同

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);

}

调用函数初始化

即可得到界面

 

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的函数

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

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

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

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

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

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

效果如下

完整代码如下

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);
}
(完)