//=======================================// PSAPIDEM - Matt Pietrek 1996// Microsoft Systems Journal, August 1996// FILE: PSAPIDEM.CPP//=======================================#include <windows.h>#include <commctrl.h>#pragma hdrstop#include "psapi.h"#include "psapidem.h"// Helper function prototypesvoid Handle_WM_COMMAND(HWND hDlg, WPARAM wParam, LPARAM lParam);void Handle_WM_INITDIALOG(HWND hDlg);void Handle_WM_CLOSE(HWND hDlg);BOOL CALLBACK PSApiDemDlgProc(HWND, UINT, WPARAM, LPARAM);HTREEITEM TVAppendString(HWND hWnd, HTREEITEM hTi, PSTR psz);void GetSetPositionInfoFromRegistry(BOOL fSave, POINT *lppt);// ======================= String literals ===============================char gszRegistryKey[] = "Software//WheatyProductions//PSAPIDEM";char g_AboutMsgTxt[] = "PSApiDem shows processes and device driver information obtained " "with PSAPI.DLL, from the Win32 SDK";char g_AboutTitleTxt[] = "PSApiDem - Matt Pietrek 1996, for MSJ";// ======================= Start of code ===============================void AddProcessDetails(HWND hWnd, HTREEITEM hTreeItem, HANDLE hProcess){ // // Fills in the details for one specific process // HMODULE hMods[1024]; DWORD cbNeeded; // // Get a list of all the HMODULEs in this process. Add a "Module" // subnode, and add the full path for each HMODULE to it. // if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { HTREEITEM hTiModules = TVAppendString(hWnd, hTreeItem, "Modules"); for (unsigned i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { char szModName[MAX_PATH]; char szItem[MAX_PATH + 64]; // Get the full path to the module's file if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName))) { wsprintf(szItem, "%s (0x%08X)", szModName, hMods[i]); TVAppendString(hWnd, hTiModules, szItem); } } } // // Next, use PSAPI.DLL to collect information about the memory usage // of the process. Add a "Memory" node to the process, and add // subnodes for each field in the PROCESS_MEMORY_COUNTERS structure. // PROCESS_MEMORY_COUNTERS pmc; if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { char szItem[128]; HTREEITEM hTiMem = TVAppendString(hWnd, hTreeItem, "Memory"); wsprintf(szItem, "%s 0x%08X", "PageFaultCount", pmc.PageFaultCount); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "PeakWorkingSetSize", pmc.PeakWorkingSetSize); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "WorkingSetSize", pmc.WorkingSetSize); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "QuotaPeakPagedPoolUsage", pmc.QuotaPeakPagedPoolUsage); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "QuotaPagedPoolUsage", pmc.QuotaPagedPoolUsage); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "QuotaPeakNonPagedPoolUsage", pmc.QuotaPeakNonPagedPoolUsage); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "QuotaNonPagedPoolUsage", pmc.QuotaNonPagedPoolUsage); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "PagefileUsage", pmc.PagefileUsage); TVAppendString(hWnd, hTiMem, szItem); wsprintf(szItem, "%s 0x%08X", "PeakPagefileUsage", pmc.PeakPagefileUsage); TVAppendString(hWnd, hTiMem, szItem); } // // Finally, use the KERNEL32 GetProcessTimes function to see how much // time the process has spent in user and kernel modes. Add this // information (in milliseconds) to the process node // FILETIME ftCreate, ftExit, ftKernel, ftUser; if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) ; { // Horrible, disgusting hack! The two lines below basically grab the // contents of a FILETIME structure and store it in a 64 bit integer. LONGLONG tUser64 = *(LONGLONG*) &ftUser; LONGLONG tKernel64 = *(LONGLONG*) &ftKernel; DWORD tUser, tKernel; // The LONGLONGs contain the time in 100 nanosecond intervals (now // there's a useful unit of measurement...). Divide each of them by // 10000 to convert into milliseconds, and store the results in a // DWORD. This means that the max time before overflowing is around // 4 Million seconds (about 49 days) tUser = (DWORD)(tUser64 / 10000); tKernel = (DWORD)(tKernel64 / 10000); // Format the user and kernel times, and add to the process node char szItem[128]; wsprintf(szItem, "User mode: %u ms", tUser); TVAppendString(hWnd, hTreeItem, szItem); wsprintf(szItem, "Kernel mode: %u ms", tKernel); TVAppendString(hWnd, hTreeItem, szItem); }}void AddProcessToList(HWND hWnd, DWORD processID){ // // Adds the process name and ID to the treeview control, then calls // AddProcessDetails to fill in the details about the process // char szProcessName[MAX_PATH] = "unknown"; char szItemString[MAX_PATH + 64]; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); if (hProcess) { HMODULE hMod; DWORD cbNeeded; if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { GetModuleBaseName(hProcess, hMod, szProcessName, sizeof(szProcessName)); } } wsprintf(szItemString, "%s (ID:%u)", szProcessName, processID); HTREEITEM hTiProcess = TVAppendString(hWnd, TVI_ROOT, szItemString); if (hProcess && hTiProcess) AddProcessDetails(hWnd, hTiProcess, hProcess); CloseHandle(hProcess);}void UpdateProcessList(HWND hDlg){ // // Clears the treeview, obtains a list of process IDs, and shows them // // Get the treeview's HWND, then clear it HWND hWnd = GetDlgItem(hDlg, IDC_TREE_PROCESS); TreeView_DeleteAllItems(hWnd); // Get the list of process IDs DWORD aProcesses[1024], cbNeeded; if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) return ; // Calculate how many process IDs were returned DWORD cProcesses = cbNeeded / sizeof(DWORD); // Spit out the information for each ID for (unsigned i = 0; i < cProcesses; i++) AddProcessToList(hWnd, aProcesses[i]);}void UpdateDriverList(HWND hDlg){ // // Clears the treeview, obtains a list of drivers, and shows them // // Get the treeview's HWND, then clear it HWND hWnd = GetDlgItem(hDlg, IDC_TREE_DRIVERS); TreeView_DeleteAllItems(hWnd); // Get the list of device driver base addresses PVOID aDrivers[1024]; DWORD cbNeeded; if (!EnumDeviceDrivers(aDrivers, sizeof(aDrivers), &cbNeeded)) return ; // Calculate how many drivers were returned DWORD cDrivers = cbNeeded / sizeof(aDrivers[0]); // Spit out the information for each driver for (unsigned i = 0; i < cDrivers; i++) { char szBaseName[MAX_PATH] = ""; char szDriverFileName[MAX_PATH] = ""; // Get the driver's base name if (GetDeviceDriverBaseName(aDrivers[i], szBaseName, sizeof(szBaseName))) { char szItem[MAX_PATH + 64]; wsprintf(szItem, "%s (0x%08X)", szBaseName, aDrivers[i]); HTREEITEM hTiDriver = TVAppendString(hWnd, TVI_ROOT, szItem); // Get the full path to the driver GetDeviceDriverFileName(aDrivers[i], szDriverFileName, sizeof (szDriverFileName)); TVAppendString(hWnd, hTiDriver, szDriverFileName); } }}void RefreshViews(HWND hDlg){ UpdateProcessList(hDlg); // Should be self-explanatory! UpdateDriverList(hDlg);}// ======================= Start of UI code ===============================int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, PSTR lpszCmdLine, int nCmdShow){ // Gotta do this for those lovely tree view controls InitCommonControls(); // Bring up the user interface (A dialog box? What a surprise!) DialogBox(hInstance, MAKEINTRESOURCE(IDD_PSAPIDEM_DLG), 0, (DLGPROC) PSApiDemDlgProc); return 0;}BOOL CALLBACK PSApiDemDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){ // // The dialog procedure for the main window // switch (msg) { case WM_COMMAND: Handle_WM_COMMAND(hDlg, wParam, lParam); return TRUE; case WM_INITDIALOG: Handle_WM_INITDIALOG(hDlg); return TRUE; case WM_CLOSE: Handle_WM_CLOSE(hDlg); break; // let everything else fall through } return FALSE;}void Handle_WM_COMMAND(HWND hDlg, WPARAM wParam, LPARAM lParam){ switch (LOWORD(wParam)) { case IDC_BUTTON_REFRESH: RefreshViews(hDlg); break; case IDC_BUTTON_EXIT: Handle_WM_CLOSE(hDlg); break; case IDC_BUTTON_ABOUT: MessageBox(hDlg, g_AboutMsgTxt, g_AboutTitleTxt, MB_OK); break; }}void Handle_WM_INITDIALOG(HWND hDlg){ // Get the window coordinates where the program was last running // and move the window to that spot. POINT pt; GetSetPositionInfoFromRegistry(FALSE, &pt); SetWindowPos(hDlg, 0, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOACTIVATE); RefreshViews(hDlg); // Fill tree views with initial values}void Handle_WM_CLOSE(HWND hDlg){ // Save off the window's X,Y coordinates for next time RECT rect; GetWindowRect(hDlg, &rect); GetSetPositionInfoFromRegistry(TRUE, (LPPOINT) &rect); EndDialog(hDlg, 0);}HTREEITEM TVAppendString(HWND hWnd, HTREEITEM hTi, PSTR psz){ // // A helper function that appends a string to the end of the list // of subnodes under a specified node (hTi). // TV_INSERTSTRUCT tvis; memset(&tvis, 0, sizeof(tvis)); tvis.hParent = hTi; tvis.hInsertAfter = TVI_LAST; tvis.item.mask = TVIF_TEXT; tvis.item.pszText = psz; return TreeView_InsertItem(hWnd, &tvis);}void GetSetPositionInfoFromRegistry(BOOL fSave, POINT *lppt){ // // Function that saves or restores the coordinates of a dialog box // in the system registry. Handles the case where there's nothing there. // HKEY hKey; DWORD dataSize, err, disposition; char szKeyName[] = "DlgCoordinates"; if (!fSave) // In case the key's not there yet, we'll lppt->x = lppt->y = 0; // return 0,0 for the coordinates // Open the registry key (or create it if the first time being used) err = RegCreateKeyEx(HKEY_CURRENT_USER, gszRegistryKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, &disposition); if (ERROR_SUCCESS != err) return ; if (fSave) // Save out coordinates { RegSetValueEx(hKey, szKeyName, 0, REG_BINARY, (PBYTE)lppt, sizeof(*lppt)); } else // read in coordinates { dataSize = sizeof(*lppt); RegQueryValueEx(hKey, szKeyName, 0, 0, (PBYTE)lppt, &dataSize); }}