{"id":21070829,"url":"https://github.com/fafalone/processpaths","last_synced_at":"2026-03-19T18:43:23.900Z","repository":{"id":215739392,"uuid":"739669666","full_name":"fafalone/ProcessPaths","owner":"fafalone","description":"Get process full paths without elevation","archived":false,"fork":false,"pushed_at":"2024-01-06T07:17:41.000Z","size":16,"stargazers_count":1,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-20T21:56:48.510Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Visual Basic 6.0","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fafalone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2024-01-06T07:04:58.000Z","updated_at":"2024-01-07T15:22:55.000Z","dependencies_parsed_at":"2024-01-06T08:24:42.162Z","dependency_job_id":"7f4db97e-fd87-4113-b7fe-fb655f128ed9","html_url":"https://github.com/fafalone/ProcessPaths","commit_stats":null,"previous_names":["fafalone/processpaths"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FProcessPaths","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FProcessPaths/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FProcessPaths/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FProcessPaths/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fafalone","download_url":"https://codeload.github.com/fafalone/ProcessPaths/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243513843,"owners_count":20303020,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-19T18:48:30.712Z","updated_at":"2025-12-29T15:12:00.442Z","avatar_url":"https://github.com/fafalone.png","language":"Visual Basic 6.0","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EnumProc\n\n### Show process full paths without elevation\n\n![image](https://github.com/fafalone/ProcessPaths/assets/7834493/b7d99212-1ce3-42f3-b7fe-f878e0ae9267)\n\nIf you've used programs like ProcessHacker or ProcessExplorer you might have noticed that you don't need to run them elevated to see the full paths of all the running processes. If you've ever tried this yourself with the standard methods of either reading the PEB or using the QueryFullProcessImageName API, you might have noticed this will fail for processes running as SYSTEM, because we're denied even the very limited PROCESS_QUERY_LIMITED_INFORMATION access right. So how can we find the full path anyway, when those are denied and the normal enum function CreateToolhelp32Snapshot only returns the file name? One way is to ask Windows itself, since it maintains a list of all process paths internally.\n\nThis is done with the NtQuerySystemInformation and the undocumented info class SystemProcessIdInformation. This allows us to specify a SYSTEM_PROCESS_ID_INFORMATION type with the ProcessId filled in, and it will fill a buffer with the full path to the image. \n\n```vba\n    Private Type SYSTEM_PROCESS_ID_INFORMATION\n        ProcessId As LongPtr\n        ImageName As UNICODE_STRING\n    End Type\n\n    Private Type UNICODE_STRING\n        uLength As Integer\n        uMaximumLength As Integer\n        pBuffer As LongPtr\n    End Type\n\n    Private Function GetProcessFullPathEx(pid As Long, pPath As String) As Long\n        'Regular method can't get path of SYSTEM process\n        'Note: API Returns NT path\n        Dim Status As Long 'NTSTATUS\n        Dim lpBuffer As LongPtr\n        Dim spii As SYSTEM_PROCESS_ID_INFORMATION\n        Dim sTemp As String\n        Dim cbMax As Long: cbMax = MAX_PATH * 2 'LenB(Of Integer) ' * sizeof(WCHAR)\n        Dim cbRet As Long\n        lpBuffer = LocalAlloc(LMEM_FIXED, cbMax)\n        spii.ProcessId = pid\n        spii.ImageName.uMaximumLength = cbMax\n        spii.ImageName.pBuffer = lpBuffer\n        \n        Status = NtQuerySystemInformation(SystemProcessIdInformation, spii, LenB(spii), cbRet)\n        If NT_SUCCESS(Status) Then\n            sTemp = LPWSTRtoStr(lpBuffer, False)\n            If bSetVM = False Then\n                MapVolumes\n            End If\n            pPath = ConvertNtPathToDosPath(sTemp)\n        Else\n            Debug.Print \"GetProcessFullPathEx error, 0x\" \u0026 Hex$(Status)\n        End If\n        \n        LocalFree lpBuffer\n        GetProcessFullPathEx = Status\n    End Function\n```\n\nThe final piece of the puzzle is the path post-processing: ConvertNtPathToDosPath. This is needed because the paths we receive here are in the format e.g. \\Device\\HarddiskVolume1\\Windows\\System32\\crss.exe rather than the drive letters we're accustomed to. The way we translate these is by first creating a map with the MapVolumes function of every drive letter to the device path:\n\n```vba\n    Private Sub MapVolumes()\n    'Map out \\Device\\Harddiskblahblah\n    Dim sDrive As String\n    Dim i As Long, j As Long\n    Dim sBuffer As String\n    ReDim VolMap(0)\n    Dim tmpMap() As VolData\n    Dim nMap As Long, nfMap As Long\n    Dim lIdx As Long\n    Dim lnMax As Long\n    Dim cb As Long\n    For lIdx = 0 To 25\n        sDrive = Chr$(65 + lIdx) \u0026 \":\"\n        sBuffer = String$(1000, vbNullChar)\n        cb = QueryDosDeviceW(StrPtr(sDrive), StrPtr(sBuffer), Len(sBuffer))\n        If cb Then\n            ReDim Preserve tmpMap(nMap)\n            tmpMap(nMap).sLetter = sDrive\n            tmpMap(nMap).sName = TrimNullW(sBuffer)\n            nMap = nMap + 1\n        End If\n    Next\n    'Next we need to sort the array so e.g. 10 will always come before 1\n    'We'll find the longest ones, add any of that length, then add any\n    'of 1 char shorter, until we've added all items\n    For i = 0 To (nMap - 1)\n        If Len(tmpMap(i).sName) \u003e lnMax Then lnMax = Len(tmpMap(i).sName)\n    Next i\n    ReDim VolMap(nMap - 1)\n    For i = lnMax To 1 Step -1\n        For j = 0 To UBound(tmpMap)\n            If Len(tmpMap(j).sName) = i Then\n                VolMap(nfMap).sName = tmpMap(j).sName\n                VolMap(nfMap).sLetter = tmpMap(j).sLetter\n                nfMap = nfMap + 1\n            End If\n        Next j\n        If nfMap = nMap Then Exit For\n    Next i\n    bSetVM = True\n    End Sub\n```\n\nThen we just run each path through a find/replace of each map name to the corresponding letter.\n\n```vba\n    Private Function ConvertNtPathToDosPath(sPath As String) As String\n    If sPath = \"\" Then Exit Function\n    \n    Dim i As Long\n    ConvertNtPathToDosPath = sPath\n    For i = 0 To UBound(VolMap)\n        ConvertNtPathToDosPath = Replace$(ConvertNtPathToDosPath, VolMap(i).sName, VolMap(i).sLetter, 1, 1)\n    Next\n    End Function\n```\n\nAll in all, this is more complicated than the traditional way, but there's plenty of situations where you're not able to run elevated but still want to display a list of processes and their paths--- or just their name, but need their paths to look up their icon.\n\n\n-----\n\nThis code is compatible with both VB6 and twinBASIC, including 64bit compilation in the latter. The .twinproj is included, but you can reimport yourself to see the only change made is to use the smoother built in Anchor resizing rather than Form_Resize, as noted in the code.\n\nThere are no dependencies and this should work on all Windows versions XP and later.\n\nUPDATE (2023 Nov 19) - Fix for garbage in system modules with no path. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffafalone%2Fprocesspaths","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffafalone%2Fprocesspaths","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffafalone%2Fprocesspaths/lists"}