Windows API to Get a Full Process Path
Question
Sunday, May 3, 2009 2:00 AM | 1 vote
Does anyone know if there is a way to obtain a process path from an exe filename or from a Process ID obtained through PROCESSENTRY32?
All replies (14)
Sunday, May 3, 2009 6:46 AM âś…Answered | 3 votes
Yes, I did battle with this one too. GetProcessImageFileName() returns the native Windows kernel path, not the Win32 path. I ended up with iterating QueryDosDevice() for drives 'A' through 'Z' to substitute the drive name. That code has been running for a while now without mishap.
static HRESULT NormalizeNTPath(wchar_t* pszPath, size_t nMax)
// Normalizes the path returned by GetProcessImageFileName
{
wchar_t* pszSlash = wcschr(&pszPath[1], '\');
if (pszSlash) pszSlash = wcschr(pszSlash+1, '\');
if (!pszSlash)
return E_FAIL;
wchar_t cSave = *pszSlash;
*pszSlash = 0;
wchar_t szNTPath[_MAX_PATH];
wchar_t szDrive[_MAX_PATH] = L"A:";
// We'll need to query the NT device names for the drives to find a match with pszPath
for (wchar_t cDrive = 'A'; cDrive < 'Z'; ++cDrive)
{
szDrive[0] = cDrive;
szNTPath[0] = 0;
if (0 != QueryDosDevice(szDrive, szNTPath, NUMCHARS(szNTPath)) &&
0 == _wcsicmp(szNTPath, pszPath))
{
// Match
wcscat_s(szDrive, NUMCHARS(szDrive), _UT("\"));
wcscat_s(szDrive, NUMCHARS(szDrive), pszSlash+1);
wcscpy_s(pszPath, nMax, szDrive);
return S_OK;
}
}
*pszSlash = cSave;
return E_FAIL;
}
Hans Passant.
Sunday, May 3, 2009 3:08 AM
By doing this I get half my answer.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
false, PENTRY32.th42ProcessID);
GetProcessImageFileName(hProcess, Buff,dwSize);
The problem with this is that I get /Device/HARDISKVOLUE and then the rest of the path. I would assume that it corresponds to C:\
I read that I can use QueryFullProcessImageName() but the problem is that it only works for some processes. Other return GetLastError message incorrect parameter.
Sunday, May 3, 2009 6:23 AM
Hello Mike,
Instead of GetProcessImageFileName(), you can use - GetModuleFileNameEx() .
Regards,
Jijo.http://weseetips.com[^] Visual C++ tips and tricks. Updated daily.
Sunday, May 3, 2009 6:28 AM | 1 vote
MSDN say's:
To retrieve the name of the main executable module for a remote process, use the GetProcessImageFileName or QueryFullProcessImageName function. This is more efficient and more reliable than calling the GetModuleFileNameEx function with a NULL module handle.
So, I'm using it but it returns devices with /Device/HARDDISKVOLUME...
Sunday, May 3, 2009 3:18 PM
Thaks for the response nobugz. This "QueryDosDevice()" is exactly what I was looking for. Why didn't you call the GetLogicalDriveStrings to obtain only the devices attached to your system?
Sunday, May 3, 2009 3:42 PM | 1 vote
Because it doesn't help you at all mapping the native device name. There is probably a mapping from device number to the return value of that function, just not one that is documented anywhere.
The problem with GetProcessImageFileName() is an interesting one, it is one of the few Win32 APIs where the native Windows API peeks through the cracks. I think this came about as part of the MSFT's settlement with the Department Of Justice, forcing them to document some of the APIs that they were accused of using in their own code but not making available to others. Not sure. That it is not actually usable as-is is par for the course.
Hans Passant.
Sunday, May 3, 2009 7:19 PM
How do I replace a part of the TCHAR string with the DOS device. Here is my test code.
// test_tchar.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
LPWSTR Buff = {0};
DWORD nBuffLen, nMaxPath = MAX_PATH;;
TCHAR LOGICALPATH[MAX_PATH], DOSPATH[] = _T("Z:");
nBuffLen = GetLogicalDriveStrings(1, Buff);
Buff = new TCHAR[nBuffLen + 1];
GetLogicalDriveStrings(nBuffLen, Buff);
for(TCHAR *pBuff=Buff; *pBuff; pBuff+=4)
{
_memccpy(DOSPATH,pBuff,2,2);
QueryDosDevice(DOSPATH,LOGICALPATH, nMaxPath);
if(wcsstr(s, LOGICALPATH))
{
delete Buff;
wcout << DOSPATH;
cin.get();
return 0;
}
}
cin.get();
delete Buff;
return 0;
}
Sunday, May 3, 2009 7:24 PM
Here is a better example. How do I replace \Device\HarddiskVolume2\WINDOWS\ with DOSPATH C:\ so I get C:\Windows\
LPWSTR Buff = {0};
DWORD nBuffLen, nMaxPath = MAX_PATH;;
TCHAR LOGICALPATH[MAX_PATH], DOSPATH[] = _T("C:");
nBuffLen = GetLogicalDriveStrings(1, Buff);
Buff = new TCHAR[nBuffLen + 1];
GetLogicalDriveStrings(nBuffLen, Buff);
for(TCHAR *pBuff=Buff; *pBuff; pBuff+=4)
{
_memccpy(DOSPATH,pBuff,2,2);
QueryDosDevice(DOSPATH,LOGICALPATH, nMaxPath);
TCHAR s[]= _T("\\Device\\HarddiskVolume2\\WINDOWS");
if(wcsstr(s, LOGICALPATH))
{
delete Buff;
return 0; // DOSPATH
}
}
cin.get();
delete Buff;
return 0;
Sunday, May 3, 2009 8:13 PM
Why are you messing with GetLogicalDriveStrings()?
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t path[_MAX_PATH];
wcscpy_s(path, _MAX_PATH, L"\Device\HarddiskVolume2\WINDOWS\");
HRESULT hr = NormalizeNTPath(path, _MAX_PATH);
if (SUCCEEDED(hr))
std::wcout << path;
return 0;
}
Output on my machine:
D:\WINDOWS\
In the code snippet, replace _UT("bla") by L"bla" and use this macro:
#define NUMCHARS(a) (sizeof(a)/sizeof(*a))
Hans Passant.
Sunday, May 3, 2009 11:41 PM
After help from you on the other post, this is what I came up with. Thanks.
void CInjection::RetDosPath(TCHAR *LogicalPath, TCHAR *DosBuffer)
{
LPWSTR Buff = {0};
size_t nBuffLen, nMaxPath = MAX_PATH;
TCHAR LOGICALPATH[MAX_PATH], DOSPATH[] = _T("Z:"), *FINALPATH;
nBuffLen = GetLogicalDriveStrings(1, Buff);
Buff = new TCHAR[nBuffLen + 1];
FINALPATH = new TCHAR[MAX_PATH];
GetLogicalDriveStrings(nBuffLen, Buff);
for(TCHAR *pBuff=Buff; *pBuff; pBuff+=4)
{
_memccpy(DOSPATH,pBuff,2,2);
DWORD nRet = QueryDosDevice(DOSPATH,LOGICALPATH, nMaxPath);
if(wcsstr(LogicalPath, LOGICALPATH))
{
_memccpy(FINALPATH,DOSPATH,4,4);
_memccpy(FINALPATH + 2 ,_T("\\"),2,2);
_memccpy(FINALPATH + 3 ,LogicalPath+(nRet-1),MAX_PATH,MAX_PATH);
wcscpy_s(DosBuffer, MAX_PATH, FINALPATH);
delete FINALPATH;
delete Buff;
return;
}
}
wcscpy_s(DosBuffer, MAX_PATH, _T("Unknown"));
delete FINALPATH;
delete Buff;
return;
}
Sunday, May 3, 2009 11:57 PM | 1 vote
Why are you messing with GetLogicalDriveStrings()?
The reason that I used GetLogicalDriveStrings() is so I didn't have to loop throug A-Z. My programs gets the processes on the local machine and then parse the location. So, I wanted to get only the drive strings relevant to my machine and save extra loops if I could.
Maybe it's not the best way and perhaps you could explain why so that I understand more. I'm kind of new to C++.
Monday, May 4, 2009 12:30 AM
I dunno. If that makes GetProcessImageFileName() work, then all the power to you. It is quite murky to me how you did it.
Hans Passant.
Monday, May 4, 2009 12:34 AM
Yes, it does make it work. Sory that my code isn't very legible.
Thursday, October 5, 2017 10:11 AM | 1 vote
Shouldn't the loop have "cDrive <= 'Z'" instead of "cDrive < 'Z'", otherwise the last Z: drive will be skipped.