Win32 common control, loading process (PE viewer) project preliminary

In the last article of this column, I led you to learn the creation of dialog boxes, and created dialog boxes in the project. In this article, I will lead you to learn Win32 common controls, understand the _WM_NOTIFY message, and lead you to initially write the prototype of the application that loads all Windows processes in the course, and lead you to explain in detail principle.

Article directory

  • 1. Standard controls and common controls
  • Two. WM_INITDIALOG message
  • Three. WM_NOTIFY message
    • 1. Use of WM_NOTIFY message
    • 2. WM_NOTIFY message parameters
    • 3. NMHDR structure
  • 4. Preliminary loading of Windows process application project
    • 1. We first draw the dialog interface:
    • 2. Then we write the application entry function:
      • Forward declaration using generic controls:
        • 1. INITCOMMONTROLSEX structure:
        • 2. `InitCommonControlsEx` function
    • 3. The callback function of the main dialog
    • 4. Initialize the process window function:
    • 5. Initialize the module window function:
      • `ListView – Insertcolumn` macro
      • `LVCOLUMN structure`:
    • 6. The process of adding fakes to the list
      • `LVM_INSERTITEM` message
      • `LVM_SETITEM` message
      • `LVITEMA` structure
    • 7. Add fake module data to the module list
  • 3. Load the initial complete code of the Windows process application project

1. Standard controls and common controls

There are many controls in Windows, some of which we do not need to load, and are frequently used, we call them standard controls, and some are not used very frequently, and we need to load them when we use them, we call them general controls.
Standard controls:

Static
Group Box
Button
Check Box
Radio Box
Edit
ComboBox
ListBox

Common controls for Windows, included in commctrl32.dll, before use:

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

Common controls include:

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

Special Note:
The control needs to be initialized by INITCOMMONCONTROLSEX before it is used. As long as this function is used anywhere in your program, it will cause the Windows program loader PE Loader to load the library.

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

Two. WM_INITDIALOG message

MSDN official documentation; WM_INITDIALOG message

#define WM_INITDIALOG 0x0110

Sent time: Before the dialog box is displayed, it is sent to the dialog callback function.
parameter:
· wParem: The handle to the control that receives the default keyboard focus. The system assigns the default keyboard focus only if the dialog procedure returns TRUE.
lParam: other initialization data.

3. WM_NOTIFY message

1. Use of WM_NOTIFY message

The message type is similar to the WM_COMMAND type, both are messages sent from the child window to the parent window.
WM_NOTIFY messages can contain richer information than WM_COMMAND.
There are many common control messages in Windows, which are described by WM_NOTIFY.

2.WM_NOTIFY message parameters

MSDN official documentation: WM_NOTIFY message

  • Send time: When an event occurs or the control needs some information, it is sent by the common control to the parent window.
  • parameter:
    wParam: The identifier of the common control that sent the message.
    lParam: Pointer to NMHDR containing notification code and other information.
  • Note:
    The target of the message must be the HWND of the control’s parent.

3. NMHDR structure

typedef struct tagNMHDR{<!-- -->
HWNDhwndFrom; //The handle of the control window that sends the notification message
UINT idFrom; //Control ID for sending notification messages
UINT code; //notification code
};

This structure can meet general requirements, but the information that can be described is still limited.
Solution: Define a structure for each notification message for different purposes:
Structure of different notification messages

4. Preliminary loading of Windows process application project

At the beginning of this chapter, we will lead you to develop an EXE that loads Windows processes, including: loading all current Windows processes, including process name, PID, mirror address, mirror size, and when you click on a process, load all the processes of the process module.
This article will lead you to write the basic code step by step, and give the complete code at the end. After the project is developed, I will release the full code.

1. We first draw the dialog interface:

Application main interface

2. Then we write the application entry function:

#include "framework.h"
#include "PEProcess.h"
#include <CommCtrl.h>
#pragma comment(lib,"comctl32.lib") //Need to use common controls
HINSTANCE hIns = 0;

//entry function
int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
) {<!-- -->
hIns = hInstance;
//Use the forward declaration of the common control
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx( &icex);

DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, (DLGPROC)MainDlgProc); //Load the main dialog box
return 0;
}

Forward declaration using common controls:

1. INITCOMMONTROLSEX structure:

Here we use the INITCOMMONCONTROLSEX structure, let’s take a look at this structure:
MSDN official document: INITCOMMONTROLSEX structure

typedef struct tagINITCOMMONCONTROLSEX {<!-- -->
  DWORD dwSize;
  DWORD dwICC;
} INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

This structure is usually used to load common control class information from the dynamic link library.

Parameter Description:
dwSize: structure size in bytes
dwICC: Set of bit flags pointing to which common control classes will be loaded from the DLL.

2. InitCommonControlsEx function

MSDN official documentation: InitCOmmonControlsEx function

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

Function description:

Function function: ensure that the common control (Comctl32) is loaded, and register the specific common control class from the DLL
Parameter Description:
Points to the INITCOMMONCONTROLSEX structure, which contains information specifying which control classes will be registered.
return value:
Returns TRUE on success, FALSE on failure.

After writing the entry function and loading the main dialog, we run it and find that the program page has come out:
program page

3. The callback function of the main dialog

We should think of:

  1. Before the dialog box is displayed, the dialog box needs to be initialized, and we need to initialize all the column names and other information
  2. Before the dialog box is displayed, the message sent is WM_INITDIALOG, and the initialization work should be completed within this message
  3. Since the initialization process is relatively complicated, I have encapsulated function processing for the initialization process window and the initialization module window respectively.
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); //Set the style of ProcessListView
break;
}
case WM_COMMAND: {<!-- -->
switch (LOWORD(wParam)) {<!-- -->
case IDC_BUTTON_ABOUT: {<!-- -->
DialogBox(hIns, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, NULL);
}
case IDC_BUTTON_PE: {<!-- -->
//Open a new dialog, PE viewer

return 0;
}
case IDC_BUTTON_OUT: {<!-- -->
EndDialog(hDlg, 0);
return TRUE;
}
}
}
}
return nRet;
}

4. Initialize the process window function:

VOID InitProcessListView(HWND hDlg) {<!-- -->
//If you set the window style, the structure is called
LV_COLUMN lv;
HWND hListProcess;

//initialization
memset( &lv, 0, sizeof(LV_COLUMN));
//Get IDC_LIST_PROCESS handle
hListProcess = GetDlgItem(hDlg, IDC_LIST3);
//Set the entire row to select
SendMessage(hListProcess, LVM_GETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

\t//first row:
lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv.pszText = (LPWSTR)TEXT("Process"); //Column title
lv.cx = 200; //line width
lv.iSubItem = 0;
//ListView_InsertColumn(hListProcess,0, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 0, (DWORD) &lv);
//EnumProcess(hLostProcess);
\t
\t//The second column
lv.pszText = (LPWSTR)TEXT("PID");
lv.cx = 75;
lv.iSubItem = 1;
//ListView_InsertColumn(hListProcess, 1, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 1, (DWORD) & amp;lv);

//third column
lv.pszText = (LPWSTR)TEXT("Mirror base address");
lv.cx = 150;
lv.iSubItem = 2;
//ListView_InsertColumn(hListProcess, 2, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 2, (DWORD) & amp;lv);

//third column
lv.pszText = (LPWSTR)TEXT("Image size");
lv.cx = 174;
lv.iSubItem = 3;
//ListView_InsertColumn(hListProcess, 3, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 3, (DWORD) &lv);
}

5. Initialize the module window function:

OID InitMoudleListView(HWND hDlg) {<!-- -->
//Setting the window style needs to call the structure
LV_COLUMN lv;
HWND hListMoudles;

//initialization
memset( &lv, 0, sizeof(LV_COLUMN));
//Get module list handle
hListMoudles = GetDlgItem(hDlg, IDC_LIST_MOUDLE);
//Set the entire row to select
SendMessage(hListMoudles, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

\t//first row:
lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv.pszText = (LPWSTR)TEXT("Module name");
lv.cx = 400;
lv.iSubItem = 0;
//ListView_Insertcolumn(hListMoudles,0, &lv);
SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD) & amp;lv);

\t//The second column:
lv.pszText = (LPWSTR)TEXT("Module location");
lv.cx = 400;
lv.iSubItem = 1;
SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD) & amp;lv);
}

We can find that in the initialization process window and initialization module window functions, we first write the title we want to display, and then use the method of sending messages to display the content. Here I have commented out one. In fact, there is no difference between the two methods, and the content can be displayed.
Let’s take a look at the ListView - Insertcolumn macro:

ListView--Insertcolumnmacro

MSDN official documentation: ListView – Insertcolumn macro

void ListView_InsertColumn(
   hwnd,
   iCol,
   pcol
);
  • Function: Insert a new column from the list view control
  • Parameters:
    • hwnd: The handle of the list view control.
    • iCol: The index of the new column.
    • pcol: Pointer to an LVCOLUMN structure containing the properties of the new column.

LVCOLUMN structure:

MSDN official documentation: LVCOLUMN structure
Contains information about a column in a report view.
Let’s check this structure in the Microsoft documentation for yourself. It is really difficult to explain.

6. The process of adding fakes to the list

Since we haven’t learned how to load all the processes in the operating system, today we will lead you to load fake process data into the list. How to load the real processes in the operating system will be explained later.

//load process function
VOID EnumProcess(HWND hListProcess) {<!-- -->
LV_ITEM vitem;

//Initialization, the first process
memset( & amp;vitem, 0, sizeof(LV_ITEM));
vitem.mask = LVIF_TEXT;

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

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

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

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


//Second process fake data:
vitem.pszText = (LPWSTR)TEXT("QQ.exe");
vitem.iItem = 1;
vitem.iSubItem = 0;
SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD) & amp;vitem);

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

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

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

//Third process fake data:
vitem.pszText = (LPWSTR)TEXT("WeChat.exe");
vitem.iItem = 2;
vitem.iSubItem = 0;
SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD) & amp;vitem);

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

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

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

Here some fake data is loaded.
Here is a place that needs special attention: When displaying the data we added in the list, only in the first one, when we use the SendMessage function, send For LVM_INSERTITEM type messages, the ListView_Insertem macro is used when using the macro. When we display data later, we all use the LVM_SETITEM type message, or use the ListView_SetItem macro, pay special attention here, if it is used incorrectly, it cannot be displayed.
Let’s take a look at the difference between these two messages:

LVM_INSERTITEMmessage

MSDN official documentation: LVM_INSERTITEM message

  • Sent On: Inserts a new item in the list view control.
  • Additional Information:
    • wParam: must be 0.
    • lParam: Pointer to an LVITEM structure specifying the properties of the list view item.

LVM_SETITEMmessage

MSDN official document: LVSETITEM message

  • Sent On: Sets some or all properties of the list view item. You can also send LVM_SETITEM to set the subitem’s text
  • Additional Information:
    • wParam: must be 0.
    • lParam: pointer to an LVITEM structure containing the properties of the new item

Note that the difference between the two is inserting new items and adding subitems in the list view control.

LVITEMA structure

MSDN official document: LVITEMA structure
Structure purpose: Used to specify or receive properties of list view items.
Due to the many usages of this structure, you can check it in the official MSDN documentation.

7. Add fake module data to the module list

This function is exactly the same as what the teacher taught in class, but it doesn’t really add data:

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

//initialization
memset( &lv, 0, sizeof(LV_ITEM));
\t
// get selected row
dwRowId = SendMessage(hListProcess, LVM_GETNEXTITEM,-1 , LVNI_SELECTED);
if (dwRowId == -1) {<!-- -->
MessageBox(NULL, TEXT("Please select a process"), TEXT("An error occurred"), MB_OK);
return;
}

// get PID
lv.iSubItem = 1;
lv.pszText = szPid;
lv.cchTextMax = 0x20;
SendMessage(hListProcess, LVM_GETITEMTEXT, dwRowId, (DWORD) &lv);

MessageBox(NULL, szPid, TEXT("PID"), MB_OK);
}

3. Load the preliminary complete code of the Windows process application project

After describing these codes, here is the complete code for everyone, but because the IDs of the controls are different, you need to modify them yourself when copying.

// PEProcess.cpp : Defines the entry point of the application.
//

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

//program forward declaration
BOOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); //Main window (dialog box) callback function
VOID InitProcessListView(HWND hDlg); //Initialize the main window process list function
VOID EnumProcess(HWND hListProcess); //Set false process data function
VOID InitMoudleListView(HWND hDlg); //Initialize the main window module list function
VOID EnumMoudles(HWND hListProcess, WPARAM wParam, LPARAM lParam); //Set fake module list function

int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
) {
hIns = hInstance;
//Use the forward declaration of the common control
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx( &icex);

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


return 0;
}


// various function implementations
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); //Set the style of ProcessListView and initialize the process list
InitMoudleListView(hDlg); //Set the style of MoudleListView, initialize the module list
break;
}
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BUTTON_ABOUT: {
DialogBox(hIns, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, NULL);
}
case IDC_BUTTON_PE: {
//Open a new dialog, PE viewer

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 & amp; & amp; pNMHDR->code == NM_CLICK) {
EnumMoudles(GetDlgItem(hDlg, IDC_LIST_PROCESS), wParam, lParam);
}
break;
}
}
return nRet;
}

VOID InitProcessListView(HWND hDlg) {
//Set the window style call structure
LV_COLUMN lv;
HWND hListProcess;

//initialization
memset( &lv, 0, sizeof(LV_COLUMN));
//Get process list handle
hListProcess = GetDlgItem(hDlg, IDC_LIST_PROCESS);
//Set the entire row to select
SendMessage(hListProcess, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
//Error code:::::
SendMessage(hListProcess, LVM_GETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

\t//first row:
lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv.pszText = (LPWSTR)TEXT("Process"); //Column title
lv.cx = 350; //line width
lv.iSubItem = 0;
//ListView_InsertColumn(hListProcess,0, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 0, (DWORD) &lv);
\t
\t//The second column
lv.pszText = (LPWSTR)TEXT("PID");
lv.cx = 100;
lv.iSubItem = 1;
//ListView_InsertColumn(hListProcess, 1, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 1, (DWORD) & amp;lv);

//third column
lv.pszText = (LPWSTR)TEXT("Mirror base address");
lv.cx = 150;
lv.iSubItem = 2;
//ListView_InsertColumn(hListProcess, 2, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 2, (DWORD) & amp;lv);

//fourth column
lv.pszText = (LPWSTR)TEXT("Image size");
lv.cx = 200;
lv.iSubItem = 3;
//ListView_InsertColumn(hListProcess, 3, &lv);
SendMessage(hListProcess, LVM_INSERTCOLUMN, 3, (DWORD) &lv);
EnumProcess(hListProcess);
}

// load process function
VOID EnumProcess(HWND hListProcess) {
LV_ITEM vitem;

//Initialization, the first process
memset( & amp;vitem, 0, sizeof(LV_ITEM));
vitem.mask = LVIF_TEXT;

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

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

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

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


//Second process fake data:
vitem.pszText = (LPWSTR)TEXT("QQ.exe");
vitem.iItem = 1;
vitem.iSubItem = 0;
SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD) & amp;vitem);

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

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

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

//Third process fake data:
vitem.pszText = (LPWSTR)TEXT("WeChat.exe");
vitem.iItem = 2;
vitem.iSubItem = 0;
SendMessage(hListProcess, LVM_INSERTITEM, 0, (DWORD) & amp;vitem);

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

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

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


VOID InitMoudleListView(HWND hDlg) {<!-- -->
//Setting the window style needs to call the structure
LV_COLUMN lv;
HWND hListMoudles;

//initialization
memset( &lv, 0, sizeof(LV_COLUMN));
//Get module list handle
hListMoudles = GetDlgItem(hDlg, IDC_LIST_MOUDLE);
//Set the entire row to select
SendMessage(hListMoudles, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

\t//first row:
lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv.pszText = (LPWSTR)TEXT("Module name");
lv.cx = 400;
lv.iSubItem = 0;
//ListView_Insertcolumn(hListMoudles,0, &lv);
SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD) & amp;lv);

\t//The second column:
lv.pszText = (LPWSTR)TEXT("Module location");
lv.cx = 400;
lv.iSubItem = 1;
SendMessage(hListMoudles, LVM_INSERTCOLUMN, 0, (DWORD) & amp;lv);
}


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

//initialization
memset( &lv, 0, sizeof(LV_ITEM));
\t
// get selected row
dwRowId = SendMessage(hListProcess, LVM_GETNEXTITEM,-1, LVNI_SELECTED);
if (dwRowId == -1) {
MessageBox(NULL, TEXT("Please select a process"), TEXT("An error occurred"), MB_OK);
return;
}

// get PID
lv.iSubItem = 1;
lv.pszText = szPid;
lv.cchTextMax = 0x20;
SendMessage(hListProcess, LVM_GETITEMTEXT, dwRowId, (DWORD) &lv);

MessageBox(NULL, szPid, TEXT("PID"), MB_OK);
}

Program effect:
program effect

That’s all for today’s sharing. If there are mistakes in the article or if I don’t understand it well, please point it out in time. I will learn very humbly. I hope everyone will make progress together! ! !