This commit is contained in:
Stefan Markovic 2024-02-21 22:45:38 +01:00
parent fef50971af
commit fed3f4a483
15 changed files with 279 additions and 217 deletions

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h FileLocksmithContextMenu.base.rc FileLocksmithContextMenu.rc" />
</Target>
@ -120,11 +121,14 @@ MakeAppx.exe pack /d . /p $(OutDir)FileLocksmithContextMenuPackage.msix /nv</Com
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@ -37,6 +37,31 @@
<None Include="Resources.resx">
<Filter>Resource Files</Filter>
</None>
<None Include="packages.config" />
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
<None Include="Assets\FileLocksmith\**">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="FileLocksmithContextMenu.base.rc">

View File

@ -5,17 +5,20 @@
#include <common/utils/resources.h>
#include <common/utils/elevation.h>
#include "FileLocksmithLib/IPC.h"
#include "FileLocksmithLib/Settings.h"
#include "FileLocksmithLib/Trace.h"
#include <atlstr.h>
#include <atlfile.h>
#include <Shlwapi.h>
#include <shobjidl_core.h>
#include <string>
#include <thread>
#include <wrl/module.h>
#include "Generated Files/resource.h"
#define BUFSIZE 4096 * 4
using namespace Microsoft::WRL;
@ -92,18 +95,34 @@ public:
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
{
Trace::Invoked();
ipc::Writer writer;
if (selection == nullptr)
{
return S_OK;
}
if (HRESULT result = writer.start(); FAILED(result))
std::wstring pipe_name(L"\\\\.\\pipe\\powertoys_filelocksmithinput_");
UUID temp_uuid;
wchar_t* uuid_chars = nullptr;
if (UuidCreate(&temp_uuid) == RPC_S_UUID_NO_ADDRESS)
{
Trace::InvokedRet(result);
return result;
auto val = get_last_error_message(GetLastError());
Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L"");
}
else if (UuidToString(&temp_uuid, reinterpret_cast<RPC_WSTR*>(&uuid_chars)) != RPC_S_OK)
{
auto val = get_last_error_message(GetLastError());
Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L"");
}
if (uuid_chars != nullptr)
{
pipe_name += std::wstring(uuid_chars);
RpcStringFree(reinterpret_cast<RPC_WSTR*>(&uuid_chars));
uuid_chars = nullptr;
}
create_pipe_thread = std::thread(&FileLocksmithContextMenuCommand::StartNamedPipeServerAndSendData, this, pipe_name);
std::wstring path = get_module_folderpath(g_hInst);
path = path + L"\\PowerToys.FileLocksmithUI.exe";
@ -117,26 +136,28 @@ public:
return result;
}
DWORD num_items;
selection->GetCount(&num_items);
for (DWORD i = 0; i < num_items; i++)
if (hPipe != INVALID_HANDLE_VALUE)
{
IShellItem* item;
result = selection->GetItemAt(i, &item);
if (SUCCEEDED(result))
{
LPWSTR file_path;
result = item->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
if (SUCCEEDED(result))
{
// TODO Aggregate items and send to UI
writer.add_path(file_path);
CoTaskMemFree(file_path);
}
CAtlFile writePipe(hPipe);
item->Release();
DWORD fileCount = 0;
// Gets the list of files currently selected using the IShellItemArray
selection->GetCount(&fileCount);
// Iterate over the list of files
for (DWORD i = 0; i < fileCount; i++)
{
IShellItem* shellItem;
selection->GetItemAt(i, &shellItem);
LPWSTR itemName;
// Retrieves the entire file system path of the file from its shell item
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &itemName);
CString fileName(itemName);
// File name can't contain '?'
fileName.Append(_T("?"));
// Write the file path into the input stream for image resizer
writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR));
}
writePipe.Close();
}
Trace::InvokedRet(S_OK);
@ -166,7 +187,47 @@ protected:
ComPtr<IUnknown> m_site;
private:
HRESULT StartNamedPipeServerAndSendData(std::wstring pipe_name)
{
hPipe = CreateNamedPipe(
pipe_name.c_str(),
PIPE_ACCESS_DUPLEX |
WRITE_DAC,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFSIZE,
BUFSIZE,
0,
NULL);
if (hPipe == NULL || hPipe == INVALID_HANDLE_VALUE)
{
return E_FAIL;
}
// This call blocks until a client process connects to the pipe
BOOL connected = ConnectNamedPipe(hPipe, NULL);
if (!connected)
{
if (GetLastError() == ERROR_PIPE_CONNECTED)
{
return S_OK;
}
else
{
CloseHandle(hPipe);
}
return E_FAIL;
}
return S_OK;
}
std::wstring context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_FILE_LOCKSMITH_CONTEXT_MENU_ENTRY, L"Unlock with File Locksmith");
std::thread create_pipe_thread;
HANDLE hPipe = INVALID_HANDLE_VALUE;
};
CoCreatableClass(FileLocksmithContextMenuCommand)

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@ -11,6 +11,7 @@
#include <common/themes/icon_helpers.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
#include <common/utils/HDropIterator.h>
// Implementations of inherited IUnknown methods
@ -167,48 +168,13 @@ IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UI
IFACEMETHODIMP ExplorerCommand::InvokeCommand(CMINVOKECOMMANDINFO* pici)
{
Trace::Invoked();
ipc::Writer writer;
if (HRESULT result = writer.start(); FAILED(result))
if (HRESULT result = LaunchUI(pici); FAILED(result))
{
Trace::InvokedRet(result);
return result;
}
if (HRESULT result = LaunchUI(pici, &writer); FAILED(result))
{
Trace::InvokedRet(result);
return result;
}
IShellItemArray* shell_item_array;
HRESULT result = SHCreateShellItemArrayFromDataObject(m_data_obj, __uuidof(IShellItemArray), reinterpret_cast<void**>(&shell_item_array));
if (SUCCEEDED(result))
{
DWORD num_items;
shell_item_array->GetCount(&num_items);
for (DWORD i = 0; i < num_items; i++)
{
IShellItem* item;
result = shell_item_array->GetItemAt(i, &item);
if (SUCCEEDED(result))
{
LPWSTR file_path;
result = item->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
if (SUCCEEDED(result))
{
// TODO Aggregate items and send to UI
writer.add_path(file_path);
CoTaskMemFree(file_path);
}
item->Release();
}
}
shell_item_array->Release();
}
Trace::InvokedRet(S_OK);
return S_OK;
}
@ -242,47 +208,97 @@ ExplorerCommand::~ExplorerCommand()
--globals::ref_count;
}
HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer)
HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici)
{
// Compute exe path
std::wstring exe_path = get_module_folderpath(globals::instance);
exe_path += L'\\';
exe_path += constants::nonlocalizable::FileNameUIExe;
HRESULT hr = E_FAIL;
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
if (pici)
if (FileLocksmithSettings().GetEnabled() &&
pici && (IS_INTRESOURCE(pici->lpVerb)) &&
(LOWORD(pici->lpVerb) == 0))
{
startupInfo.wShowWindow = pici->nShow;
}
else
{
startupInfo.wShowWindow = SW_SHOWNORMAL;
Trace::Invoked();
// Compute exe path
std::wstring exe_path = get_module_folderpath(globals::instance);
exe_path += L'\\';
exe_path += constants::nonlocalizable::FileNameUIExe;
// Create an anonymous pipe to stream filenames
SECURITY_ATTRIBUTES sa;
HANDLE hReadPipe;
HANDLE hWritePipe;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
CAtlFile writePipe(hWritePipe);
CString commandLine;
commandLine.Format(_T("\"%s\""), exe_path.data());
int nSize = commandLine.GetLength() + 1;
LPTSTR lpszCommandLine = new TCHAR[nSize];
_tcscpy_s(lpszCommandLine, nSize, commandLine);
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.hStdInput = hReadPipe;
startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startupInfo.wShowWindow = static_cast<WORD>(pici->nShow);
PROCESS_INFORMATION processInformation;
// Start the resizer
CreateProcess(
NULL,
lpszCommandLine,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&startupInfo,
&processInformation);
delete[] lpszCommandLine;
if (!CloseHandle(processInformation.hProcess))
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (!CloseHandle(processInformation.hThread))
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (m_data_obj)
{
// Stream the input files
HDropIterator i(m_data_obj);
for (i.First(); !i.IsDone(); i.Next())
{
CString fileName(i.CurrentItem());
// File name can't contain '?'
fileName.Append(_T("?"));
writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR));
}
}
writePipe.Close();
}
Trace::InvokedRet(hr);
PROCESS_INFORMATION processInformation;
std::wstring command_line = L"\"";
command_line += exe_path;
command_line += L"\"\0";
CreateProcessW(
NULL,
command_line.data(),
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&startupInfo,
&processInformation);
// Discard handles
CloseHandle(processInformation.hProcess);
CloseHandle(processInformation.hThread);
return S_OK;
return hr;
}

View File

@ -2,8 +2,6 @@
#include "pch.h"
#include "FileLocksmithLib/IPC.h"
#define EXPLORER_COMMAND_UUID_STR "84d68575-e186-46ad-b0cb-baeb45ee29c0"
class __declspec(uuid(EXPLORER_COMMAND_UUID_STR)) ExplorerCommand : public IExplorerCommand, public IShellExtInit, public IContextMenu
@ -45,7 +43,7 @@ private:
HBITMAP m_hbmpIcon = nullptr;
// Helpers
HRESULT LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer);
HRESULT LaunchUI(CMINVOKECOMMANDINFO* pici);
std::atomic<ULONG> m_ref_count = 1;
IDataObject* m_data_obj = NULL;

View File

@ -8,6 +8,8 @@
#include <ShlObj_core.h>
#include <atlbase.h>
#include <commctrl.h>
#include <atlfile.h>
#include <atlstr.h>
// C++ Standard library
#include <atomic>

View File

@ -65,14 +65,12 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Constants.h" />
<ClInclude Include="IPC.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="Trace.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="IPC.cpp" />
<ClCompile Include="Settings.cpp" />
<ClCompile Include="Trace.cpp" />
<ClCompile Include="FileLocksmithLib.cpp" />

View File

@ -27,9 +27,6 @@
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IPC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Constants.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -41,9 +38,6 @@
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IPC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View File

@ -1,62 +0,0 @@
#include "pch.h"
#include "IPC.h"
#include "Constants.h"
#include <common/SettingsAPI/settings_helpers.h>
constexpr DWORD DefaultPipeBufferSize = 8192;
constexpr DWORD DefaultPipeTimeoutMillis = 200;
namespace ipc
{
Writer::Writer()
{
start();
}
Writer::~Writer()
{
finish();
}
HRESULT Writer::start()
{
std::wstring path = PTSettingsHelper::get_module_save_folder_location(constants::nonlocalizable::PowerToyName);
path += L"\\";
path += constants::nonlocalizable::LastRunPath;
try
{
m_stream = std::ofstream(path);
return S_OK;
}
catch (...)
{
return E_FAIL;
}
}
HRESULT Writer::add_path(LPCWSTR path)
{
int length = lstrlenW(path);
if (!m_stream.write(reinterpret_cast<const char*>(path), length * sizeof(WCHAR)))
{
return E_FAIL;
}
WCHAR line_break = L'\n';
if (!m_stream.write(reinterpret_cast<const char*>(&line_break), sizeof(WCHAR)))
{
return E_FAIL;
}
return S_OK;
}
void Writer::finish()
{
add_path(L"");
m_stream.close();
}
}

View File

@ -1,22 +0,0 @@
#pragma once
#include "pch.h"
#include <fstream>
namespace ipc
{
class Writer
{
public:
Writer();
~Writer();
HRESULT start();
HRESULT add_path(LPCWSTR path);
void finish();
HANDLE get_read_handle();
private:
std::ofstream m_stream;
};
}

View File

@ -4,6 +4,8 @@
#include "../FileLocksmithLib/Constants.h"
#define BUFSIZE 4096 * 4
namespace FileLocksmith::Interop
{
public ref struct ProcessResult
@ -92,40 +94,79 @@ namespace FileLocksmith::Interop
return from_wstring_view(path_cpp);
}
static array<System::String^>^ ReadPathsFromFile()
static array<System::String ^>^ ReadPathsFromPipe(System::String^ pipeName)
{
std::ifstream stream(paths_file());
std::wstring pipe = from_system_string(pipeName);
HANDLE hStdin;
std::vector<std::wstring> result_cpp;
std::wstring line;
bool finished = false;
while (!finished)
if (pipe.size() > 0)
{
WCHAR ch{};
// We have to read data like this
if (!stream.read(reinterpret_cast<char*>(&ch), 2))
while (1)
{
finished = true;
}
else if (ch == L'\n')
{
if (line.empty())
hStdin = CreateFile(
pipe.c_str(), // pipe name
GENERIC_READ | GENERIC_WRITE, // read and write
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hStdin != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
auto error = GetLastError();
if (error != ERROR_PIPE_BUSY)
{
finished = true;
break;
}
else
if (!WaitNamedPipe(pipe.c_str(), 3))
{
result_cpp.push_back(line);
line = {};
printf("Could not open pipe: 20 second wait timed out.");
}
}
else
{
line += ch;
}
}
else
{
hStdin = GetStdHandle(STD_INPUT_HANDLE);
}
if (hStdin == INVALID_HANDLE_VALUE)
{
ExitProcess(1);
}
BOOL bSuccess;
WCHAR chBuf[BUFSIZE];
DWORD dwRead;
std::vector<std::wstring> result_cpp;
for (;;)
{
// Read from standard input and stop on error or no data.
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE * sizeof(wchar_t), &dwRead, NULL);
if (!bSuccess || dwRead == 0)
break;
std::wstring inputBatch{ chBuf, dwRead / sizeof(wchar_t) };
std::wstringstream ss(inputBatch);
std::wstring item;
wchar_t delimiter = '?';
while (std::getline(ss, item, delimiter))
{
result_cpp.push_back(item);
}
if (!bSuccess)
break;
}
CloseHandle(hStdin);
auto result = gcnew array<System::String ^>(static_cast<int>(result_cpp.size()));
for (int i = 0; i < result->Length; i++)

View File

@ -17,3 +17,4 @@
#include <set>
#include <algorithm>
#include <fstream>
#include <sstream>

View File

@ -9,6 +9,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FileLocksmith.Interop;
@ -78,7 +79,11 @@ namespace PowerToys.FileLocksmithUI.ViewModels
public MainViewModel()
{
paths = NativeMethods.ReadPathsFromFile();
var args = Environment.GetCommandLineArgs();
var pipeName = args.Where(s => s.Contains("\\\\.\\pipe\\")).FirstOrDefault();
MessageBox.Show(args.ToString());
paths = NativeMethods.ReadPathsFromPipe(pipeName);
Logger.LogInfo($"Starting FileLocksmith with {paths.Length} files selected.");
LoadProcessesCommand = new AsyncRelayCommand(LoadProcessesAsync);
}

View File

@ -151,7 +151,7 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
if (!CreatePipe(&hReadPipe, &hWritePipe, NULL, 0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;