mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 08:29:10 +01:00
Compare commits
3 Commits
update-tel
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43b2b4d5da | ||
|
|
5c48124e4c | ||
|
|
98063b17a7 |
@@ -117,7 +117,11 @@ public:
|
||||
|
||||
HRESULT result;
|
||||
|
||||
if (!RunNonElevatedEx(path.c_str(), L"", get_module_folderpath(g_hInst)))
|
||||
// Get pipe name from writer if using pipes
|
||||
std::wstring pipe_name = writer.get_pipe_name();
|
||||
std::wstring command_args = pipe_name.empty() ? L"" : pipe_name;
|
||||
|
||||
if (!RunNonElevatedEx(path.c_str(), command_args.c_str(), get_module_folderpath(g_hInst)))
|
||||
{
|
||||
result = E_FAIL;
|
||||
Trace::InvokedRet(result);
|
||||
|
||||
@@ -287,7 +287,17 @@ HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer
|
||||
PROCESS_INFORMATION processInformation;
|
||||
std::wstring command_line = L"\"";
|
||||
command_line += exe_path;
|
||||
command_line += L"\"\0";
|
||||
command_line += L"\"";
|
||||
|
||||
// Add pipe name as command line argument if using pipes
|
||||
std::wstring pipe_name = writer->get_pipe_name();
|
||||
if (!pipe_name.empty())
|
||||
{
|
||||
command_line += L" ";
|
||||
command_line += pipe_name;
|
||||
}
|
||||
|
||||
command_line += L"\0";
|
||||
|
||||
CreateProcessW(
|
||||
NULL,
|
||||
|
||||
@@ -4,13 +4,17 @@
|
||||
#include "Constants.h"
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
#include <rpc.h>
|
||||
|
||||
constexpr DWORD DefaultPipeBufferSize = 8192;
|
||||
constexpr DWORD DefaultPipeTimeoutMillis = 200;
|
||||
|
||||
namespace ipc
|
||||
{
|
||||
Writer::Writer()
|
||||
Writer::Writer() : m_pipe_handle(INVALID_HANDLE_VALUE), m_use_pipes(true)
|
||||
{
|
||||
start();
|
||||
}
|
||||
@@ -22,6 +26,32 @@ namespace ipc
|
||||
|
||||
HRESULT Writer::start()
|
||||
{
|
||||
// Try to use pipes first, fall back to file-based IPC if needed
|
||||
if (m_use_pipes)
|
||||
{
|
||||
// Generate unique pipe name similar to PowerRename
|
||||
UUID temp_uuid;
|
||||
wchar_t* uuid_chars = nullptr;
|
||||
|
||||
if (UuidCreate(&temp_uuid) == RPC_S_OK &&
|
||||
UuidToString(&temp_uuid, reinterpret_cast<RPC_WSTR*>(&uuid_chars)) == RPC_S_OK)
|
||||
{
|
||||
m_pipe_name = L"\\\\.\\pipe\\powertoys_filelocksmith_input_";
|
||||
m_pipe_name += uuid_chars;
|
||||
RpcStringFree(reinterpret_cast<RPC_WSTR*>(&uuid_chars));
|
||||
|
||||
HRESULT hr = create_pipe_server();
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
// If pipe creation failed, fall back to file-based IPC
|
||||
m_use_pipes = false;
|
||||
}
|
||||
|
||||
// File-based IPC fallback
|
||||
std::wstring path = PTSettingsHelper::get_module_save_folder_location(constants::nonlocalizable::PowerToyName);
|
||||
path += L"\\";
|
||||
path += constants::nonlocalizable::LastRunPath;
|
||||
@@ -37,8 +67,70 @@ namespace ipc
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT Writer::create_pipe_server()
|
||||
{
|
||||
m_pipe_handle = CreateNamedPipe(
|
||||
m_pipe_name.c_str(),
|
||||
PIPE_ACCESS_DUPLEX | WRITE_DAC,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
DefaultPipeBufferSize,
|
||||
DefaultPipeBufferSize,
|
||||
DefaultPipeTimeoutMillis,
|
||||
NULL);
|
||||
|
||||
if (m_pipe_handle == NULL || m_pipe_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
start_pipe_server_thread();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void Writer::start_pipe_server_thread()
|
||||
{
|
||||
m_pipe_thread = std::thread([this]() {
|
||||
// This call blocks until a client process connects to the pipe
|
||||
BOOL connected = ConnectNamedPipe(m_pipe_handle, NULL);
|
||||
if (!connected)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error != ERROR_PIPE_CONNECTED)
|
||||
{
|
||||
// Connection failed
|
||||
CloseHandle(m_pipe_handle);
|
||||
m_pipe_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
// ERROR_PIPE_CONNECTED means client connected before ConnectNamedPipe was called
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
HRESULT Writer::add_path(LPCWSTR path)
|
||||
{
|
||||
if (m_use_pipes && m_pipe_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Wait for pipe connection to be established
|
||||
if (m_pipe_thread.joinable())
|
||||
{
|
||||
m_pipe_thread.join();
|
||||
}
|
||||
|
||||
if (m_pipe_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Create delimited path string
|
||||
std::wstring delimited_path = path;
|
||||
delimited_path += L"?"; // Use '?' as delimiter like PowerRename
|
||||
|
||||
DWORD path_length_bytes = static_cast<DWORD>(delimited_path.length() * sizeof(WCHAR));
|
||||
DWORD bytes_written;
|
||||
BOOL result = WriteFile(m_pipe_handle, delimited_path.c_str(), path_length_bytes, &bytes_written, NULL);
|
||||
return result ? S_OK : E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// File-based IPC fallback
|
||||
int length = lstrlenW(path);
|
||||
if (!m_stream.write(reinterpret_cast<const char*>(path), length * sizeof(WCHAR)))
|
||||
{
|
||||
@@ -56,7 +148,24 @@ namespace ipc
|
||||
|
||||
void Writer::finish()
|
||||
{
|
||||
add_path(L"");
|
||||
m_stream.close();
|
||||
if (m_use_pipes)
|
||||
{
|
||||
if (m_pipe_thread.joinable())
|
||||
{
|
||||
m_pipe_thread.join();
|
||||
}
|
||||
|
||||
if (m_pipe_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(m_pipe_handle);
|
||||
m_pipe_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// File-based IPC
|
||||
add_path(L"");
|
||||
m_stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
namespace ipc
|
||||
{
|
||||
@@ -15,8 +17,16 @@ namespace ipc
|
||||
HRESULT add_path(LPCWSTR path);
|
||||
void finish();
|
||||
HANDLE get_read_handle();
|
||||
std::wstring get_pipe_name() const { return m_pipe_name; }
|
||||
|
||||
private:
|
||||
std::ofstream m_stream;
|
||||
HRESULT create_pipe_server();
|
||||
void start_pipe_server_thread();
|
||||
|
||||
std::ofstream m_stream; // Keep for backwards compatibility
|
||||
HANDLE m_pipe_handle;
|
||||
std::wstring m_pipe_name;
|
||||
std::thread m_pipe_thread;
|
||||
bool m_use_pipes;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "NativeMethods.h"
|
||||
#include "FileLocksmith.h"
|
||||
#include "../FileLocksmithLib/Constants.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace winrt::PowerToys::FileLocksmithLib::Interop::implementation
|
||||
{
|
||||
@@ -95,6 +96,99 @@ namespace winrt::PowerToys::FileLocksmithLib::Interop::implementation
|
||||
}
|
||||
return com_array<hstring>{ result_cpp.begin(), result_cpp.end() };
|
||||
}
|
||||
|
||||
com_array<hstring> NativeMethods::ReadPathsFromPipe(hstring const& pipeName)
|
||||
{
|
||||
std::vector<std::wstring> result_cpp;
|
||||
|
||||
if (pipeName.empty())
|
||||
{
|
||||
return com_array<hstring>{ result_cpp.begin(), result_cpp.end() };
|
||||
}
|
||||
|
||||
std::wstring pipe_name_str = pipeName.c_str();
|
||||
HANDLE hPipe = INVALID_HANDLE_VALUE;
|
||||
|
||||
// Try to connect to the pipe for up to 5 seconds
|
||||
for (int attempts = 0; attempts < 50 && hPipe == INVALID_HANDLE_VALUE; attempts++)
|
||||
{
|
||||
hPipe = CreateFile(
|
||||
pipe_name_str.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (hPipe != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
if (GetLastError() != ERROR_PIPE_BUSY)
|
||||
break;
|
||||
|
||||
if (!WaitNamedPipe(pipe_name_str.c_str(), 100))
|
||||
break;
|
||||
}
|
||||
|
||||
if (hPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Fall back to file-based reading
|
||||
return ReadPathsFromFile();
|
||||
}
|
||||
|
||||
// Set read timeout to prevent hanging
|
||||
COMMTIMEOUTS timeouts = { 0 };
|
||||
timeouts.ReadIntervalTimeout = 100;
|
||||
timeouts.ReadTotalTimeoutConstant = 3000; // 3 second timeout
|
||||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
SetCommTimeouts(hPipe, &timeouts);
|
||||
|
||||
// Read from pipe - collect all data first
|
||||
const DWORD BUFSIZE = 4096;
|
||||
std::wstring allData;
|
||||
WCHAR chBuf[BUFSIZE];
|
||||
DWORD dwRead;
|
||||
BOOL bSuccess;
|
||||
|
||||
// Read with timeout protection
|
||||
int read_attempts = 0;
|
||||
const int max_read_attempts = 30; // Maximum 30 read attempts
|
||||
|
||||
for (;;)
|
||||
{
|
||||
bSuccess = ReadFile(hPipe, chBuf, BUFSIZE * sizeof(WCHAR), &dwRead, NULL);
|
||||
|
||||
if (!bSuccess || dwRead == 0 || ++read_attempts > max_read_attempts)
|
||||
break;
|
||||
|
||||
// Append to accumulated data
|
||||
allData.append(chBuf, dwRead / sizeof(WCHAR));
|
||||
|
||||
if (!bSuccess)
|
||||
break;
|
||||
}
|
||||
|
||||
CloseHandle(hPipe);
|
||||
|
||||
// Parse all data using delimiter
|
||||
if (!allData.empty())
|
||||
{
|
||||
std::wstringstream ss(allData);
|
||||
std::wstring item;
|
||||
wchar_t delimiter = L'?';
|
||||
|
||||
while (std::getline(ss, item, delimiter))
|
||||
{
|
||||
if (!item.empty())
|
||||
{
|
||||
result_cpp.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return com_array<hstring>{ result_cpp.begin(), result_cpp.end() };
|
||||
}
|
||||
|
||||
bool NativeMethods::StartAsElevated(array_view<hstring const> paths)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace winrt::PowerToys::FileLocksmithLib::Interop::implementation
|
||||
static com_array<winrt::PowerToys::FileLocksmithLib::Interop::ProcessResult> FindProcessesRecursive(array_view<hstring const> paths);
|
||||
static hstring PidToFullPath(uint32_t pid);
|
||||
static com_array<hstring> ReadPathsFromFile();
|
||||
static com_array<hstring> ReadPathsFromPipe(hstring const& pipeName);
|
||||
static bool StartAsElevated(array_view<hstring const> paths);
|
||||
static bool SetDebugPrivilege();
|
||||
static bool IsProcessElevated();
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace FileLocksmithUI
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public static string[] CommandLineArgs { get; private set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="App"/> class.
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
@@ -43,6 +45,10 @@ namespace FileLocksmithUI
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
// Store command line arguments for access by other classes
|
||||
string[] commandArgs = Environment.GetCommandLineArgs();
|
||||
CommandLineArgs = commandArgs.Length > 1 ? commandArgs.Skip(1).ToArray() : Array.Empty<string>();
|
||||
|
||||
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredFileLocksmithEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
||||
|
||||
@@ -79,8 +79,47 @@ namespace PowerToys.FileLocksmithUI.ViewModels
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
paths = NativeMethods.ReadPathsFromFile();
|
||||
Logger.LogInfo($"Starting FileLocksmith with {paths.Length} files selected.");
|
||||
// Check if pipe name was passed as command line argument
|
||||
string[] args = FileLocksmithUI.App.CommandLineArgs;
|
||||
string pipeName = null;
|
||||
|
||||
// Look for pipe name in command line arguments
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
foreach (string arg in args)
|
||||
{
|
||||
if (arg.StartsWith(@"\\.\pipe\"))
|
||||
{
|
||||
pipeName = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to read from pipe first, fall back to file if needed
|
||||
if (!string.IsNullOrEmpty(pipeName))
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo($"Attempting to read from pipe: {pipeName}");
|
||||
paths = NativeMethods.ReadPathsFromPipe(pipeName);
|
||||
Logger.LogInfo($"Successfully read {paths.Length} files from pipe.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to read from pipe {pipeName}: {ex.Message}");
|
||||
Logger.LogInfo("Falling back to file-based IPC.");
|
||||
paths = NativeMethods.ReadPathsFromFile();
|
||||
Logger.LogInfo($"Fallback: Read {paths.Length} files from file.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInfo("No pipe name provided, using file-based IPC.");
|
||||
paths = NativeMethods.ReadPathsFromFile();
|
||||
Logger.LogInfo($"Read {paths.Length} files from file.");
|
||||
}
|
||||
|
||||
LoadProcessesCommand = new AsyncRelayCommand(LoadProcessesAsync);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user