diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj index f1d35839c0..918ca41d79 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj @@ -1,5 +1,6 @@ + @@ -120,11 +121,14 @@ MakeAppx.exe pack /d . /p $(OutDir)FileLocksmithContextMenuPackage.msix /nv + 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}. + + \ No newline at end of file diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters index 4e95cc85ee..b30764cfb8 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters @@ -37,6 +37,31 @@ Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp b/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp index 1abf0e947b..50dd743ac5 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp @@ -5,17 +5,20 @@ #include #include -#include "FileLocksmithLib/IPC.h" #include "FileLocksmithLib/Settings.h" #include "FileLocksmithLib/Trace.h" +#include +#include #include #include #include +#include #include #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(&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(&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 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) diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/packages.config b/src/modules/FileLocksmith/FileLocksmithContextMenu/packages.config index 6199e2345c..ff4b059648 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/packages.config +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp index e0eee3d781..2569d4fa4b 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp @@ -11,6 +11,7 @@ #include #include #include +#include // 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(&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(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; } \ No newline at end of file diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h index d59f266157..1e19290a5d 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h @@ -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 m_ref_count = 1; IDataObject* m_data_obj = NULL; diff --git a/src/modules/FileLocksmith/FileLocksmithExt/pch.h b/src/modules/FileLocksmith/FileLocksmithExt/pch.h index b083a02c0e..327e72645f 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/pch.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/pch.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include // C++ Standard library #include diff --git a/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj b/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj index 3e3ecc8756..e1230148f8 100644 --- a/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj @@ -65,14 +65,12 @@ - - diff --git a/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj.filters b/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj.filters index 8b223a619b..db7afb49eb 100644 --- a/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj.filters +++ b/src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj.filters @@ -27,9 +27,6 @@ Header Files - - Header Files - Header Files @@ -41,9 +38,6 @@ Source Files - - Source Files - Source Files diff --git a/src/modules/FileLocksmith/FileLocksmithLib/IPC.cpp b/src/modules/FileLocksmith/FileLocksmithLib/IPC.cpp deleted file mode 100644 index c99a3c353a..0000000000 --- a/src/modules/FileLocksmith/FileLocksmithLib/IPC.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "pch.h" - -#include "IPC.h" -#include "Constants.h" - -#include - -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(path), length * sizeof(WCHAR))) - { - return E_FAIL; - } - - WCHAR line_break = L'\n'; - if (!m_stream.write(reinterpret_cast(&line_break), sizeof(WCHAR))) - { - return E_FAIL; - } - - return S_OK; - } - - void Writer::finish() - { - add_path(L""); - m_stream.close(); - } -} diff --git a/src/modules/FileLocksmith/FileLocksmithLib/IPC.h b/src/modules/FileLocksmith/FileLocksmithLib/IPC.h deleted file mode 100644 index 44ae5f451c..0000000000 --- a/src/modules/FileLocksmith/FileLocksmithLib/IPC.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "pch.h" - -#include - -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; - }; -} diff --git a/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp b/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp index e208adafcf..ee52d0ade4 100644 --- a/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp +++ b/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp @@ -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^ ReadPathsFromFile() + static array^ ReadPathsFromPipe(System::String^ pipeName) { - std::ifstream stream(paths_file()); + std::wstring pipe = from_system_string(pipeName); + HANDLE hStdin; - std::vector 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(&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 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(static_cast(result_cpp.size())); for (int i = 0; i < result->Length; i++) diff --git a/src/modules/FileLocksmith/FileLocksmithLibInterop/pch.h b/src/modules/FileLocksmith/FileLocksmithLibInterop/pch.h index 5bfe0c9082..e2535651f4 100644 --- a/src/modules/FileLocksmith/FileLocksmithLibInterop/pch.h +++ b/src/modules/FileLocksmith/FileLocksmithLibInterop/pch.h @@ -17,3 +17,4 @@ #include #include #include +#include diff --git a/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs b/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs index d473a05c58..48090bd75d 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs +++ b/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs @@ -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); } diff --git a/src/modules/powerrename/dll/PowerRenameExt.cpp b/src/modules/powerrename/dll/PowerRenameExt.cpp index 9e9f2910e7..9216c93599 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.cpp +++ b/src/modules/powerrename/dll/PowerRenameExt.cpp @@ -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;