Reputation: 12210
I'm using the code below to get the file path and name for a given process. I simply pass in a ProcessID to the function ExePathFromProcID and it is supposed to return the full path. It enumerates the hard drives as devices instead of using drive letters, but this is not my complaint. Have a look at my code and then see my complaint below.
Public Declare Function OpenProcess Lib "kernel32" ( _
ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Public Declare Function GetProcessImageFileName Lib "psapi.dll" Alias "GetProcessImageFileNameA" _
(ByVal hProcess As Long, _
ByVal lpImageFileName As String, _
ByVal nSize As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Function ExePathFromProcID(idProc As Long) As String
Const MAX_PATH = 260
Const PROCESS_QUERY_INFORMATION = &H400
Const PROCESS_VM_READ = &H10
Dim sBuf As String
Dim sChar As Long, l As Long, hProcess As Long
sBuf = String$(MAX_PATH, Chr$(0))
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, idProc)
If hProcess Then
sChar = GetProcessImageFileName(hProcess, sBuf, MAX_PATH)
If sChar Then
sBuf = Left$(sBuf, sChar)
ExePathFromProcID = sBuf
Debug.Print sBuf
End If
CloseHandle hProcess
End If
End Function
My complaint is that parts of a longer process path will still show when a shorter path is returned, as long as the longer path was retrieved first. Here's an example:
First Call (correct results):
\Device\HarddiskVolume2\Program Files\Portable Apps\Notepad++ Portable\App\Notepad++\notepad++.exe
Second Call (unexpected results):
\Device\HarddiskVolume2\Program Files\Microsoft Office\Office12\MSACCESS.EXE tepad++\notepad++.exe
Notice the "tepad++\notepad++.exe" at the end of the results for the second call? Don't be fooled by the fact that it falls on the second line. It's all part of the same string and is all returned on the second call to this function.
Any ideas why my function is returning this? It would appear that it's a problem of a global string variable not getting cleared but I'm using the code almost exactly as I posted it. No global variables.
Upvotes: 2
Views: 5926
Reputation: 24447
There is a comment on the docs in regards to what the return value is: http://msdn.microsoft.com/en-us/library/ms683217(v=vs.85).aspx#3
ie. the returned value is not the length of the path. So you should use some sort of strlen function.
Upvotes: 0
Reputation: 175816
If you look at the returned buffer the correct path is actually terminated by a null, eg if I pass the PID of my ultramon.exe:
\ D e v i c e \ H a r d d i s k V o l u m e 1 \ P r o g r a m F i l e s \ U l t r a M o n \ U l t r a M o n . e x e x . e x e 3 9 3 8 5 C 5 6 4 2 3 6 2 E 4 5 5 8 4 5 0 6 1 7 2 4 E 6 F 7 4 6 9 6
5C 44 65 76 69 63 65 5C 48 61 72 64 64 69 73 6B 56 6F 6C 75 6D 65 31 5C 50 72 6F 67 72 61 6D 20 46 69 6C 65 73 5C 55 6C 74 72 61 4D 6F 6E 5C 55 6C 74 72 61 4D 6F 6E 2E 65 78 65 0 78 2E 65 78 65 0 33 39 20 33 38 20 35 43 20 35 36 20 34 32 20 33 36 20 32 45 20 34 35 20 35 38 20 34 35 20 30 20 36 31 20 37 32 20 34 45 20 36 46 20 37 34 20 36 39 20 36
The docs don't explicitly say the return value is the length of the path, just the length of the copied buffer which seems not to be the same thing; If the function succeeds, the return value specifies the length of the string copied to the buffer.
This means you need to:
sBuf = Left$(sBuf, InStr(1, sBuf, ChrW$(0)))
Upvotes: 1
Reputation: 787
I think replacing GetProcessImageFileName with QueryFullProcessImageName will resolve the first issue.
The second issue is simply GetProcessImageFileName writing a null-terminated string on top of another. I'm not a VB expert but you can either clear the buffer before passing it to GetProcessImageFileName or setting the right length after it returns.
Upvotes: 1