Link to home
Start Free TrialLog in
Avatar of stitch2802
stitch2802

asked on

Background processes enumeration displays only one process - help

Hello,

This method only displays one process name as follows:
[System]

What I want is to see all the processes displayed along with the user id (i.e., [System] Apache.exe and so on). I've run it through the debugger and tried stepping through the method, but to no avail - I can't figure out what could be causing the bug.

Here's my code:

=================================================================================
//ProcessInfo.h

#pragma once
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <vdmdbg.h>
#include <ios>
#include <iostream>
#include <limits>
#include "Common.h"
#include "IAcquire.h"
#include <vector>


typedef BOOL (CALLBACK *PROCENUMPROC)(DWORD, WORD, LPSTR, LPARAM);
typedef struct {
      DWORD          dwPID;
      PROCENUMPROC   lpProc;
      DWORD          lParam;
      BOOL           bEnd;
      TCHAR               UserName[256];
      TCHAR               Domain[256];
      TCHAR               ProcessName[256];
                    

} ProcessInfoEx;

class ProcessInfo : public IAcquire
{
public:
      bool Acquire();
      ProcessInfoEx &Process(size_t idx);
      size_t ProcessInfo::ProcessCount();
      typedef std::vector<ProcessInfoEx> Processes;

private:
      Processes processes;
      
};
=================================================================================

//ProcessInfo.cpp

#include "ProcessInfo.h"



BOOL WINAPI EnumProcs(PROCENUMPROC lpProc, LPARAM lParam);
BOOL GetCurrentUserAndDomain(LPTSTR szUser, PDWORD pcchUser, LPTSTR szDomain, PDWORD pcchDomain, DWORD dwPID);
BOOL WINAPI Enum16(DWORD dwThreadId, WORD HMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined);
BOOL CALLBACK ProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR szProcess, LPARAM lParam);


bool ProcessInfo::Acquire()
{
      return EnumProcs((PROCENUMPROC)ProcessEnumerator, reinterpret_cast<LPARAM>(&processes)) ? true : false;
      
}

//
// The EnumProcs function takes a pointer to a callback function that
// will be called once per process with the process filename and
// the process id.
// lpProc - address of the callback routine
// lParam - a user-defined LPARAM value to be passed to the callback routine.
// Callback function definition:
// BOOL CALLBACK Proc ( DWORD dw, WORD w, LPCTSTR lpstr, LPARAM lParam);

BOOL WINAPI EnumProcs(PROCENUMPROC lpProc, LPARAM lParam)
{
      OSVERSIONINFO osver;
      HINSTANCE hInstLib = NULL;
      HINSTANCE hInstLib2 = NULL;
      HANDLE hSnapShot = NULL;

      LPDWORD lpdwPIDs = NULL;
      PROCESSENTRY32 procentry;
      BOOL bFlag;
      DWORD dwSize;
      DWORD dwSize2;

      DWORD dwIndex;
      HMODULE hMod;
      HANDLE hProcess;
      char szFileName[MAX_PATH];
    // ProcessInfoEx sProcess;

      //the container for processes       
      ProcessInfo::Processes *process = reinterpret_cast<ProcessInfo::Processes *>(lParam);


      // 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, DWORD * );
      BOOL  ( WINAPI *lpfEnumProcessModules ) (HANDLE, HMODULE *, DWORD, LPDWORD );
      DWORD ( WINAPI *lpfGetModuleBaseName ) (HANDLE, HMODULE, LPTSTR, DWORD );

      //VDMDBG Function Pointers
      INT ( WINAPI *lpfVDMEnumTaskWOWEx) (DWORD, TASKENUMPROCEX, LPARAM);

      //Retrieve the OS version
      osver.dwOSVersionInfoSize = sizeof(osver);
      if(!GetVersionEx(&osver)) return FALSE;

      //if Windows NT 4.0
      if(osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion == 4)
      {
            __try {
                  //Get the procedure address explicitly.
                  //We do this so that we don't have to worry about modules
                  //failing to load under OSes other than Windows NT 4.0
                  //because references to PSAPI.dll can't be resolved.
                  hInstLib = LoadLibraryA("PSAPI.DLL");

                  if(hInstLib == NULL)
                        __leave;

                  hInstLib2 = LoadLibrary("VDMDBG.DLL");
                  if(hInstLib2 == NULL)
                        __leave;

                  //Get procedure addresses.
                  lpfEnumProcesses = (BOOL (WINAPI *) (DWORD *, DWORD, DWORD*))
                        GetProcAddress(hInstLib, "EnumProcesses");

                  lpfEnumProcessModules = (BOOL (WINAPI *) (HANDLE, HMODULE *, DWORD, LPDWORD))
                        GetProcAddress(hInstLib, "EnumProcessModules");

                  lpfGetModuleBaseName = (DWORD (WINAPI *) (HANDLE, HMODULE, LPTSTR, DWORD))
                        GetProcAddress (hInstLib, "GetModuleBaseNameA");

                  lpfVDMEnumTaskWOWEx = (INT (WINAPI*) (DWORD, TASKENUMPROCEX, LPARAM))
                        GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");

                  if(lpfEnumProcesses == NULL
                        || lpfEnumProcessModules == NULL
                        || lpfGetModuleBaseName == NULL
                        || lpfVDMEnumTaskWOWEx == NULL)
                        __leave;

                  //
                  // Call the PSAPI function EnumProcesses to get all of the
                  // ProcIDs currently in the system.

                  // 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 the buffer and again to fill the buffer.
                  // This isn't true: the cbNeeded parameter returns the
                  // number of PIDs returned, so if your buffer size is
                  // zero cbNeeded returns zero.
                  //
                  // The "HeapAlloc" loop here ensures that we actually
                  // allocate a buffer large enough for all the PIDS in the system.

                  dwSize2 = 256 * sizeof(DWORD);
                  do {
                        if(lpdwPIDs){
                              HeapFree(GetProcessHeap(), 0, lpdwPIDs);
                              dwSize *= 2;
                        }

                        lpdwPIDs = (LPDWORD)HeapAlloc(GetProcessHeap(), 0, dwSize2);
                        if (lpdwPIDs == NULL)
                              __leave;

                        if(!lpfEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
                              __leave;

                  }while (dwSize == dwSize2);

                  //How many ProcIds 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 to be opened).
                        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 will be the
                              // EXE module for which we will retrieve the name.
                              if (lpfEnumProcessModules(hProcess, &hMod,
                                    sizeof(hMod), &dwSize2)) {

                                          // Get the module name
                                          if (!lpfGetModuleBaseName(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, "NTVDM.EXE") == 0) {

                              ProcessInfoEx proc;

                              // Fill in some info for the 16-bit enum proc.
                              proc.dwPID = lpdwPIDs[dwIndex];
                              proc.lpProc = lpProc;
                              proc.lParam = (DWORD) lParam;
                              proc.bEnd = FALSE;

                              // Enum the 16-bit stuff.
                              lpfVDMEnumTaskWOWEx(lpdwPIDs[dwIndex],
                                    (TASKENUMPROCEX) Enum16, (LPARAM) &proc);

                              // Did our main enum func say quit?
                              if (proc.bEnd)
                                    break;
                              process->push_back(proc);

                        }
                  }

            } __finally {

                  if (hInstLib)
                        FreeLibrary(hInstLib);

                  if (hInstLib2)
                        FreeLibrary(hInstLib2);

                  if (lpdwPIDs)
                        HeapFree(GetProcessHeap(), 0, lpdwPIDs);
            }

            // If any OS other than Windows NT 4.0.
      } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
            || (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
            && osver.dwMajorVersion > 4)) {

                  __try {

                        hInstLib = LoadLibraryA("Kernel32.DLL");
                        if (hInstLib == NULL)
                              __leave;

                        // If NT-based OS, load VDMDBG.DLL.
                        if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
                              hInstLib2 = LoadLibraryA("VDMDBG.DLL");
                              if (hInstLib2 == NULL)
                                    __leave;
                        }

                        // Get procedure addresses. We are linking to
                        // these functions explicitly, because a module using
                        // this code would fail to load under Windows NT,
                        // which does not have the Toolhelp32
                        // functions in KERNEL32.DLL.
                        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)
                              __leave;

                        if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
                              lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
                                    LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");
                              if (lpfVDMEnumTaskWOWEx == NULL)
                                    __leave;
                        }

                        // Get a handle to a Toolhelp snapshot of all 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) {
                              ProcessInfoEx sProcess;



                              // Call the enum func with the filename and ProcID.
                              if (lpProc(procentry.th32ProcessID, 0,
                                    procentry.szExeFile, reinterpret_cast<LPARAM>(&sProcess))) {

                                          // Did we just bump into an NTVDM?
                                          if (_stricmp(procentry.szExeFile, "NTVDM.EXE") == 0) {

                                                ProcessInfoEx sProcess;
                                                // Fill in some info for the 16-bit enum proc.

                                                sProcess.dwPID = procentry.th32ProcessID;
                                                sProcess.lpProc = lpProc;
                                                sProcess.lParam = (DWORD) lParam;
                                                sProcess.bEnd = FALSE;

                                                // Enum the 16-bit stuff.
                                                lpfVDMEnumTaskWOWEx(procentry.th32ProcessID,
                                                      (TASKENUMPROCEX) Enum16, (LPARAM) &sProcess);

                                                // Did our main enum func say quit?
                                                if (sProcess.bEnd)
                                                      break;
                                                process->push_back(sProcess);
                                          }

                                          procentry.dwSize = sizeof(PROCESSENTRY32);
                                          bFlag = lpfProcess32Next(hSnapShot, &procentry);
                                          process->push_back(sProcess);


                                    } else
                                          bFlag = FALSE;
                        }

                  } __finally {

                        if (hInstLib)
                              FreeLibrary(hInstLib);

                        if (hInstLib2)
                              FreeLibrary(hInstLib2);
                  }

            } 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;

      ProcessInfoEx *psInfo = (ProcessInfoEx *)lpUserDefined;

      bRet = psInfo->lpProc(psInfo->dwPID, hTask16, pszFileName,psInfo->lParam);

      if (!bRet)
            psInfo->bEnd = TRUE;

      return !bRet;
}


BOOL CALLBACK ProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR szProcess, LPARAM lParam)
{

      ProcessInfoEx &process = *(reinterpret_cast<ProcessInfoEx *>(lParam));

      TCHAR userName[256]= {0};
      TCHAR domainName[256]= {0};

      DWORD userLen = sizeof(userName)/sizeof(userName[0]);
      DWORD domainLen = sizeof(domainName)/sizeof(domainName[0]);

      if (wTask == 0){
            if(GetCurrentUserAndDomain(userName, &userLen, domainName, &domainLen, dwPID))
            {
                  HRESULT hr = StringCchCopy(process.UserName, sizeof(process.UserName), userName);

                  if (FAILED(hr))
                  {
                        return false;
                  }

                  hr = StringCchCopy(process.Domain, domainLen, domainName);
                  if (FAILED(hr))
                  {
                        return false;
                  }

                  hr = StringCchCopy(process.ProcessName, sizeof(szProcess), szProcess);
                  if (FAILED(hr))
                  {
                        return false;
                  }

            }
            else
            {
            HRESULT hr = StringCchCopy(process.ProcessName,sizeof(process.ProcessName), szProcess);
                  if (FAILED(hr))
                  {
                        return false;
                  }
            }

      }

      else { //if GetCurrentUserAndDomain == false

            HRESULT hr = StringCchCopy(process.ProcessName, sizeof(szProcess), szProcess);
            if (FAILED(hr))
            {
                  return false;
            }
            //printf("  %5u %s\n", wTask, szProcess);}
      }
      return TRUE;
}


BOOL GetCurrentUserAndDomain(PTSTR szUser, PDWORD pcchUser, PTSTR szDomain, PDWORD pcchDomain, DWORD dwPID)
{

      BOOL         fSuccess = FALSE;
      HANDLE       hToken   = NULL;
      HANDLE       hProcess = NULL;
      PTOKEN_USER  ptiUser  = NULL;
      DWORD        cbti     = 0;
      SID_NAME_USE snu;

      __try {

            // Get the process handle
            hProcess = OpenProcess ( PROCESS_QUERY_INFORMATION, FALSE, dwPID);

            if (!hProcess)
                  __leave;

            if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
                  __leave;


            // Obtain the size of the user information in the token.
            if (GetTokenInformation(hToken, TokenUser, NULL, 0, &cbti)) {

                  // Call should have failed due to zero-length buffer.
                  __leave;

            } else {

                  // Call should have failed due to zero-length buffer.
                  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
                        __leave;
            }

            // Allocate buffer for user information in the token.
            ptiUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, cbti);
            if (!ptiUser)
                  __leave;

            // Retrieve the user information from the token.
            if (!GetTokenInformation(hToken, TokenUser, ptiUser, cbti, &cbti))
                  __leave;

            // Retrieve user name and domain name based on user's SID.
            if (!LookupAccountSid(NULL, ptiUser->User.Sid, szUser, pcchUser,
                  szDomain, pcchDomain, &snu))
                  __leave;

            fSuccess = TRUE;
      }


      __finally {

            // Free resources.
            if (hToken)
                  CloseHandle(hToken);

            if (ptiUser)
                  HeapFree(GetProcessHeap(), 0, ptiUser);
      }

      return fSuccess;
}

size_t ProcessInfo::ProcessCount()
{
      return processes.size();
}


ProcessInfoEx& ProcessInfo::Process( size_t index )
{
      if( index < processes.size() )
      {
            return processes.at(index);
      }
      return processes.back();
}

All suggestions are welcome - please help!

TIA,
S/.
Avatar of jkr
jkr
Flag of Germany image

You seem to have a mismatched if/else pair:

  if (wTask == 0){
         if(GetCurrentUserAndDomain(userName, &userLen, domainName, &domainLen, dwPID))
         {
              HRESULT hr = StringCchCopy(process.UserName, sizeof(process.UserName), userName);

              if (FAILED(hr))
              {
                   return false;
              }

              hr = StringCchCopy(process.Domain, domainLen, domainName);
              if (FAILED(hr))
              {
                   return false;
              }

              hr = StringCchCopy(process.ProcessName, sizeof(szProcess), szProcess);
              if (FAILED(hr))
              {
                   return false;
              }

         }
         else
         {
         HRESULT hr = StringCchCopy(process.ProcessName,sizeof(process.ProcessName), szProcess);
              if (FAILED(hr))
              {
                   return false;
              }
         }

    }

    else { //if GetCurrentUserAndDomain == false <-------- belongs to 'if (wTask == 0)'
Avatar of stitch2802
stitch2802

ASKER

Thanks jkr, but the braces match up (sorry about my misleading comment), so that doesn't seem to be causing the issue.

Here's my resulting XML output:

 <Snapshot_of_Running_Processes>
    <Process>
     <Module>[System Process]</Module>
     <User>¤Þ</User>
     <Domain>Tß</Domain>
    </Process>
    <Process>
     <Module>System</Module>
     <User>¤Þ</User>
     <Domain>Tß</Domain>
    </Process>
   </Snapshot_of_Running_Processes>

The thing is, I know this method should work because I built a separate solution just for the EnumProcs part (from my previous question that you had helped me with, remember?) that  produces the output I want, but I think somehow in the GetCurrentUserAndDomain callback, it never goes past the first process - not sure why.

Comments?
 
Will no one help?
Patience, dude. ;-)

On which platform do you have this problem?
And it's OK that it displays only one line but how many processes do you have at earlier parts of the program? In other words: Have you tried using a debugger and checking the values agains the values you believe there should be? And it gets into a freeze after the first line or quits after the first process?

Btw, in the "lpfEnumProcesses" loop "dwSize2" is never increased so you'll never get out of the loop if you have more than 256 processes.
hehe joghurt ;) point taken.

It's on XP.
>On which platform do you have this problem?

I have 47 processes displaying in my task manager.
>how many processes do you have at earlier parts of the program?

Yes, in the debugger, it displays the following:
[System Process]
System
NT Authority
crss.exe
NT Authority

After which it quits and displays the results.
>Have you tried using a debugger and checking the values agains the values you believe there should be? And it gets into a freeze after the first line or quits after the first process?

Hm, working on this, thanks.
>Btw, in the "lpfEnumProcesses" loop "dwSize2" is never increased so you'll never get out of the loop if you have more than >256 processes.

Thanks for your help,
S/.
oops, sorry about "top-posting"
Btw, unless you have Chinese (or so) user and domain names (that I don't believe), the values
    <User>¤Þ</User>
     <Domain>Tß</Domain>
seem a bit bogus. Such strings may also cause a crash after some loops.

What happens if you comment out the junk and print only the process IDs, then print the process names, too?
joghurt,

After commenting out the getCurrentUserAndDomain function, and simply printing out the process name, it displays all my running processes. (yay, thanks!)

But I'll need the UserName, because I have to separate out the processes by System or User as displayed in Task Manager.

Any advice?
Your version of GetCurrentUserAndDomain seems to be a slight modifitcation of the original function.
What happens if you comment out parts of this function? I'd comment out LookupAccountSidA at the first try.
And would you put a MessageBox for all __leave statements to check where's the error? [Or use your debugger.]

Btw, another bug: In ProcessEnumerator you have two instances of
    StringCchCopy(process.ProcessName, sizeof(szProcess), szProcess);
However, as the second parameter you should supply size of the destination (that is, size of process.ProcessName) instead. Furthermore, because szProcess is a pointer, sizeof(szProcess) is 4.

And the main bug:
The "processes" member is a std::vector. Now you take its address in ProcessInfo::Acquire and cast it to ProcessInfoEx* in ProcessEnumerator. This is a no-no. You must not use the address of a std::vector to manipulate its elements.
Hi joghurt,

I've fixed the bugs that you pointed out - thanks!  However, that did not resolve the issue.

I've tried running it through a debugger, but I'm not getting any useful information. The message boxes that I added displayed the names as shown in the xml file and then the program continued execution.

Essentially, I've been agonizing over this issue ever since - will you please help?

Thanks,
S/.
Can you include the current sources?
Hi joghurt,

Sure, here it is:

//ProcessInfo.cpp

#include "ProcessInfo.h"

//Function definitions
BOOL WINAPI EnumProcs(PROCENUMPROC lpProc, LPARAM lParam);
BOOL GetCurrentUserAndDomain(LPTSTR szUser, PDWORD pcchUser, LPTSTR szDomain, PDWORD pcchDomain, DWORD dwPID);
BOOL WINAPI Enum16(DWORD dwThreadId, WORD HMod16, WORD hTask16, PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined);
BOOL CALLBACK ProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR szProcess, LPARAM lParam);


bool ProcessInfo::Acquire()
{
      return EnumProcs((PROCENUMPROC)ProcessEnumerator, reinterpret_cast<LPARAM>(&processes)) ? true : false;

}

//
// The EnumProcs function takes a pointer to a callback function that
// will be called once per process with the process filename and
// the process id.
// lpProc - address of the callback routine
// lParam - a user-defined LPARAM value to be passed to the callback routine.
// Callback function definition:
// BOOL CALLBACK Proc ( DWORD dw, WORD w, LPCTSTR lpstr, LPARAM lParam);

BOOL WINAPI EnumProcs(PROCENUMPROC lpProc, LPARAM lParam)
{
      OSVERSIONINFO osver;
      HINSTANCE hInstLib = NULL;
      HINSTANCE hInstLib2 = NULL;
      HANDLE hSnapShot = NULL;

      LPDWORD lpdwPIDs = NULL;
      PROCESSENTRY32 procentry;
      BOOL bFlag;
      DWORD dwSize;
      DWORD dwSize2;

      DWORD dwIndex;
      HMODULE hMod;
      HANDLE hProcess;
      char szFileName[MAX_PATH];
      // ProcessInfoEx sProcess;

      //the container for processes      
      ProcessInfo::Processes *process = reinterpret_cast<ProcessInfo::Processes *>(lParam);


      // 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, DWORD * );
      BOOL  ( WINAPI *lpfEnumProcessModules ) (HANDLE, HMODULE *, DWORD, LPDWORD );
      DWORD ( WINAPI *lpfGetModuleBaseName ) (HANDLE, HMODULE, LPTSTR, DWORD );

      //VDMDBG Function Pointers
      INT ( WINAPI *lpfVDMEnumTaskWOWEx) (DWORD, TASKENUMPROCEX, LPARAM);

      //Retrieve the OS version
      osver.dwOSVersionInfoSize = sizeof(osver);
      if(!GetVersionEx(&osver)) return FALSE;

      //if Windows NT 4.0
      if(osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion == 4)
      {
            __try {
                  //Get the procedure address explicitly.
                  //We do this so that we don't have to worry about modules
                  //failing to load under OSes other than Windows NT 4.0
                  //because references to PSAPI.dll can't be resolved.
                  hInstLib = LoadLibraryA("PSAPI.DLL");

                  if(hInstLib == NULL)
                        __leave;

                  hInstLib2 = LoadLibrary("VDMDBG.DLL");
                  if(hInstLib2 == NULL)
                        __leave;

                  //Get procedure addresses.
                  lpfEnumProcesses = (BOOL (WINAPI *) (DWORD *, DWORD, DWORD*))
                        GetProcAddress(hInstLib, "EnumProcesses");

                  lpfEnumProcessModules = (BOOL (WINAPI *) (HANDLE, HMODULE *, DWORD, LPDWORD))
                        GetProcAddress(hInstLib, "EnumProcessModules");

                  lpfGetModuleBaseName = (DWORD (WINAPI *) (HANDLE, HMODULE, LPTSTR, DWORD))
                        GetProcAddress (hInstLib, "GetModuleBaseNameA");

                  lpfVDMEnumTaskWOWEx = (INT (WINAPI*) (DWORD, TASKENUMPROCEX, LPARAM))
                        GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");

                  if(lpfEnumProcesses == NULL
                        || lpfEnumProcessModules == NULL
                        || lpfGetModuleBaseName == NULL
                        || lpfVDMEnumTaskWOWEx == NULL)
                        __leave;

                  //
                  // Call the PSAPI function EnumProcesses to get all of the
                  // ProcIDs currently in the system.

                  // 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 the buffer and again to fill the buffer.
                  // This isn't true: the cbNeeded parameter returns the
                  // number of PIDs returned, so if your buffer size is
                  // zero cbNeeded returns zero.
                  //
                  // The "HeapAlloc" loop here ensures that we actually
                  // allocate a buffer large enough for all the PIDS in the system.

                  dwSize2 = 256 * sizeof(DWORD);
                  do {
                        if(lpdwPIDs){
                              HeapFree(GetProcessHeap(), 0, lpdwPIDs);
                              dwSize *= 2;
                        }

                        lpdwPIDs = (LPDWORD)HeapAlloc(GetProcessHeap(), 0, dwSize2);
                        if (lpdwPIDs == NULL)
                              __leave;

                        if(!lpfEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
                              __leave;

                  }while (dwSize == dwSize2);

                  //How many ProcIds 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 to be opened).
                        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 will be the
                              // EXE module for which we will retrieve the name.
                              if (lpfEnumProcessModules(hProcess, &hMod,
                                    sizeof(hMod), &dwSize2)) {

                                          // Get the module name
                                          if (!lpfGetModuleBaseName(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, "NTVDM.EXE") == 0) {

                              ProcessInfoEx proc;

                              // Fill in some info for the 16-bit enum proc.
                              proc.dwPID = lpdwPIDs[dwIndex];

                              proc.lpProc = lpProc;
                              proc.lParam = (DWORD) lParam;
                              proc.bEnd = FALSE;

                              // Enum the 16-bit stuff.
                              lpfVDMEnumTaskWOWEx(lpdwPIDs[dwIndex],
                                    (TASKENUMPROCEX) Enum16, (LPARAM) &proc);

                              // Did our main enum func say quit?
                              if (proc.bEnd)
                                    break;
                              process->push_back(proc);

                        }
                  }

            } __finally {

                  if (hInstLib)
                        FreeLibrary(hInstLib);

                  if (hInstLib2)
                        FreeLibrary(hInstLib2);

                  if (lpdwPIDs)
                        HeapFree(GetProcessHeap(), 0, lpdwPIDs);
            }

            // If any OS other than Windows NT 4.0.
      } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
            || (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
            && osver.dwMajorVersion > 4)) {

                  __try {

                        hInstLib = LoadLibraryA("Kernel32.DLL");
                        if (hInstLib == NULL)
                              __leave;

                        // If NT-based OS, load VDMDBG.DLL.
                        if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
                              hInstLib2 = LoadLibraryA("VDMDBG.DLL");
                              if (hInstLib2 == NULL)
                                    __leave;
                        }

                        // Get procedure addresses. We are linking to
                        // these functions explicitly, because a module using
                        // this code would fail to load under Windows NT,
                        // which does not have the Toolhelp32
                        // functions in KERNEL32.DLL.
                        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)
                              __leave;

                        if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
                              lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
                                    LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");
                              if (lpfVDMEnumTaskWOWEx == NULL)
                                    __leave;
                        }

                        // Get a handle to a Toolhelp snapshot of all 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) {
                              ProcessInfoEx sProcess;



                              // Call the enum func with the filename and ProcID.
                              if (lpProc(procentry.th32ProcessID, 0,
                                    procentry.szExeFile, reinterpret_cast<LPARAM>(&sProcess))) {

                                          // Did we just bump into an NTVDM?
                                          if (_stricmp(procentry.szExeFile, "NTVDM.EXE") == 0) {

                                                ProcessInfoEx sProcess;
                                                // Fill in some info for the 16-bit enum proc.

                                                sProcess.dwPID = procentry.th32ProcessID;
                                                sProcess.lpProc = lpProc;
                                                sProcess.lParam = (DWORD) lParam;
                                                sProcess.bEnd = FALSE;

                                                // Enum the 16-bit stuff.
                                                lpfVDMEnumTaskWOWEx(procentry.th32ProcessID,
                                                      (TASKENUMPROCEX) Enum16, (LPARAM) &sProcess);

                                                // Did our main enum func say quit?
                                                if (sProcess.bEnd)
                                                      break;
                                                process->push_back(sProcess);
                                          }

                                          procentry.dwSize = sizeof(PROCESSENTRY32);
                                          bFlag = lpfProcess32Next(hSnapShot, &procentry);
                                          process->push_back(sProcess);


                                    } else
                                          bFlag = FALSE;
                        }

                  } __finally {

                        if (hInstLib)
                              FreeLibrary(hInstLib);

                        if (hInstLib2)
                              FreeLibrary(hInstLib2);
                  }

            } 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;

      ProcessInfoEx *psInfo = (ProcessInfoEx *)lpUserDefined;

      bRet = psInfo->lpProc(psInfo->dwPID, hTask16, pszFileName,psInfo->lParam);

      if (!bRet)
            psInfo->bEnd = TRUE;

      return !bRet;
}


BOOL CALLBACK ProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR szProcess, LPARAM lParam)
{
      
      ProcessInfoEx &process = *(reinterpret_cast<ProcessInfoEx *>(lParam));

      TCHAR userName[1024]= {0};
      TCHAR domainName[1024]= {0};

      DWORD userLen = sizeof(userName)/sizeof(userName[0]);
      DWORD domainLen = sizeof(domainName)/sizeof(domainName[0]);

      if (wTask == 0)
      {
            //if(GetCurrentUserAndDomain(userName, &userLen, domainName, &domainLen, dwPID))
            //{
            //      HRESULT hr = StringCchCopy(process.UserName, sizeof(process.UserName), userName);

            //      if (FAILED(hr))
            //      {
            //            MessageBox("Copy of User Name failed", 0, 0);
            //            
            //            return false;
            //      }

            //      hr = StringCchCopy(process.Domain, domainLen, domainName);
            //      if (FAILED(hr))
            //      {
            //            return false;
            //      }

            //      hr = StringCchCopy(process.ProcessName, sizeof(process.ProcessName), szProcess);
            //      if (FAILED(hr))
            //      {
            //            return false;
            //      }

            //      //processContainer.push_back(procInfo);

            //}
            //else
            //{
                  HRESULT hr = StringCchCopy(process.ProcessName,sizeof(process.ProcessName), szProcess);
                  if (FAILED(hr))
                  {
                        return false;
                  }
                  //processContainer.push_back(procInfo);
            //}


      }

      else { //if wtask == false

            HRESULT hr = StringCchCopy(process.ProcessName, sizeof(process.ProcessName), szProcess);
            if (FAILED(hr))
            {
                  return false;
            }
            //processContainer.push_back(procInfo);
            //printf("  %5u %s\n", wTask, szProcess);}
      }
      return TRUE;
}




BOOL GetCurrentUserAndDomain(PTSTR szUser, PDWORD pcchUser, PTSTR szDomain, PDWORD pcchDomain, DWORD dwPID)
{

     BOOL         fSuccess = FALSE;
     HANDLE       hToken   = NULL;
     HANDLE       hProcess = NULL;
     PTOKEN_USER  ptiUser  = NULL;
     DWORD        cbti     = 0;
     SID_NAME_USE snu;

     __try {

          // Get the process handle
          hProcess = OpenProcess ( PROCESS_QUERY_INFORMATION, FALSE, dwPID);
             
          if (!hProcess)
               __leave;

          if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
               __leave;


          // Obtain the size of the user information in the token.
          if (GetTokenInformation(hToken, TokenUser, NULL, 0, &cbti)) {
                  
               // Call should have failed due to zero-length buffer.
               __leave;

          } else {

               // Call should have failed due to zero-length buffer.
               if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
                    __leave;
          }

          // Allocate buffer for user information in the token.
          ptiUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, cbti);
          if (!ptiUser)
               __leave;

          // Retrieve the user information from the token.
          if (!GetTokenInformation(hToken, TokenUser, ptiUser, cbti, &cbti))
               __leave;

          // Retrieve user name and domain name based on user's SID.
          if (!LookupAccountSid(NULL, ptiUser->User.Sid, szUser, pcchUser,
               szDomain, pcchDomain, &snu))
               __leave;

          fSuccess = TRUE;
     }


     __finally {

          // Free resources.
          if (hToken)
               CloseHandle(hToken);

          if (ptiUser)
               HeapFree(GetProcessHeap(), 0, ptiUser);
     }

     return fSuccess;
}

size_t ProcessInfo::ProcessCount()
{
     return processes.size();
}


ProcessInfoEx& ProcessInfo::Process( size_t index )
{
     if( index < processes.size() )
     {
          return processes.at(index);
     }
     return processes.back();
}


TIA,
S/.
ASKER CERTIFIED SOLUTION
Avatar of joghurt
joghurt

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
OMG, joghurt - that worked! Thank you!!

=o)
Oh, just a last comment: If ProcessEnumerator fails, you shouldn't abort the enumeration; you should only skip that one process.
joghurt - what do you mean?

learning,
S/.
If you return FALSE in ProcessEnumerator, the enumeration is aborted. So if there's some error (you don't have necessary privileges for a given process, etc.), you should only skip only that one process [to be more precise, only that one given member of the structure], and you shouldn't ever return FALSE. Or only if push_back fails and you're sure the next call will fail, too.
Joghurt,

Thanks for your feedback - so, in other words, instead of doing this:

HRESULT hr = StringCchCopy(process.UserName, sizeof(process.UserName), userName);

if (FAILED(hr))
     {
         return false;
     }

I should do this, instead:
if (FAILED(hr))
     {
        break;
     }

Let me know.

Thanks,
S/.



Because there is no "for" or "switch" statement nearby, "break" won't compile. ;-)

BOOL CALLBACK ProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR szProcess, LPARAM lParam)
{
     
     ProcessInfoEx &process = *(reinterpret_cast<ProcessInfoEx *>(lParam));

     TCHAR userName[1024]= {0};
     TCHAR domainName[1024]= {0};

     DWORD userLen = sizeof(userName)/sizeof(userName[0]);
     DWORD domainLen = sizeof(domainName)/sizeof(domainName[0]);
     bool ok = true;

     process.UserName[0] = _T('\0');
     process.Domain[0] = _T('\0');
     process.ProcessName[0] = _T('\0');
     if (wTask == 0)
     {
          if(GetCurrentUserAndDomain(userName, &userLen, domainName, &domainLen, dwPID)) {
               HRESULT hr = StringCchCopy(process.UserName, sizeof(process.UserName), userName);
               if (FAILED(hr))
                    ok = false;

               hr = StringCchCopy(process.Domain, domainLen, domainName);
               if (FAILED(hr))
                    ok = false;

               hr = StringCchCopy(process.ProcessName, sizeof(process.ProcessName), szProcess);
               if (FAILED(hr))
                    ok = false;
          }
          else
          {
               HRESULT hr = StringCchCopy(process.ProcessName,sizeof(process.ProcessName), szProcess);
               if (FAILED(hr))
                    ok = false;
          }
     }
     else { //if wtask == false
          HRESULT hr = StringCchCopy(process.ProcessName, sizeof(process.ProcessName), szProcess);
          if (FAILED(hr))
               ok = false;
     }

// It's up to you whether you want to list processes with incomplete process information.
// If you need them, just remove the "if" line.
     if (ok)
         processContainer.push_back(process);

     return TRUE;
}
Awesome, joghurt - thanks very much for your help!

=)