|
|
PROG |
| 22 Haz 2001 | ENUMERATE APPLICATIONS in Win32 | VC++ Articels |
|
SUMMARY
One common programming problem in
Win32 is the need to enumerate all "applications". The Task Manager under
Windows NT 4.0 is a perfect example of this. It lists "applications" in two
ways. The first tab of the Task Manager lists all "application windows" on the
desktop. The second tab of the Task Manager lists all the "processes" in the
system. This article provides details on how to perform both these tasks in Windows 95 and
Windows NT. Enumerating
Top-Level Windows
If you compare enumerating
processes and enumerating top-level windows on the desktop, enumerating top-level windows
is probably easier. To enumerate top-level windows under both Windows NT and Windows 95
platforms, use the EnumWindows() function. Do not use GetWindow() to create your list of
windows, as it can be confused by z-order changes and lost windows. Enumerating
Processes
Creating a list of processes in
the system is a little more complex than enumerating windows. This is primarily due to the
fact that the API functions for doing this are completely different under Windows 95 and
Windows NT. Under Windows 95, you must use functions from the ToolHelp32 group of APIs.
Under Windows NT, you use functions from PSAPI.DLL, which is available in the Platform
SDK. This article will discuss both of these techniques and also provide a sample wrapper
function called "EnumProcs()" that works under Windows NT and Windows 95. CreateToolhelp32Snapshot(),
Process32First(), and Process32Next(). The first step in using the
ToolHelp32 functions is to create a "snapshot" of the information in the system.
You do this using the CreateToolhelp32Snapshot() function. This function allows you to
choose what type of information is stored in the snapshot. Make sure you include the
TH32CS_SNAPPROCESS flag if you are interested in process information. This function
returns a HANDLE, and it is important to remember to pass the handle to CloseHandle()
after you are finished with it. EnumProcesses(),
EnumProcessModules(), GetModuleFileNameEx(), GetModuleBaseName() EnumProcesses() is the first
step in creating a list of processes in the system. Following is its declaration: BOOL EnumProcesses( DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded );
EnumProcesses() takes a pointer
to a DWORD array (lpidProcess), the size of the array (cb), and a pointer to a DWORD to
return the length of the data returned(cbNeeded). The DWORD array is populated with an
array of process ID's for the processes in the system. The pointer to the DWORD parameter
(cbNeeded) returns the size of the array that is used. The following calculation tells you
how many process IDs were returned: nReturned = cbNeeded / sizeof(DWORD). One important
note should be made. Although the documentation names the returned DWORD
"cbNeeded", there is actually no way to find out how big the passed in array
must be. EnumProcesses() will never return a value in cbNeeded that is larger than the
size of array value that you passed in the cb parameter. As a result the only way to
assure success with the EnumProcesses() function is to allocate a DWORD array, if cbNeeded
== cb upon return, allocate a larger array and try again until cbNeeded is smaller than
cb. EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbReturned );
This will put the handle of the
first module of the process in the hModule variable. Remember that a process doesn't
really have a name, but that the first module in the process is going to be the executable
of the process. Now you can use the hModule with the GetModuleFileNameEx(),
GetModuleBaseName() to get the full path name, or the simple module name for the process
executable. Both functions take the handle to the process, handle to the module, and a
buffer pointer in which to return the name, followed by the size of the buffer. INT WINAPI VDMEnumTaskWOWEx( DWORD dwProcessId, TASKENUMPROCEX fp,
LPARAM lparam );
where dwProcessId is the id of
the NTVDM process whose 16-bit tasks you want to enumerate. The fp parameter is a pointer
to a callback "enum" function. And the lparam parameter is a user-defined lparam
to be passed to your enum function. BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ
pszModName, PSZ pszFileName, LPARAM lpUserDefined );
This function will be called
once per 16-bit task running under the NTVDM process passed into VDMEnumTaskWOWEx(). You
should return FALSE if you wish to continue enumerating and TRUE if you wish to end
enumeration. NOTE: This is the opposite of EnumWindows(). Sample Code
The following sample code
encapsulates the PSAPI.DLL and ToolHelp32 functions into one function called
"EnumProcs()". This function works similar to EnumWindows() in that it takes a
pointer to a function and calls it repeatedly, once for each process in the system.
Following is the declaration for the function: BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam );
If you use this function, the
callback function should be declared as follows: BOOL CALLBACK Proc( DWORD dw, WORD w16, LPCSTR lpstr, LPARAM lParam );
The dw parameter will contain
the ID, the w16 is the 16-bit task number or 0 if 32-bit process (always zero under
Windows 95), the lpstr parameter will point to the filename, and the lParam is the
user-defined lParam passed into EnumProcs(). /*********************
EnumProc.h
*********************/
#include <windows.h>
typedef BOOL (CALLBACK *PROCENUMPROC)( DWORD, WORD, LPSTR,
LPARAM ) ;
BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam ) ;
/*********************
EnumProc.c (or .cpp)
*********************/
#include "EnumProc.h"
#include <tlhelp32.h>
#include <vdmdbg.h>
typedef struct
{
DWORD dwPID ;
PROCENUMPROC lpProc ;
DWORD lParam ;
BOOL bEnd ;
} EnumInfoStruct ;
BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16,
PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined ) ;
// The EnumProcs function takes a pointer to a callback function
// that will be called once per process in the system providing
// process EXE filename and process ID.
// Callback function definition:
// BOOL CALLBACK Proc( DWORD dw, LPCSTR lpstr, LPARAM lParam ) ;
//
// lpProc -- Address of callback routine.
//
// lParam -- A user-defined LPARAM value to be passed to
// the callback routine.
BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam )
{
OSVERSIONINFO osver ;
HINSTANCE hInstLib ;
HINSTANCE hInstLib2 ;
HANDLE hSnapShot ;
PROCESSENTRY32 procentry ;
BOOL bFlag ;
LPDWORD lpdwPIDs ;
DWORD dwSize, dwSize2, dwIndex ;
HMODULE hMod ;
HANDLE hProcess ;
char szFileName[ MAX_PATH ] ;
EnumInfoStruct sInfo ;
// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ;
BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ;
BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ;
// PSAPI Function Pointers.
BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * );
BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *,
DWORD, LPDWORD );
DWORD (WINAPI *lpfGetModuleFileNameEx)( HANDLE, HMODULE,
LPTSTR, DWORD );
// VDMDBG Function Pointers.
INT (WINAPI *lpfVDMEnumTaskWOWEx)( DWORD,
TASKENUMPROCEX fp, LPARAM );
// Check to see if were running under Windows95 or
// Windows NT.
osver.dwOSVersionInfoSize = sizeof( osver ) ;
if( !GetVersionEx( &osver ) )
{
return FALSE ;
}
// If Windows NT:
if( osver.dwPlatformId == VER_PLATFORM_WIN32_NT )
{
// Load library and get the procedures explicitly. We do
// this so that we don't have to worry about modules using
// this code failing to load under Windows 95, because
// it can't resolve references to the PSAPI.DLL.
hInstLib = LoadLibraryA( "PSAPI.DLL" ) ;
if( hInstLib == NULL )
return FALSE ;
hInstLib2 = LoadLibraryA( "VDMDBG.DLL" ) ;
if( hInstLib2 == NULL )
return FALSE ;
// Get procedure addresses.
lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*))
GetProcAddress( hInstLib, "EnumProcesses" ) ;
lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress( hInstLib,
"EnumProcessModules" ) ;
lpfGetModuleFileNameEx =(DWORD (WINAPI *)(HANDLE, HMODULE,
LPTSTR, DWORD )) GetProcAddress( hInstLib,
"GetModuleFileNameExA" ) ;
lpfVDMEnumTaskWOWEx =(INT(WINAPI *)( DWORD, TASKENUMPROCEX,
LPARAM))GetProcAddress( hInstLib2, "VDMEnumTaskWOWEx" );
if( lpfEnumProcesses == NULL ||
lpfEnumProcessModules == NULL ||
lpfGetModuleFileNameEx == NULL ||
lpfVDMEnumTaskWOWEx == NULL)
{
FreeLibrary( hInstLib ) ;
FreeLibrary( hInstLib2 ) ;
return FALSE ;
}
// Call the PSAPI function EnumProcesses to get all of the
// ProcID's currently in the system.
// NOTE: In the documentation, the third parameter of
// EnumProcesses is named cbNeeded, which implies that you
// can call the function once to find out how much space to
// allocate for a buffer and again to fill the buffer.
// This is not the case. The cbNeeded parameter returns
// the number of PIDs returned, so if your buffer size is
// zero cbNeeded returns zero.
// NOTE: The "HeapAlloc" loop here ensures that we
// actually allocate a buffer large enough for all the
// PIDs in the system.
dwSize2 = 256 * sizeof( DWORD ) ;
lpdwPIDs = NULL ;
do
{
if( lpdwPIDs )
{
HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
dwSize2 *= 2 ;
}
lpdwPIDs = HeapAlloc( GetProcessHeap(), 0, dwSize2 );
if( lpdwPIDs == NULL )
{
FreeLibrary( hInstLib ) ;
FreeLibrary( hInstLib2 ) ;
return FALSE ;
}
if( !lpfEnumProcesses( lpdwPIDs, dwSize2, &dwSize ) )
{
HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
FreeLibrary( hInstLib ) ;
FreeLibrary( hInstLib2 ) ;
return FALSE ;
}
}while( dwSize == dwSize2 ) ;
// How many ProcID's did we get?
dwSize /= sizeof( DWORD ) ;
// Loop through each ProcID.
for( dwIndex = 0 ; dwIndex < dwSize ; dwIndex++ )
{
szFileName[0] = 0 ;
// Open the process (if we can... security does not
// permit every process in the system).
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, lpdwPIDs[ dwIndex ] ) ;
if( hProcess != NULL )
{
// Here we call EnumProcessModules to get only the
// first module in the process this is important,
// because this will be the .EXE module for which we
// will retrieve the full path name in a second.
if( lpfEnumProcessModules( hProcess, &hMod,
sizeof( hMod ), &dwSize2 ) )
{
// Get Full pathname:
if( !lpfGetModuleFileNameEx( hProcess, hMod,
szFileName, sizeof( szFileName ) ) )
{
szFileName[0] = 0 ;
}
}
CloseHandle( hProcess ) ;
}
// Regardless of OpenProcess success or failure, we
// still call the enum func with the ProcID.
if(!lpProc( lpdwPIDs[dwIndex], 0, szFileName, lParam))
break ;
// Did we just bump into an NTVDM?
if( _stricmp( szFileName+(strlen(szFileName)-9),
"NTVDM.EXE")==0)
{
// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = lpdwPIDs[dwIndex] ;
sInfo.lpProc = lpProc ;
sInfo.lParam = lParam ;
sInfo.bEnd = FALSE ;
// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx( lpdwPIDs[dwIndex],
(TASKENUMPROCEX) Enum16,
(LPARAM) &sInfo);
// Did our main enum func say quit?
if(sInfo.bEnd)
break ;
}
}
HeapFree( GetProcessHeap(), 0, lpdwPIDs ) ;
FreeLibrary( hInstLib2 ) ;
// If Windows 95:
}else if( osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
{
hInstLib = LoadLibraryA( "Kernel32.DLL" ) ;
if( hInstLib == NULL )
return FALSE ;
// Get procedure addresses.
// We are linking to these functions of Kernel32
// explicitly, because otherwise a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in the Kernel 32.
lpfCreateToolhelp32Snapshot=
(HANDLE(WINAPI *)(DWORD,DWORD))
GetProcAddress( hInstLib,
"CreateToolhelp32Snapshot" ) ;
lpfProcess32First=
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress( hInstLib, "Process32First" ) ;
lpfProcess32Next=
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress( hInstLib, "Process32Next" ) ;
if( lpfProcess32Next == NULL ||
lpfProcess32First == NULL ||
lpfCreateToolhelp32Snapshot == NULL )
{
FreeLibrary( hInstLib ) ;
return FALSE ;
}
// Get a handle to a Toolhelp snapshot of the systems
// processes.
hSnapShot = lpfCreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS, 0 ) ;
if( hSnapShot == INVALID_HANDLE_VALUE )
{
FreeLibrary( hInstLib ) ;
return FALSE ;
}
// Get the first process' information.
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bFlag = lpfProcess32First( hSnapShot, &procentry ) ;
// While there are processes, keep looping.
while( bFlag )
{
// Call the enum func with the filename and ProcID.
if(lpProc( procentry.th32ProcessID, 0,
procentry.szExeFile, lParam ))
{
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bFlag = lpfProcess32Next( hSnapShot, &procentry );
}else
bFlag = FALSE ;
}
}else
return FALSE ;
// Free the library.
FreeLibrary( hInstLib ) ;
return TRUE ;
}
BOOL WINAPI Enum16( DWORD dwThreadId, WORD hMod16, WORD hTask16,
PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined )
{
BOOL bRet ;
EnumInfoStruct *psInfo = (EnumInfoStruct *)lpUserDefined ;
bRet = psInfo->lpProc( psInfo->dwPID, hTask16, pszFileName,
psInfo->lParam ) ;
if(!bRet)
{
psInfo->bEnd = TRUE ;
}
return !bRet;
}
REFERENCES
"Microsoft Systems
Journal," August 1996 Number 8, "Under the Hood," Matt Pietrek Additional query words:
kbDSupport kbdss kbKernBase kbAPI kbDebug kbThread processes list listof find finding
program running |
|
| home | about me | links | projects | clean energy | programming | ||