views:

101

answers:

2

To sum it up, I need to do this:

12345(hWnd) -> "C:\setup.exe"


Right now, I am using GetProcessImageFileName to retrieve the Kernel Device Path of a process handle. I'm retrieving the handle using OpenProcess, passing it the PID. The PID (which I also need) is being retrieved using GetWindowThreadProcessId.

However, this gets me to a string like:

\Device\Harddisk1\setup.exe

At this point, I enumerate all drives on the system using DriveInfo.GetDrives(), and then call QueryDosDevice. Finally, I can do some string-manipulation magic, and "boom," I have my path.


Ok, so my issues:

  1. This process breaks down on Network drives.
  2. All I really want is QueryFullProcessImageName on XP

There HAS to be a better way to do this. Please enlighten me, oh gods of WIN32API!

+1  A: 

The obvious question would be why you don't just use QueryFullProcessImageName, if that's what you want? Do you need compatibility with older versions of Windows?

The closest equivalent to QueryFullProcessImageName that's available on XP is probably GetModuleFileNameEx. I'd probably detect whether QueryFullProcessImageName is available and use it if possible, otherwise fall back to GetModuleFileNameEx.

Edit: While GetModuleFileNameEx isn't 100% dependable at retrieving the name of the executable for every possible process, it does work at least a fairly substantial part of the time. Here's a quick bit of test code I put together:

#include <windows.h>
#include <psapi.h>

#include <iostream>
#include <string>
#include <map>

std::string getfilename(DWORD pid) { 
    HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
    static int winver;
    char path[256]= {0};
    DWORD size = sizeof(path);

    if (winver==0) 
        winver = GetVersion() & 0xf; 

#if WINVER >= 0x600
    if (winver >= 6)    
        QueryFullProcessImageName(process, 0, path, &size);
    else
#endif
    if (!GetModuleFileNameEx(process, NULL, path, sizeof(path)))
        strcpy(path, "Unknown");
    return std::string(path);
}

typedef std::map<DWORD, std::string> win_map;

namespace std { 
    ostream &operator<<(ostream &os, win_map::value_type const &v) { 
        return os << v.first << ": " << v.second;
    }
}

BOOL CALLBACK show_info(HWND window, LPARAM lParam) {
    win_map &exes = *(win_map *)lParam;

    DWORD pid;
    GetWindowThreadProcessId(window, &pid);
    exes[pid] = getfilename(pid);
    return true;
}

int main() {
    win_map exes;
    EnumWindows(show_info, (LPARAM)&exes);
    std::copy(exes.begin(), exes.end(), std::ostream_iterator<win_map::value_type>(std::cout, "\n"));
    return 0;
}

The results of a quick test are somewhat interesting. Compiled as 32-bit code, the version using QueryFullProcessImageName found 33 processes with top-level windows, and found names for 31 of those executables. The version using GetModuleFileNameEx, also found 33 processes, but only found names for 21 of the executable. If, however, I compile it as 64-bit code, either version finds filenames for 31 out of 33 executables (and the same two fail). Given the frequency with which you see XP/x64, that's probably of little consequence, but I found it interesting nonetheless.

In any case, even the least capable version (32-bit/GMFNE) found names for ~2/3rds of the files. While that's certainly not what you'd hope for, it's certainly better than nothing.

Jerry Coffin
QueryFullProcessImageName doesn't work on XP. Yes, I need this to work on older OSes. I am planning on using the Vista+ api if it is supported.
John Gietzen
Oh, and `GetModuleFileNameEx` doesn't work for a module across in another process.
John Gietzen
+1  A: 

It must be possible to retrieve the file path to running processes since Sysinternals' Process Explorer does it. Of course, Process Explorer uses NtQueryInformationProcess, which isn't a supported function. (On the other hand, since you're specifically targeting XP and would use QueryFullProcessImageName on Vista and later, the fear of the API becoming unavailable in a future version of Windows is probably not a concern.)

CodeProject has an article about how to use NtQueryInformationProcess.

jamesdlin