无法始终检索进程的路径

时间:2014-03-31 12:19:23

标签: vb.net

我使用以下代码:

        Using searcher As New ManagementObjectSearcher("SELECT * FROM Win32_Process WHERE ProcessId = " & 1234)
            For Each mgmtObj As ManagementObject In searcher.Get()
                Dim cl As String() = mgmtObj.Item("ExecutablePath").ToString().Split("""")
                Console.WriteLine(cl(cl.Length - 1))
            Next
        End Using

注意:1234是一个示例ID,它将是GetProcessById的结果。

这适用于:

  • 该进程在同一用户下运行。
  • 此过程不是Windows服务(无论用户运行的是什么用户)。

在我测试的所有其他情况下,我收到以下错误:

Object reference not set to an instance of an object.

作为替代方案,我也尝试过以下代码:

Private Shared Function GetAssemblyPathAboveVista(ProcessId As Integer) As String
    Dim buffer = New StringBuilder(1024)
    Dim hprocess As IntPtr = OpenProcess(ProcessAccessFlags.QueryInformation, False, ProcessId)
    If hprocess <> IntPtr.Zero Then
        Try
            Dim size As Integer = buffer.Capacity
            If QueryFullProcessImageName(hprocess, 0, buffer, size) Then
                Return buffer.ToString()
            End If
        Finally
            CloseHandle(hprocess)
        End Try
    End If
    Throw New Win32Exception(Marshal.GetLastWin32Error())
End Function

<DllImport("kernel32.dll")> _
Private Shared Function QueryFullProcessImageName(hprocess As IntPtr, dwFlags As Integer, lpExeName As StringBuilder, ByRef size As Integer) As Boolean
End Function
<DllImport("kernel32.dll")> _
Private Shared Function OpenProcess(dwDesiredAccess As ProcessAccessFlags, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr
End Function

<DllImport("kernel32.dll", SetLastError:=True)> _
Private Shared Function CloseHandle(hHandle As IntPtr) As Boolean
End Function

Enum ProcessAccessFlags As UInteger
    All = &H1F0FFF
    Terminate = &H1
    CreateThread = &H2
    VMOperation = &H8
    VMRead = &H10
    VMWrite = &H20
    DupHandle = &H40
    SetInformation = &H200
    QueryInformation = &H400
    Synchronize = &H100000
End Enum

这仅适用于:   - 该进程在同一用户下运行。   - 该流程不是Windows服务(无论用户运行的是什么用户)。

在所有其他情况下,OpenProcess函数返回0。

注意:我正在使用具有标准权限的用户(非管理员)在Windows 8.1上进行测试。我的应用程序必须以标准用户身份运行。

我的问题是:如何始终能够检索进程路径?我只需要路径信息。

3 个答案:

答案 0 :(得分:3)

所以即使是正常的CLR方法也无法读取升级的过程&#39;文件名,显然是因为实现中的错误。他们在Windows Vista及更高版本中添加了一个新的API标志,以便能够读取信息,但他们还没有在CLR中实现它。您发布的第二个示例似乎与我读过的c#源相同,但它需要比C#到VB转换器能够提供的更多关注。

在任何情况下,它都适用于Process类的扩展方法。

Module ProcessPathExtension
    <DllImport("kernel32.dll")> _
    Private Function QueryFullProcessImageName(hprocess As IntPtr, dwFlags As Integer, lpExeName As StringBuilder, ByRef size As Integer) As Boolean
    End Function
    <DllImport("kernel32.dll")> _
    Private Function OpenProcess(dwDesiredAccess As ProcessAccessFlags, bInheritHandle As Boolean, dwProcessId As Integer) As IntPtr
    End Function

    <DllImport("kernel32.dll", SetLastError:=True)> _
    Private Function CloseHandle(hHandle As IntPtr) As Boolean
    End Function

    Enum ProcessAccessFlags As UInteger
        All = &H1F0FFF
        Terminate = &H1
        CreateThread = &H2
        VMOperation = &H8
        VMRead = &H10
        VMWrite = &H20
        DupHandle = &H40
        SetInformation = &H200
        QueryInformation = &H400
        QueryLimitedInformation = &H1000
        Synchronize = &H100000
    End Enum

    <Extension()> _
    Public Function Path(ByVal _process As Process) As String
        Dim processPath As String = ""

        ' The new QueryLimitedInformation flag is only available on Windows Vista and up.
        If Environment.OSVersion.Version.Major >= 6 Then
            Dim processHandle As IntPtr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, False, _process.Id)
            Try
                If Not processHandle = IntPtr.Zero Then
                    Dim buffer = New StringBuilder(1024)
                    If QueryFullProcessImageName(processHandle, 0, buffer, buffer.Capacity) Then
                        processPath = buffer.ToString()
                    End If
                End If
            Finally
                CloseHandle(processHandle)
            End Try
        Else
            processPath = _process.MainModule.FileName
        End If

        Return processPath
    End Function
End Module

在项目中包含此模块,您可以通过Process对象的新.Path方法访问进程路径:

For Each p In Process.GetProcesses().OrderBy(Function(x) x.Id, Nothing)
    Console.WriteLine(p.Id & vbTab & p.ProcessName & vbCrLf & p.Path & vbCrLf & vbCrLf)
Next

答案 1 :(得分:2)

您是否有任何特殊原因没有使用正常的方法来执行此操作,以便您可以捕获异常?

    For Each p In Process.GetProcessesByName("someprocess.exe")
        Try
            Console.WriteLine(p.MainModule.FileName)
        Catch ex As Exception
            Console.WriteLine(ex.Message & vbCrLf & ex.StackTrace)
            Exit Sub
        End Try
    Next

或者如果你真的需要通过ID获取它:

    Try
        Dim p = Process.GetProcessById(processId)
        If p IsNot Nothing Then
            Console.WriteLine(p.MainModule.FileName)
        End If
    Catch ex As Exception
        Console.WriteLine(ex.Message & vbCrLf & ex.StackTrace)
    End Try

答案 2 :(得分:1)

好吧,我自己试了一下。从我所看到的,它归结为权利。您不能触摸其他用户进程。服务通常由SYSTEM执行,您可能知道这是另一个用户帐户。以管理员身份运行Visual Studio将为您提供所需的内容。

如果您不想以管理员身份执行Visual Studio,则必须将程序设置为在启动时需要管理权限。我希望这不能以任何其他方式解决。