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:
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:
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:
3. The callback function of the main dialog
We should think of:
- 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
- Before the dialog box is displayed, the message sent is
WM_INITDIALOG
, and the initialization work should be completed within this message - 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--Insertcolumn
macro
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_INSERTITEM
message
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_SETITEM
message
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:
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! ! !