Win32通用控件,加载进程(PE查看器)项目初步

news2024/12/26 12:02:40

在本专栏上一篇文章中带领大家学习了对话框的创建,并且在项目中创建出了对话框。在这一篇文章中,我将带领大家学习Win32通用控件,了解_WM_NOTIFY消息,并且带领大家初步写出课程中加载Windows所有进程的应用程序的雏形,带领大家详解其中的原理。

文章目录

  • 一.标准控件与通用控件
  • 二.WM_INITDIALOG消息
  • 三.WM_NOTIFY消息
    • 1.WM_NOTIFY消息的使用
    • 2.WM_NOTIFY消息参数
    • 3.NMHDR结构
  • 四.加载Windows进程应用程序项目初步
    • 1. 我们首先画出对话框界面:
    • 2. 然后我们来写出应用程序入口函数:
      • 使用通用控件的向前声明:
        • 1. INITCOMMONTROLSEX结构体:
        • 2.`InitCommonControlsEx`函数
    • 3.主对话框的回调函数
    • 4.初始化进程窗口函数:
    • 5.初始化模块窗口函数:
      • `ListView——Insertcolumn`宏
      • ` LVCOLUMN 结构`:
    • 6.向列表中加假的进程
      • `LVM_INSERTITEM`消息
      • `LVM_SETITEM`消息
      • `LVITEMA` 结构
    • 7.向模块列表中加入假的模块数据
  • 三.加载Windows进程应用程序项目初步完整代码

一.标准控件与通用控件

Windows中有很多控件,有一些控件是我们不需要加载的,使用频率很高我们称为标准控件,有一些使用频率不是很高,我们在使用的时候需要加载,我们称为通用控件。
标准控件:

Static
Group Box
Button
CHeck Box
Radio Box
Edit
ComboBox
ListBox

Windows通用控件,包含在commctrl32.dll中,使用前需要:

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

通用控件包括:

Animation
ComboBoxEx
Drag_List_Box
Flat_Scroll_bar
Header
HotKey
ImageList
IPAddress
List_View
Month_Calendar
Pager
Progress_Bar
Property_Sheets
Rebar
Status Bars
SysLink
Tab
Toolbar
Trackbar
TreeView
Up_and_Down

特别说明:
通过用控件在使用前,需要通过INITCOMMONCONTROLSEX进行初始化。 只要在你的程序中任意地方使用了该函数,就会使得Windows的程序加载器PE Loader加载该库。

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

二.WM_INITDIALOG消息

MSDN官方文档;WM_INITDIALOG消息

#define WM_INITDIALOG                   0x0110

发送时间:在对话框显示之前,发送到对话框回调函数。
参数:
· wParem:用于接收默认键盘焦点的控件的句柄。 仅当对话框过程返回 TRUE 时,系统才分配默认键盘焦点。
lParam:其他初始化数据。

三.WM_NOTIFY消息

1.WM_NOTIFY消息的使用

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

2.WM_NOTIFY消息参数

MSDN官方文档:WM_NOTIFY消息

  • 发送时间:当发生事件或控件需要一些信息时,由公共控件发送到父窗口。
  • 参数:
    wParam:发送消息的公用控件的标识符。
    lParam:指向包含通知代码和其他信息的NMHDR的指针。
  • 注意:
    消息的目标必须是控件父级的HWND.

3.NMHDR结构

typedef struct tagNMHDR{
	HWNDhwndFrom;      //发送通知消息的控件窗口句柄
	UINT idFrom;       //发送通知消息的控件ID
	UINT code;         //通知代码
};

这个结构体能满足一般的需求,但能描述的信息任然是有限的。
解决方案:对每一种不同用途的通知消息都定义一种结构体来表示:
不同通知消息的结构体

四.加载Windows进程应用程序项目初步

这一章开始,带领大家开发一个加载Windows进程的EXE,其中包括:加载Windows当前的所有进程,包括进程名,PID,镜像地址,镜像大小,并且再单击进程的时候,加载该进程所有的模块。
本篇文章将会一步一步带领大家写出基本的代码,在最后给出完整的代码。在项目开发完毕后,我会发布完整代码。

1. 我们首先画出对话框界面:

应用程序主界面

2. 然后我们来写出应用程序入口函数:

#include "framework.h"
#include "PEProcess.h"
#include <CommCtrl.h>             
#pragma comment(lib,"comctl32.lib")        //需要使用通用控件
HINSTANCE hIns = 0;

//入口函数
int APIENTRY WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow
) {
	hIns = hInstance;
	//使用通用控件的向前声明
	INITCOMMONCONTROLSEX icex;
	icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icex.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&icex);

	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, (DLGPROC)MainDlgProc);     //加载主对话框
	return 0;
}

使用通用控件的向前声明:

1. INITCOMMONTROLSEX结构体:

这里我们使用到了INITCOMMONCONTROLSEX结构体,我们来看看这个结构体:
MSDN官方文档:INITCOMMONTROLSEX结构体

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

该结构通常用于从动态链接库加载常见控件类信息。

参数说明:
dwSize:结构大小(以字节为单位)
dwICC:指向将从DLL加载哪些常见控件类的位标志集。

2.InitCommonControlsEx函数

MSDN官方文档:InitCOmmonControlsEx函数

BOOL InitCOmmonControlsEx(
	[in] const INITCOMMONCONTROLSEX *pocce
);

函数说明:

函数功能:确保加载公共控件(Comctl32),并从该DLL注册特定的公共控件类
参数说明:
指向INITCOMMONCONTROLSEX结构体,该结构包含指定将注册哪些控件类信息。
返回值:
成功返回TRUE,失败返回FALSE.

在写入了入口函数,并且加载了主对话框之后,我们运行,发现程序页面已经出来了:
程序页面

3.主对话框的回调函数

我们应该想到:

  1. 在对话框显示之前,需要初始化对话框,需要我们将列名等信息全部初始化
  2. 在对话框显示之前,发送的消息为WM_INITDIALOG,初始化的工作应该在这个消息内完成
  3. 由于初始化的过程比较复杂,我这里对初始化进程窗口和初始化模块窗口分别封装了函数处理
OOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	BOOL nRet = FALSE;

	switch (uMsg) {
		case WM_CLOSE: {
			EndDialog(hDlg, 0);
			PostQuitMessage(0);
			break;
		}
		case WM_INITDIALOG: {
			InitProcessListView(hDlg);         //设置ProcessListView的风格
			break;
		}
		case WM_COMMAND: {
			switch (LOWORD(wParam)) {
				case IDC_BUTTON_ABOUT: {
					DialogBox(hIns, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, NULL);
				}
				case IDC_BUTTON_PE: {
					//打开新的对话框,PE查看器

					return 0;
				}
				case IDC_BUTTON_OUT: {
					EndDialog(hDlg, 0);
					return TRUE;
				}
			}
		}
	}
	return nRet;
}

4.初始化进程窗口函数:

VOID InitProcessListView(HWND hDlg) {
	//设置窗口风格的话调用了结构体
	LV_COLUMN lv;
	HWND hListProcess;

	//初始化
	memset(&lv, 0, sizeof(LV_COLUMN));
	//获取IDC_LIST_PROCESS句柄
	hListProcess = GetDlgItem(hDlg, IDC_LIST3);
	//设置整行选中
	SendMessage(hListProcess, LVM_GETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

	//第一列:
	lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
	lv.pszText = (LPWSTR)TEXT("进程");           //列标题
	lv.cx = 200;             //行宽
	lv.iSubItem = 0;
	//ListView_InsertColumn(hListProcess,0,&lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 0, (DWORD)&lv);
	//EnumProcess(hLostProcess);
	
	//第二列
	lv.pszText = (LPWSTR)TEXT("PID");
	lv.cx = 75;
	lv.iSubItem = 1;
	//ListView_InsertColumn(hListProcess, 1, &lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 1, (DWORD)&lv);

	//第三列
	lv.pszText = (LPWSTR)TEXT("镜像基址");
	lv.cx = 150;
	lv.iSubItem = 2;
	//ListView_InsertColumn(hListProcess, 2, &lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 2, (DWORD)&lv);

	//第三列
	lv.pszText = (LPWSTR)TEXT("镜像大小");
	lv.cx = 174;
	lv.iSubItem = 3;
	//ListView_InsertColumn(hListProcess, 3, &lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 3, (DWORD)&lv);
}

5.初始化模块窗口函数:

OID InitMoudleListView(HWND hDlg) {
	//设置窗口风格需要调用结构体
	LV_COLUMN lv;
	HWND hListMoudles;

	//初始化
	memset(&lv, 0, sizeof(LV_COLUMN));
	//获取模块列表句柄
	hListMoudles = GetDlgItem(hDlg, IDC_LIST_MOUDLE);
	//设置整行选中
	SendMessage(hListMoudles, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

	//第一列:
	lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
	lv.pszText = (LPWSTR)TEXT("模块名称");
	lv.cx = 400;
	lv.iSubItem = 0;
	//ListView_Insertcolumn(hListMoudles,0,&lv);
	SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD)&lv);

	//第二列:
	lv.pszText = (LPWSTR)TEXT("模块位置");
	lv.cx = 400;
	lv.iSubItem = 1;
	SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD)&lv);
}

我们可以发现,在初始化进程窗口和初始化模块窗口函数中,我们先写好了我们想要显示的标题,之后使用发送消息的方式使内容显示。这里我注释掉了一个,实际上这两种方式没有什么区别,都能使内容显示。
我们来看看ListView——Insertcolumn宏:

ListView——Insertcolumn

MSDN官方文档:ListView——Insertcolumn宏

void ListView_InsertColumn(
   hwnd,
   iCol,
   pcol
);
  • 功能:从列表视图控件中插入新列
  • 参数:
    • hwnd:列表视图控件的句柄。
    • iCol:新列的索引。
    • pcol:指向包含新列属性的 LVCOLUMN 结构的指针。

LVCOLUMN 结构:

MSDN官方文档:LVCOLUMN 结构
包含有关报表视图中的列的信息。
这个结构体大家到微软文档中自行查看吧,解释起来真的很费劲。

6.向列表中加假的进程

由于我们还没有学习如何加载操作系统中的所有进程,所以今天带领大家向列表中加载假的进程数据,有关如何加载操作系统真正的进程,后续会为大家讲解。

//加载进程函数
VOID EnumProcess(HWND hListProcess) {
	LV_ITEM vitem;

	//初始化,第一个进程
	memset(&vitem, 0, sizeof(LV_ITEM));
	vitem.mask = LVIF_TEXT;

	//假数据:
	vitem.pszText = (LPWSTR)TEXT("csrss.exe");
	vitem.iItem = 0;
	vitem.iSubItem = 0;
	//ListView_Insertem(hListProcess,*vitem);
	SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem);

	vitem.pszText = (LPWSTR)TEXT("448");
	vitem.iItem = 0;
	vitem.iSubItem = 1;
	SendMessage(hListProcess, LVM_SETITEM, 0, (DWORD)&vitem);
	//ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("56590000");
	vitem.iItem = 0;
	vitem.iSubItem = 2;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("000F0000");
	vitem.iItem = 0;
	vitem.iSubItem = 3;
	ListView_SetItem(hListProcess, &vitem);


	//第二个进程假数据:
	vitem.pszText = (LPWSTR)TEXT("QQ.exe");
	vitem.iItem = 1;
	vitem.iSubItem = 0;
	SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem);

	vitem.pszText = (LPWSTR)TEXT("153");
	vitem.iItem = 1;
	vitem.iSubItem = 1;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("65580000");
	vitem.iItem = 1;
	vitem.iSubItem = 2;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("001E0000");
	vitem.iItem = 1;
	vitem.iSubItem = 3;
	ListView_SetItem(hListProcess, &vitem);

	//第三个进程假数据:
	vitem.pszText = (LPWSTR)TEXT("WeChat.exe");
	vitem.iItem = 2;
	vitem.iSubItem = 0;
	SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem);

	vitem.pszText = (LPWSTR)TEXT("256");
	vitem.iItem = 2;
	vitem.iSubItem = 1;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("75960000");
	vitem.iItem = 2;
	vitem.iSubItem = 2;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("015B0000");
	vitem.iItem = 2;
	vitem.iSubItem = 3;
	ListView_SetItem(hListProcess, &vitem);
}

这里加载了一些假的数据。
这里有个需要尤其注意的地方: 就是在列表中显示我们加入的数据的时候,只有在第一个,我们使用SendMessage函数的时候,发送了LVM_INSERTITEM类型的消息,使用宏的时候使用了ListView_Insertem宏,在后续显示数据的时候,我们都是用的是LVM_SETITEM类型的消息,或者是使用了ListView_SetItem宏,这里要尤其注意,如果使用错误的话,是不能显示的。
我们来看看这两种消息的区别:

LVM_INSERTITEM消息

MSDN官方文档:LVM_INSERTITEM消息

  • 发送时间:在列表视图控件中插入新项。
  • 附加信息:
    • wParam:必须为0.
    • lParam:指向指定列表视图项属性的 LVITEM 结构的指针。

LVM_SETITEM消息

MSDN官方文档:LVSETITEM消息

  • 发送时间:设置列表视图项的某些或全部属性。 还可以发送LVM_SETITEM来设置子项的文本
  • 附加信息:
    • wParam:必须为0.
    • lParam:指向包含新项属性的 LVITEM 结构的指针

注意两者区别为在列表视图控件中插入新项和添加子项。

LVITEMA 结构

MSDN官方文档:LVITEMA结构
结构用途:用于指定或接收列表视图项的属性。
这个结构由于用法很多,大家自行到MSDN官方文档中查阅。

7.向模块列表中加入假的模块数据

这个函数与老师上课讲的完全一致,但是并非真的添加了数据:

VOID EnumMoudles(HWND hListProcess, WPARAM wParam, LPARAM lParam) {
	DWORD dwRowId;
	TCHAR szPid[21];
	LV_ITEM lv;

	//初始化
	memset(&lv, 0, sizeof(LV_ITEM));
	
	//获取选择行
	dwRowId = SendMessage(hListProcess, LVM_GETNEXTITEM,-1 , LVNI_SELECTED);
	if (dwRowId == -1) {
		MessageBox(NULL, TEXT("请选择进程"), TEXT("出错啦"), 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);
}

三.加载Windows进程应用程序项目初步完整代码

讲述了这些代码,这里给大家给出完整代码,但是由于控件的ID不同,复制的时候还需要自己改动。

// PEProcess.cpp : 定义应用程序的入口点。
//

#include "framework.h"
#include "PEProcess.h"
#include <CommCtrl.h>
#pragma comment(lib,"comctl32.lib")
HINSTANCE hIns = 0;

//程序向前声明
BOOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);     //主窗口(对话框)回调函数
VOID InitProcessListView(HWND hDlg);       //初始化主窗口进程列表函数
VOID EnumProcess(HWND hListProcess);       //设置假的进程数据函数
VOID InitMoudleListView(HWND hDlg);        //初始化主窗口模块列表函数
VOID EnumMoudles(HWND hListProcess, WPARAM wParam, LPARAM lParam);  //设置假的模块列表函数

int APIENTRY WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow
) {
	hIns = hInstance;
	//使用通用控件的向前声明
	INITCOMMONCONTROLSEX icex;
	icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icex.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&icex);

	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, (DLGPROC)MainDlgProc);


	return 0;
}


//各类函数实现
BOOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	BOOL nRet = FALSE;

	switch (uMsg) {
		case WM_CLOSE: {
			EndDialog(hDlg, 0);
			PostQuitMessage(0);
			break;
		}
		case WM_INITDIALOG: {
			InitProcessListView(hDlg);         //设置ProcessListView的风格,初始化进程列表
			InitMoudleListView(hDlg);          //设置MoudleListView的风格,初始化模块列表
			break;
		}
		case WM_COMMAND: {
			switch (LOWORD(wParam)) {
				case IDC_BUTTON_ABOUT: {
					DialogBox(hIns, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, NULL);
				}
				case IDC_BUTTON_PE: {
					//打开新的对话框,PE查看器

					return 0;
				}
				case IDC_BUTTON_OUT: {
					EndDialog(hDlg, 0);
					PostQuitMessage(0);
					return TRUE;
				}
			}
		}
		case WM_NOTIFY: {
			NMHDR* pNMHDR = (NMHDR*)lParam;
			if (wParam == IDC_LIST_PROCESS && pNMHDR->code == NM_CLICK) {
				EnumMoudles(GetDlgItem(hDlg, IDC_LIST_PROCESS), wParam, lParam);
			}
			break;
		}
	}
	return nRet;
}

VOID InitProcessListView(HWND hDlg) {
	//设置窗口风格调用结构体
	LV_COLUMN lv;
	HWND hListProcess;

	//初始化
	memset(&lv, 0, sizeof(LV_COLUMN));
	//获取进程列表句柄
	hListProcess = GetDlgItem(hDlg, IDC_LIST_PROCESS);
	//设置整行选中
	SendMessage(hListProcess, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
	//出错代码:::::
	SendMessage(hListProcess, LVM_GETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

	//第一列:
	lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
	lv.pszText = (LPWSTR)TEXT("进程");           //列标题
	lv.cx = 350;             //行宽
	lv.iSubItem = 0;
	//ListView_InsertColumn(hListProcess,0,&lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 0, (DWORD)&lv);
	
	//第二列
	lv.pszText = (LPWSTR)TEXT("PID");
	lv.cx = 100;
	lv.iSubItem = 1;
	//ListView_InsertColumn(hListProcess, 1, &lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 1, (DWORD)&lv);

	//第三列
	lv.pszText = (LPWSTR)TEXT("镜像基址");
	lv.cx = 150;
	lv.iSubItem = 2;
	//ListView_InsertColumn(hListProcess, 2, &lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 2, (DWORD)&lv);

	//第四列
	lv.pszText = (LPWSTR)TEXT("镜像大小");
	lv.cx = 200;
	lv.iSubItem = 3;
	//ListView_InsertColumn(hListProcess, 3, &lv);
	SendMessage(hListProcess, LVM_INSERTCOLUMN, 3, (DWORD)&lv);
	EnumProcess(hListProcess);
}

//加载进程函数
VOID EnumProcess(HWND hListProcess) {
	LV_ITEM vitem;

	//初始化,第一个进程
	memset(&vitem, 0, sizeof(LV_ITEM));
	vitem.mask = LVIF_TEXT;

	//假数据:
	vitem.pszText = (LPWSTR)TEXT("csrss.exe");
	vitem.iItem = 0;
	vitem.iSubItem = 0;
	//ListView_Insertem(hListProcess,*vitem);
	SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem);

	vitem.pszText = (LPWSTR)TEXT("448");
	vitem.iItem = 0;
	vitem.iSubItem = 1;
	SendMessage(hListProcess, LVM_SETITEM, 0, (DWORD)&vitem);
	//ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("56590000");
	vitem.iItem = 0;
	vitem.iSubItem = 2;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("000F0000");
	vitem.iItem = 0;
	vitem.iSubItem = 3;
	ListView_SetItem(hListProcess, &vitem);


	//第二个进程假数据:
	vitem.pszText = (LPWSTR)TEXT("QQ.exe");
	vitem.iItem = 1;
	vitem.iSubItem = 0;
	SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem);

	vitem.pszText = (LPWSTR)TEXT("153");
	vitem.iItem = 1;
	vitem.iSubItem = 1;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("65580000");
	vitem.iItem = 1;
	vitem.iSubItem = 2;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("001E0000");
	vitem.iItem = 1;
	vitem.iSubItem = 3;
	ListView_SetItem(hListProcess, &vitem);

	//第三个进程假数据:
	vitem.pszText = (LPWSTR)TEXT("WeChat.exe");
	vitem.iItem = 2;
	vitem.iSubItem = 0;
	SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD)&vitem);

	vitem.pszText = (LPWSTR)TEXT("256");
	vitem.iItem = 2;
	vitem.iSubItem = 1;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("75960000");
	vitem.iItem = 2;
	vitem.iSubItem = 2;
	ListView_SetItem(hListProcess, &vitem);

	vitem.pszText = (LPWSTR)TEXT("015B0000");
	vitem.iItem = 2;
	vitem.iSubItem = 3;
	ListView_SetItem(hListProcess, &vitem);
}


VOID InitMoudleListView(HWND hDlg) {
	//设置窗口风格需要调用结构体
	LV_COLUMN lv;
	HWND hListMoudles;

	//初始化
	memset(&lv, 0, sizeof(LV_COLUMN));
	//获取模块列表句柄
	hListMoudles = GetDlgItem(hDlg, IDC_LIST_MOUDLE);
	//设置整行选中
	SendMessage(hListMoudles, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

	//第一列:
	lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
	lv.pszText = (LPWSTR)TEXT("模块名称");
	lv.cx = 400;
	lv.iSubItem = 0;
	//ListView_Insertcolumn(hListMoudles,0,&lv);
	SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD)&lv);

	//第二列:
	lv.pszText = (LPWSTR)TEXT("模块位置");
	lv.cx = 400;
	lv.iSubItem = 1;
	SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD)&lv);
}


VOID EnumMoudles(HWND hListProcess, WPARAM wParam, LPARAM lParam) {
	DWORD dwRowId;
	TCHAR szPid[21];
	LV_ITEM lv;

	//初始化
	memset(&lv, 0, sizeof(LV_ITEM));
	
	//获取选择行
	dwRowId = SendMessage(hListProcess, LVM_GETNEXTITEM,-1 , LVNI_SELECTED);
	if (dwRowId == -1) {
		MessageBox(NULL, TEXT("请选择进程"), TEXT("出错啦"), 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);
}

程序效果:
程序效果

今天的分享就到这里,如果文章中出现错误或者是本人理解不到位的地方,还请大家及时指出来,我会非常虚心地学习。希望大家共同进步!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/563614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

因为懒,我用了“低代码”打下手

目录 一、前言 二、工具介绍 三、平台特点 四、如何使用JNPF&#xff1f; 五、低代码平台的选择参考 六、总结 一、前言 「一个优秀的开发者&#xff0c;一定是会利用各种工具来提升自己的开发效率。」 前段时间&#xff0c;体验了很多国内前沿的低代码平台&#xff0c;在…

Fiddler抓包工具之Fiddler界面主菜单功能介绍

Fiddler界面主菜单功能介绍 File菜单 File菜单中的命令主要支持完成通过Fiddler来启动和停止web流量的捕获&#xff08;capture&#xff09;,也可以加载或存储捕获的流量 &#xff08;1&#xff09;Capture Traffic&#xff1a;默认勾选&#xff0c;勾选此项才可抓包&#xff…

Linux ALSA音频工具

参考&#xff1a; ALSA 音频工具 amixer、aplay、arecord Linux Alsa ALSA的配置文件 音频录制——arecord 音频播放——aplay 音频配置——amixer alsamixer与amixer的区别 alsamixer是Linux音频框架ALSA工具之一&#xff0c;用于配置音频各个参数; alsamixer是基于文本图形…

APP中有html5页面的时候,怎么进行元素定位

测试app的时候&#xff0c;我们知道可以通过UI Automator Viewer进行元素定位 但是很多app中都会内嵌h5页面&#xff0c;这个时候定位就会变成下图这样&#xff1a; 第一步&#xff1a; 在手机中打开当前app的h5界面&#xff0c;使用usb连接电脑后&#xff0c; 第二步&#x…

聚焦金融行业网络安全|安全狗亮相知虎科技生态大会

5月18日&#xff0c;知虎科技生态大会在上海顺利举办。作为国内云原生安全领导厂商&#xff0c;安全狗也受邀出席此次活动。 据悉&#xff0c;此次大会邀请了来自国内顶级安全厂商的安全专家介绍从网络安全、应用安全、业务安全、数据安全、资金安全、通信安全等六个方面全方位…

抖音账号矩阵系统源码/技术开发搭建私有化部署开源

抖音SEO矩阵系统是基于抖音平台的搜索引擎优化技术的一种系统&#xff0c;其主要作用是通过一系列的技术手段&#xff0c;提高抖音视频的曝光和排名&#xff0c;使其获得更多的流量和粉丝。在本文中&#xff0c;我们将介绍抖音SEO矩阵系统的开发技术&#xff0c;包括系统设计、…

ElasticSearch集成SpringBoot实践及数据同步

一 前言 ES 全称 Elasticsearch 是一款分布式的全文搜索引擎&#xff0c;在互联网公司中&#xff0c;这款搜索引擎一直被程序员们所推崇。常见的使用场景如ELK日志分析&#xff0c;电商APP的商品推荐&#xff0c;社交APP的同城用户推荐等等。今天结合自己平时的一些学习对它与…

分布式事务解决方案Seata-1.6.0版本Docker安装

1. 简介 为什么要安装1.6.0 版本&#xff1f; 因为低版本Seata只支持单表的DDL语句&#xff0c;只有在在1.6.0版本及以上才支持 UPDATE JOIN。 注&#xff1a;1.6.0版本与 低版本的主要区别在于配置文件格式的修改&#xff0c;一站式解决方案&#xff1a;分布式事务解决方案Sea…

有哪些好用的App云测试平台?

一、国内外6种好用app云测平台推荐&#xff08;章节末附pk图&#xff09; 1、国内云测平台 1&#xff09;Testin云测 网址&#xff1a;https://www.testin.cn/ Testin云测平台是一款基于云端的移动应用测试平台&#xff0c;为移动应用开发者和测试人员提供一站式的移动应用质…

DNS风险分析及安全防护研究(一):DNS自身风险分析(中科三方)

作为互联网上的一项基础服务&#xff0c;DNS在网站运行中起到了至关重要的作用&#xff0c;然而其安全性在很长一段时间内都没有得到足够的重视。DNS采用不可靠的UDP协议&#xff0c;安全性具有较大的漏洞&#xff0c;攻击者很容易利用这些漏洞发动攻击&#xff0c;从而引起一些…

第三章 向量与线性方程组

引言 题型总结中推荐例题有蓝皮书的题型较为重要&#xff0c;只有吉米多维奇的题型次之。码字不易&#xff0c;如果这篇文章对您有帮助的话&#xff0c;希望您能点赞、评论、收藏&#xff0c;投币、转发、关注。您的鼓励就是我前进的动力&#xff01; 知识点思维导图 补充&…

CodeForces.1786A2.发牌.[中等][flg标识][数学规律][双色牌]

题目描述&#xff1a; 题目解读&#xff1a; 发牌问题&#xff0c;给两人发双色牌&#xff0c;同样还是 给a发1张&#xff0c;然后给b发2&#xff0c;3张&#xff1b; 给a发4&#xff0c;5张&#xff0c;给b发6&#xff0c;7张&#xff1b; 给a发8&#xff0c;9张&#xff…

软件测试基础知识整理(七)- 因果图法、正交法、场景法、错误推测法

目录 一、因果图法 1.1 因果图中的基本符号 1.2 操作步骤 二、正交法 2.1 正交表概念 2.2 举例说明 三、场景法 3.1 操作步骤 3.2 举例说明 四、错误推测法&#xff08;了解&#xff09; 一、因果图法 因果图法用于识别系统中可能存在的输入和输出的关系&#xff0c;…

《高性能MySQL》——创建高性能的索引(笔记)

文章目录 五、创建高性能的索引5.1 索引基础5.1.1 索引的类型B-Tree索引哈希索引空间数据索引(R-Tree)全文索引其他索引类别 5.2 索引的优点索引是最好的解决方案吗? 5.3 高性能的索引策略5.3.1 独立的列5.3.2 前缀索引和索引选择性5.3.3 多列索引5.3.4 选择合适的索引列顺序5…

了解和使用Docker

前提 本文对 Docker 进行全面阐述&#xff0c;详细介绍 Docker 的作用、其基本使用&#xff0c;如常用命令、Dockerfile 的作用及使用、Docker Compose 的作用及使用。常用的基本上都会涉及&#xff0c;其他可以在 Docker 官网进行查漏补缺。 下面还有投票&#xff0c;一起参…

CRM客户管理系统开发 获客管理营销全搞定

企业经营管理是有很大学问的&#xff0c;无论是生产经营、销售、服务还是客户管理、维护、营销都是需要付出一定的人力物力来管理的。传统的企业管理多是通过人工方式来完成&#xff0c;个中细节繁琐复杂&#xff0c;耗时耗力还很容易出现纰漏。所以随着科技的发展&#xff0c;…

vscode配置文件-vutur自动格式化-eslint校验-属性换行

vscode配置文件-vutur自动格式化-eslint校验-属性换行 {// eslint 配置"eslint.enable": true,"eslint.run": "onType","eslint.options": {"extensions": [".js",".vue",".jsx",".tsx…

实验篇(7.2) 02. 部署物理实验环境(上) 远程访问 ❀ Fortinet网络安全专家 NSE4

【简介】当大家了解到并不需要很高的代价就可以动手做FortiOS 7.2的实验&#xff0c;很多人愿意尝试使用FortiGate防火墙硬件来学习最验难掌握的远程访问部分&#xff0c;这里我们将学习现场部署一套物理实验环境&#xff0c;让大家看到&#xff0c;在一张桌子上&#xff0c;在…

【软件设计师】高频考点集锦

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软考…

什么是IP地址及IP地址分类详解

概念 IP地址&#xff0c;英文名为IP Address&#xff0c;是internet protocol address的缩写&#xff0c;译为互联网协议地址&#xff0c;又译为网际协议地址。它是IP协议&#xff08;internet protocol &#xff09;提供的一种统一的地址格式&#xff0c;分配给使用IP协议的设…