process_path.h
1 #pragma once 2 #define WIN32_LEAN_AND_MEAN 3 #include <windows.h> 4 #include <shlwapi.h> 5 6 #include <string> 7 #include <thread> 8 9 // Get the executable path or module name for modern apps 10 inline std::wstring get_process_path(DWORD pid) noexcept 11 { 12 auto process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, TRUE, pid); 13 std::wstring name; 14 if (process != INVALID_HANDLE_VALUE) 15 { 16 name.resize(MAX_PATH); 17 DWORD name_length = static_cast<DWORD>(name.length()); 18 if (QueryFullProcessImageNameW(process, 0, name.data(), &name_length) == 0) 19 { 20 name_length = 0; 21 } 22 name.resize(name_length); 23 CloseHandle(process); 24 } 25 return name; 26 } 27 28 // Get the executable path or module name for modern apps 29 inline std::wstring get_process_path(HWND window) noexcept 30 { 31 const static std::wstring app_frame_host = L"ApplicationFrameHost.exe"; 32 33 DWORD pid{}; 34 GetWindowThreadProcessId(window, &pid); 35 auto name = get_process_path(pid); 36 37 if (name.length() >= app_frame_host.length() && 38 name.compare(name.length() - app_frame_host.length(), app_frame_host.length(), app_frame_host) == 0) 39 { 40 // It is a UWP app. We will enumerate the windows and look for one created 41 // by something with a different PID 42 DWORD new_pid = pid; 43 44 EnumChildWindows( 45 window, [](HWND hwnd, LPARAM param) -> BOOL { 46 auto new_pid_ptr = reinterpret_cast<DWORD*>(param); 47 DWORD pid; 48 GetWindowThreadProcessId(hwnd, &pid); 49 if (pid != *new_pid_ptr) 50 { 51 *new_pid_ptr = pid; 52 return FALSE; 53 } 54 else 55 { 56 return TRUE; 57 } 58 }, 59 reinterpret_cast<LPARAM>(&new_pid)); 60 61 // If we have a new pid, get the new name. 62 if (new_pid != pid) 63 { 64 return get_process_path(new_pid); 65 } 66 } 67 68 return name; 69 } 70 71 inline std::wstring get_process_path_waiting_uwp(HWND window) 72 { 73 const static std::wstring appFrameHost = L"ApplicationFrameHost.exe"; 74 75 int attempt = 0; 76 auto processPath = get_process_path(window); 77 78 while (++attempt < 30 && processPath.length() >= appFrameHost.length() && 79 processPath.compare(processPath.length() - appFrameHost.length(), appFrameHost.length(), appFrameHost) == 0) 80 { 81 std::this_thread::sleep_for(std::chrono::milliseconds(5)); 82 processPath = get_process_path(window); 83 } 84 85 return processPath; 86 } 87 88 inline std::wstring get_module_filename(HMODULE mod = nullptr) 89 { 90 wchar_t buffer[MAX_PATH + 1]; 91 DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH); 92 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 93 { 94 const DWORD long_path_length = 0xFFFF; // should be always enough 95 std::wstring long_filename(long_path_length, L'\0'); 96 actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length); 97 return long_filename.substr(0, actual_length); 98 } 99 return { buffer, actual_length }; 100 } 101 102 inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool removeFilename = true) 103 { 104 wchar_t buffer[MAX_PATH + 1]; 105 DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH); 106 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 107 { 108 const DWORD long_path_length = 0xFFFF; // should be always enough 109 std::wstring long_filename(long_path_length, L'\0'); 110 actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length); 111 PathRemoveFileSpecW(long_filename.data()); 112 long_filename.resize(std::wcslen(long_filename.data())); 113 long_filename.shrink_to_fit(); 114 return long_filename; 115 } 116 117 if (removeFilename) 118 { 119 PathRemoveFileSpecW(buffer); 120 } 121 return { buffer, static_cast<uint64_t>(lstrlenW(buffer))}; 122 }