HK1
HK1

Reputation: 12210

Get Process Path and Name by ProcessID - Inconsistent Results (VB6, VBA)

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

Answers (3)

Mike Kwan
Mike Kwan

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

Alex K.
Alex K.

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

Nikos
Nikos

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

Related Questions