When the program has an exception and loses its response, it is common practice to open the Windows Task Manager and force it to "kill" it.
Windows Task Manager is a good thing to show all the processes that are executing on the current system. And their real-time performance parameters. But as a program ape, do you know how these functions are implemented?
"What's so hard about that?! "You might say," It's not just a few process enumeration functions called. "Yes, it's not hard to simply implement a feature similar to the Windows Task Manager. But. You're not going to hurry. About the process enumeration, maybe you just may not, tress; Besides, we have three and four of them here. Besides. We also have enhancements here that show the modules (i.e. DLLs, dynamic link libraries) information associated with each process.
Basic knowledge of processes and DLLs
You know. Windows 98/2000/XP is a multitasking operating system. So-called multi-tasking. is the ability to execute multiple processes at the same time in the system. The so-called process is the execution instance of the application. In layman's words, a process is an execution. EXE program.
Processes in the system are uniquely identified with a DWORD type of data, which we call PID. Even if the same application executes multiple instances. Their PID is not the same. Other than that. Processes have their own private virtual address space, and processes do not interfere with each other; Each process includes at least one thread.
So. What does a DLL have to do with a process? As you know, the Windows operating system uses DLLs to support public function calls from the date Windows was born. The function code implemented in the DLL does not show up in the. exe file, but can be used by various processes.
Advantages of using DLLs include:
1) can significantly reduce the size of each component (especially for some large software systems).
2) make the upgrade easier.
Let's say we want to use the new version number function. After changing the function in the DLL. Just need to compile the DLL project again. Then connect the individual applications that use the function, and the application itself does not need to be compiled again.
3) facilitates functional modularity and even team collaboration for development tasks.
In general, a process always calls a function in this or that DLL. A process is a dependency of a DLL. In our demo program, we not only do the process enumeration. We also want to reveal this dependency of the process and DLL. The user interface for the demo program is as follows:
Figure 1 The user interface of the demo program
Okay, let's go straight. Then go on. Let's introduce the various process enumeration methods.
Method One: Using the tool library function
This is one of the oldest and most important ways to support this from the beginning of Windows 95. The most important of these API functions is CreateToolhelp32Snapshot, its function prototypes such as the following:
HANDLE WINAPI CreateToolhelp32Snapshot (
DWORD DwFlags,
DWORD Th32processid
);
The function is to take a "snapshot" of the system. The photographed object is determined by the number of dwflags, for example, the dwflags value of th32cs_snapprocess indicates that the object is the entire process in the system, and the value is Th32cs_ Snapmodule indicates that the object is the entire module (that is, the DLL) called by the process specified by the Th32processid parameter.
After the call to the CreateToolhelp32Snapshot function has taken a snapshot of the specified object. We will be able to use Process32First, Process32Next, Module32first, Module32next and other functions to "take the chip" work, is to traverse all the processes that have just been photographed, process calls all modules.
Our Demo program provides a complete code implementation:
BOOL ctoolhelpspy::buildprocesslist (void) {//Take a snapshot of all processes in the system HANDLE Hprocesssnap = CreateToolhelp32Snapshot (th32cs_snap PROCESS, 0); if (Hprocesssnap = = Invalid_handle_value) {return FALSE; } PROCESSENTRY32 pe32 = {0}; pe32.dwsize = sizeof (PROCESSENTRY32); Traverse the entire process of the shot down if (Process32First (Hprocesssnap, &pe32)) {do {if (Pe32.th32processi D && strcmp (pe32.szexefile, "System") {//Save process name, PID Cprocessitem Pro Cessitem; Processitem.setprocessname (Pe32.szexefile); Processitem.setprocessid (PE32.TH32PROCESSID); Add list Save Mproclist.addtail (Processitem); }} while (Process32Next (Hprocesssnap, &pe32)); } closehandle (HPROCESSSNAP); return TRUE;} BOOL ctoolhelpspy::buildmodulelist (cprocessitem& inProcess) {//Take a snapshot of all modules called by the specified process HANDLE Hmodulesnap = Createtool Help32snapShot (Th32cs_snapmodule, Inprocess.getprocessid ()); if (Hmodulesnap = = Invalid_handle_value) {return FALSE; } MODULEENTRY32 me32 = {0}; me32.dwsize = sizeof (MODULEENTRY32); Inprocess.cleanupmodulelist (); Traverse all Modules if (Module32first (Hmodulesnap, &me32)) {do {//Save module file full path InProc Ess. Addmoduleitem (Me32.szexepath); } while (Module32next (Hmodulesnap, &me32)); } closehandle (HMODULESNAP); return TRUE;}
Note: The tool library function is implemented in Kernel32.dll.
program development, we need to include the header file Tlhelp32.h, connection library file Kernel32.lib.
Note: We use our own definition of class Cprocessitem to describe a process, which preserves the process name, PID and other information. It also maintains a list of all modules that the process invokes. Accordingly, we also use a self-defined class Cmoduleitem to describe the narrative module, which holds the full path of the module file, the version number, file size, description information, product name and so on. Same below
Method Two: Use the PSAPI (Process Status API) function
This is a method under Windows nt/2000. The core is to use the EnumProcesses function. Its prototypes are for example the following:
BOOL EnumProcesses (
DWORD *lpidprocess,//array of PID used to hold all processes
DWORD CB,//size of the above array
DWORD *cbneeded//PID Array (valid) bytes actually returned
);
Once the PID of all the processes in the system is obtained, we are able to use the OpenProcess function to open the specified process. Call GetModuleBaseName again to get the name of the process. Call EnumProcessModules to enumerate all modules called by the process and call Getmodulefilenameex to get the full path of the module file.
Our demo program provides a complete code implementation:
BOOL cpsapispy::buildprocesslist (void) {//enumerates PID DWORD processes[1024] for all processes in the system, needed; if (! EnumProcesses (processes, sizeof (processes), &needed)) {return FALSE; } char Szname[max_path] = ""; DWORD Actualprocesscount = Needed/sizeof (DWORD); for (DWORD i = 0; i < Actualprocesscount; i++) {//save process PID Cprocessitem Processitem; Processitem.setprocessid (Processes[i]); Open the current process to get the process action handle HANDLE hprocess = openprocess (process_query_information | Process_vm_read, FALSE, Processes[i]); if (hprocess) {hmodule hmodule; DWORD needed; Enumerates all modules that are called by the current process if (EnumProcessModules (hprocess, &hmodule, sizeof (hmodule), &needed)) { Get and save the process name GetModuleBaseName (hprocess, hmodule, SzName, sizeof (szName)); Processitem.setprocessname (SzName); Mproclist.addtail (processitEM); } closehandle (hprocess); }} return TRUE; BOOL cpsapispy::buildmodulelist (cprocessitem& inProcess) {//based on PID to open the process. Get a process action handle HANDLE hprocess = openprocess (process_query_information | Process_vm_read, FALSE, Inprocess.getprocessid ()); if (hprocess) {hmodule modules[1024]; DWORD needed; Enumerates all modules that are called by the current process if (EnumProcessModules (hprocess, modules, sizeof (modules), &needed)) {Char Szname[max_path] = ""; Inprocess.cleanupmodulelist (); DWORD Actualmodulecount = Needed/sizeof (DWORD); Get full path for each module file for (DWORD i = 1; i < Actualmodulecount; i++) {Getmodulefilenamee X (hprocess, Modules[i], szName, sizeof (szName)); Inprocess.addmoduleitem (SzName); }} CloseHandle (hprocess); } return TRUE;
Note: The PSAPI function is implemented in Psapi.dll.
Development of the program. We need to include header file Psapi.h, connection library file Psapi.lib. These files are available after installing Microsoft's Platform SDK.
Method Three: Performance data collected using the system (performance)
This is also a method under Windows nt/2000.
First, we need to introduce some background knowledge about performance monitoring (performance monitoring).
So-called performance monitoring. is actually a system feature provided by Windows nt/2000. It can be in real-time collection, analysis of the system's applications, services, drivers and other performance data, in order to analyze the bottleneck of the system, monitoring the performance of components, and finally help users to the rational deployment of the system. Over here. There is also the concept of introducing a performance object (performance objects). That is, the person being monitored.
performance objects in a generic system include processors (Processor), processes (process), threads (thread), network traffic (such as TCP, UDP, ICMP, IP, and so on), system services (such as ACS/RSVP service), and so on. (We are concerned about the process in this article.) An object named "Process". Below, we give the structure reference diagram of the system performance data:
Figure 2 Structure of system performance data
There are two types of performance objects: one that supports only one instance. There is also a support for multiple instances. (The process objects we care about support multiple instances, and each instance corresponds to a process in the system.) An object can have multiple performance metrics, and each performance metric is recorded with a single counter (Counter). As far as the process object is concerned. The type of counter it owns includes the ID process (PID of the process), thread count (number of threads), priority Base (process precedence), Io read bytes/sec (bytes per second IO read), io Writer bytes/ SEC (Io writes bytes per second) and so on.
(In this article we only care about the value of the ID process counter.)
)
Support for single Instance object data structures such as the following (that is, the expanded form of individual object blocks in Figure 2):
Figure 3 Object data structure support for single instance
Object data structures that support multiple instances, such as the following (the definition portion of each instance is added):
Figure 4 Object data structures that support multiple instances
know the performance data structure, then how do we read them? The most important method is to use the Register table functions, such as RegOpenKeyEx, RegQueryValueEx, RegCloseKey, and so on. It is worth noting that although the Register function is used here, performance data is not stored in the registry database; The function RegOpenKeyEx is called when reading performance data. The primary key value should be hkey_performance_data. When the performance data is obtained, the offsets are calculated based on the definition of the data structures, and we are able to get the information we are interested in.
Our Demo program provides a complete code implementation:
#define Initial_size 51200#define extend_size 25600#define regkey_perf _t ("Software\\microsoft\\win dows nt\\currentversion\\perflib ") #define Regsubkey_counters _t (" COUNTERS ") #define Process_counter _t (" PROCESS ") # Define Processid_counter _t ("ID process") BOOL cperformancespy::buildprocesslist (void) {//Step one: Get all the objects in the system from a specific register path, count The name of the LANGID lid = Makelangid (lang_english, sublang_neutral); TCHAR szsubkey[1024]; _stprintf (Szsubkey, _t ("%s\\%03x"), regkey_perf, lid); HKEY Hsubkey; DWORD RT = RegOpenKeyEx (HKEY_LOCAL_MACHINE, Szsubkey, 0, Key_read, &hsubkey); if (rt! = ERROR_SUCCESS) {return FALSE; } DWORD dwtype = 0; DWORD dwsize = 0; Lpbyte buffer = NULL; BOOL pass = FALSE; Get buffer size to load all counter names RT = RegQueryValueEx (Hsubkey, regsubkey_counters, NULL, &dwtype, NULL, &dwsize); if (RT = = ERROR_SUCCESS) {buffer = (LPBYTE) malloc (dwsize); memset (buffer, 0, dwsize); RT = RegQueryValueEx (Hsubkey, Regsubkey_counters, NULL, &dwtype, buffer, &dwsize); } LPSTR p, p2; DWORD Dwprocessidtitle; DWORD Dwprocessidcounter; Pperf_data_block pperf; Pperf_object_type POBJ; Pperf_instance_definition Pinst; Pperf_counter_block Pcounter; Pperf_counter_definition pcounterdef; if (RT = = ERROR_SUCCESS) {pass = TRUE; Step two: Find the object named "Process" and the counter named "ID Process"//get their index value (because the object, counter in performance data is identified by index) P = (LPSTR) buffer; while (*p) {if (P > (LPSTR) buffer) {for (P2 = p-2; _istdigit (*P2); p 2--); } if (_TCSICMP (p, process_counter) = = 0) {//Gets the index of the "PROCESS" Object for (P2 = P-2; _istdigit (*P2); p2--); _tcscpy (Szsubkey, p2+1); } else if (STRICMP (p, PRocessid_counter) = = 0) {//Get index of "ID Process" counter for (P2 = p-2; _istdigit (*P2) ; p2--); Dwprocessidtitle = Atol (p2 + 1); }//point to the next string p + = (_tcslen (p) + 1); }//Step three: Get the full performance data of the process object free (buffer); buffer = NULL; dwsize = initial_size; Buffer = (LPBYTE) malloc (dwsize); memset (buffer, 0, dwsize); while (pass) {RT = RegQueryValueEx (Hkey_performance_data, Szsubkey, NULL, &dwtype, b Uffer, &dwsize); Pperf = (pperf_data_block) buffer; The performance data block begins with a four character "PERF" ID if (RT = = ERROR_SUCCESS) && (dwsize > 0) && PPERF-&G T Signature[0] = = (WCHAR) ' P ' && pperf->signature[1] = = (WCHAR) ' E ' && pperf- >SIGNATURE[2] = = (WCHAR) ' R ' && pperf->signaturE[3] = = (WCHAR) ' F ') {break; }//Assume that the buffer is not large enough to enlarge the buffer and then try if (rt = = Error_more_data) {dwsize + = extend_size; Buffer = (LPBYTE) realloc (buffer, dwsize); memset (buffer, 0, dwsize); } else {pass = FALSE; }}} if (pass) {POBJ = (Pperf_object_type) ((DWORD) pperf + pperf->headerlength); Step four: Find the "ID process" counter in the Counter definition section of the process object data Pcounterdef = (pperf_counter_definition) (DWORD) POBJ + Pobj->headerlen GTH); for (DWORD i = 0; I < (DWORD) pobj->numcounters; i++) {if (Pcounterdef->counternametitleindex = = Dwprocessidtitle) {Dwprocessidcounter = pcounterdef->counteroffset; Break } pcounterdef++; }//Step five: Traverse all instances. Gets the name of the instance (that is, the process name) and the PID TCHAR szProcessName[MAX_PATH]; Pinst = (pperf_instance_definition) ((DWORD) POBJ + pobj->definitionlength); for (i = 0; I < (DWORD) pobj->numinstances; i++) {//get process name P = (LPSTR) ((DWORD) Pinst + Pinst->nameoffset); RT = WideCharToMultiByte (CP_ACP, 0, (LPCWSTR) p,-1, szProcessName, sizeof (szprocessname), NULL, NULL); Get process pid Pcounter = (Pperf_counter_block) ((DWORD) Pinst + pinst->bytelength); DWORD processId = * ((Lpdword) ((DWORD) Pcounter + dwprocessidcounter)); if (strcmp (szProcessName, "System") && processId) {Cprocessitem processitem; Processitem.setprocessid (PROCESSID); Processitem.setprocessname (szProcessName); Mproclist.addtail (Processitem); }//point to the next process Pinst = (pperf_instance_definition) ((DWORD) Pcounter + pcounter-> ByteLength); }} if (buffer) {free (buffer); buffer = NULL; } regclosekey (Hsubkey); RegCloseKey (Hkey_performance_data); return pass;}
Note: Method Three is only used to register the table operation function. And these functions are implemented in Advapi32.dll.
In program development, we need to include header file winperf.h. In addition, the modules that are called by each process in the method are still used by the PSAPI function of the two, and are no longer listed here.
Method Four: Use the PDH (performance Data Helper) function
The underlying implementation of such a method is the same as the method three. But we see that method three is very cumbersome to implement.
To simplify the application. The PDH function provides a layer of encapsulation of the implementation of method three. Our process enumeration here, mainly using the Pdhenumobjectitems function. Its function prototypes are for example the following:
Pdh_status Pdhenumobjectitems (
LPCTSTR Szdatasource,//Data source
LPCTSTR Szmachinename,//Machine name
LPCTSTR szObjectName,//object name
LPTSTR mszcounterlist,//Counter list
Lpdword pcchcounterlistlength,//Counter list length
LPTSTR mszinstancelist,//Instance list
Lpdword Pcchinstancelistlength,//Instance list length
DWORD Dwdetaillevel,//level of information obtained
DWORD dwFlags//reserved for 0
);
For each given process instance, we also get its PID, which is the value of the "ID process" counter. At this time, we will use other PDH functions, including: Pdhopenquery, Pdhaddcounter, Pdhcollectquerydata, Pdhgetformattedcountervalue, Pdhclosequery and so on.
Our demo program provides a complete code implementation:
BOOL cpdhspy::buildprocesslist (void) {LPTSTR szcounterlistbuffer = NULL; DWORD dwcounterlistsize = 0; LPTSTR szinstancelistbuffer = NULL; DWORD dwinstancelistsize = 0; BOOL pass = FALSE; Call Pdhenumobjectitems for the first time to get the required list length pdh_status pdhstatus = Pdhenumobjectitems (null, NULL, TEXT ("Process"), SZC Ounterlistbuffer, &dwcounterlistsize, Szinstancelistbuffer, &dwinstancelistsize, PERF_DETAIL_WIZARD, 0); if (Pdhstatus = = ERROR_SUCCESS) {Szcounterlistbuffer = (LPTSTR) malloc (dwcounterlistsize * sizeof (TCHAR) )); Szinstancelistbuffer = (LPTSTR) malloc ((dwinstancelistsize * sizeof (TCHAR))); Second call to PdhenumobjectitemsGet all counters and instances of the "process" Object pdhstatus = Pdhenumobjectitems (null, NULL, TEXT ("process"), Szcounterlistbuffe R, &dwcounterlistsize, Szinstancelistbuffer, &dwinstancelistsize, Perf_detail_wizard, 0); if (Pdhstatus = = ERROR_SUCCESS) {pass = TRUE; LPTSTR pinst = Szinstancelistbuffer; Get each instance name, which is the process name for (; *pinst! = 0; Pinst + = Lstrlen (pinst) + 1) {if (strcmp (Pinst, "System") && strcmp (Pinst, "Idle") &am p;& strcmp (Pinst, "_total")) {Cprocessitem processitem; PID Processitem.setprocessid (Getpidcountervalue (Pinst)) to obtain the process; Processitem.setprocessname (Pinst); Mproclist.addtail (Processitem); }}}} if (Szcounterlistbuffer! = NULL) {free (szcounterlistbuffer); SzCounterlistbuffer = NULL; } if (Szinstancelistbuffer! = NULL) {free (szinstancelistbuffer); Szinstancelistbuffer = NULL; } return pass;} DWORD Cpdhspy::getpidcountervalue (LPTSTR ininstancename) {//Open a Query object hquery hquery = NULL; Pdh_status pdhstatus = pdhopenquery (0, 0, &hquery); Hcounter hcounter = NULL; Char Szpathbuffer[max_path]; sprintf (Szpathbuffer, "\\Process (%s) \\ID Process", ininstancename); Pdhstatus = Pdhaddcounter (hquery, Szpathbuffer, 0, &hcounter); Pdhstatus = Pdhcollectquerydata (hquery); Gets the value of the "ID Process" counter of the current instance DWORD Ctrtype; Pdh_fmt_countervalue Fmtvalue; Pdhstatus = Pdhgetformattedcountervalue (Hcounter, Pdh_fmt_long, &ctrtype, &fmtvalue); Close Query Object pdhstatus = Pdhclosequery (hquery); return fmtvalue.longvalue;}
Note: The PDH function is implemented in Pdh.dll.
program development, we need to include the header file Pdh.h, connection library file Pdh.lib.
Demo Program Description
Our demo program was developed using VC6.0 and is a dialog-based MFC program. The program design adheres to the OOP style. As well as the user interface (Interface) separate from the business logic, the structure is simple and clear, I believe that we are very easy to read code.
Since this article introduces a total of four process enumeration methods, we have designed a logical control class inheritance structure such as the following:
Figure 5 Demonstrating the program logic control class structure
Other than that. The demo program uses a deferred enumeration (Lazy enumerating) policy for the module that the process calls, that is, when the program starts, it does not enumerate all the modules that are called by the process. And only when it is needed.
This can significantly save time when the program starts.
Written in the last
Process shadowing (which is relative to process enumeration) has always been a very hot topic, with a lot of ideas. One of them is the call to intercept the system API function enumprocesses. After reading through this article, do you think this idea is feasible? Or you have other new ideas. These are the author's original intention of writing this article. (Note: Written in 2004.) Published in "CSDN Development master".
)
Screws Process Enumeration