#include "pch.h" #include "FileLocksmith.h" #include "../FileLocksmithExt/Constants.h" namespace FileLocksmith::Interop { public ref struct ProcessResult { System::String^ name; System::UInt32 pid; System::String^ user; array^ files; }; System::String^ from_wstring_view(std::wstring_view str) { return gcnew System::String(str.data(), 0, static_cast(str.size())); } std::wstring from_system_string(System::String^ str) { // TODO use some built-in method auto chars = str->ToCharArray(); std::wstring result(chars->Length, 0); for (int i = 0; i < chars->Length; i++) { result[i] = chars[i]; } return result; } std::wstring paths_file() { #pragma warning(suppress : 4691) // Weird warning about System::String from referenced library not being the one expected (?!) std::wstring path = from_system_string(interop::Constants::AppDataPath()); path += L"\\"; path += constants::nonlocalizable::PowerToyName; path += L"\\"; path += constants::nonlocalizable::LastRunPath; return path; } std::wstring executable_path() { return pid_to_full_path(GetCurrentProcessId()); } public ref struct NativeMethods { static array ^ FindProcessesRecursive(array^ paths) { const int n = paths->Length; std::vector paths_cpp(n); for (int i = 0; i < n; i++) { paths_cpp[i] = from_system_string(paths[i]); } auto result_cpp = find_processes_recursive(paths_cpp); const auto result_size = static_cast(result_cpp.size()); auto result = gcnew array(result_size); for (int i = 0; i < result_size; i++) { auto item = gcnew ProcessResult; item->name = from_wstring_view(result_cpp[i].name); item->pid = result_cpp[i].pid; item->user = from_wstring_view(result_cpp[i].user); const int n_files = static_cast(result_cpp[i].files.size()); item->files = gcnew array(n_files); for (int j = 0; j < n_files; j++) { item->files[j] = from_wstring_view(result_cpp[i].files[j]); } result[i] = item; } return result; } static System::String^ PidToFullPath(System::UInt32 pid) { auto path_cpp = pid_to_full_path(pid); return from_wstring_view(path_cpp); } static array^ ReadPathsFromFile() { std::ifstream stream(paths_file()); std::vector result_cpp; std::wstring line; bool finished = false; while (!finished) { WCHAR ch{}; // We have to read data like this if (!stream.read(reinterpret_cast(&ch), 2)) { finished = true; } else if (ch == L'\n') { if (line.empty()) { finished = true; } else { result_cpp.push_back(line); line = {}; } } else { line += ch; } } auto result = gcnew array(static_cast(result_cpp.size())); for (int i = 0; i < result->Length; i++) { result[i] = from_wstring_view(result_cpp[i]); } return result; } static System::Boolean StartAsElevated(array ^ paths) { std::ofstream stream(paths_file()); const WCHAR newline = L'\n'; for (int i = 0; i < paths->Length; i++) { auto path_cpp = from_system_string(paths[i]); stream.write(reinterpret_cast(path_cpp.c_str()), path_cpp.size() * sizeof(WCHAR)); stream.write(reinterpret_cast(&newline), sizeof(WCHAR)); } stream.write(reinterpret_cast(&newline), sizeof(WCHAR)); if (!stream) { return false; } stream.close(); auto exec_path = executable_path(); SHELLEXECUTEINFOW exec_info{}; exec_info.cbSize = sizeof(exec_info); exec_info.fMask = SEE_MASK_NOCLOSEPROCESS; exec_info.hwnd = NULL; exec_info.lpVerb = L"runas"; exec_info.lpFile = exec_path.c_str(); exec_info.lpParameters = L"--elevated"; exec_info.lpDirectory = NULL; exec_info.nShow = SW_SHOW; exec_info.hInstApp = NULL; if (ShellExecuteExW(&exec_info)) { CloseHandle(exec_info.hProcess); return true; } return false; } /* Adapted from "https://learn.microsoft.com/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--" */ static System::Boolean SetDebugPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tp{}; LUID luid; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != 0) { if (!LookupPrivilegeValue( NULL, // lookup privilege on local system SE_DEBUG_NAME, // privilege to lookup &luid)) // receives LUID of privilege { CloseHandle(hToken); return false; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) { CloseHandle(hToken); return false; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { CloseHandle(hToken); return false; } CloseHandle(hToken); return true; } return false; } // adapted from common/utils/elevation.h. No need to bring all dependencies to this project, though. // TODO: Make elevation.h lighter so that this function can be used without bringing dependencies like spdlog in. static System::Boolean IsProcessElevated() { HANDLE token = nullptr; bool elevated = false; if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { TOKEN_ELEVATION elevation{}; DWORD size; if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size)) { elevated = (elevation.TokenIsElevated != 0); } } if (token) { CloseHandle(token); } return elevated; } }; }