[PowerRename] Fluent UX (#13678)

* PowerRename new UI

* Add scrollviewer

* Don't deploy PowerRenameUI_new

* Visual updates

* Visual updates

* Updates

* Update Resources.resw

* Added docs button

* Update MainWindow.xaml

* Wire Docs button

* RegEx -> regular expressions

* Update Show only renamed list on search/replace text changed

* Update Show only renamed list on search/replace text changed - proper fix
Set searchTerm to NULL when cleared - fix Show only renamed files on clear searchTerm

* Files/folders input error handling

* Fix renaming with keeping UI window opened

After renaming folder, all of it's children need path update.
Without path update, further renaming of children items would
fail.

* Update only children, not all items with greater depth

* Fix dictionary false positives

* Remove .NET dep

* Rename PowerRenameUI_new to PowerRenameUILib
Rename executable PowerRenameUIHost to PowerRename

Co-authored-by: Laute <Niels.Laute@philips.com>
This commit is contained in:
Stefan Markovic
2021-10-25 15:40:19 +02:00
committed by GitHub
parent ce942b0585
commit 5cfbd72fa8
128 changed files with 5759 additions and 8196 deletions

View File

@@ -1,8 +1,5 @@
#include "pch.h"
#include "PowerRenameExt.h"
#include <PowerRenameUI.h>
#include <PowerRenameItem.h>
#include <PowerRenameManager.h>
#include <trace.h>
#include <Helpers.h>
#include <common/themes/icon_helpers.h>
@@ -11,6 +8,7 @@
#include <common/utils/resources.h>
#include <common/utils/process_path.h>
#include <common/utils/HDropIterator.h>
extern HINSTANCE g_hInst;
@@ -116,6 +114,11 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
}
HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
{
return RunPowerRename(pici, nullptr);
}
HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemArray* psiItemArray)
{
HRESULT hr = E_FAIL;
@@ -124,84 +127,119 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
(LOWORD(pici->lpVerb) == 0))
{
Trace::Invoked();
InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct;
hr = E_OUTOFMEMORY;
if (pInvokeData)
// Set the application path based on the location of the dll
std::wstring path = get_module_folderpath(g_hInst);
path = path + L"\\PowerRename.exe";
LPTSTR lpApplicationName = (LPTSTR)path.c_str();
// 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))
{
pInvokeData->hwndParent = pici->hwnd;
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &(pInvokeData->pstrm));
if (SUCCEEDED(hr))
{
hr = SHCreateThread(s_PowerRenameUIThreadProc, pInvokeData, CTF_COINIT | CTF_PROCESS_REF, nullptr) ? S_OK : E_FAIL;
if (FAILED(hr))
{
pInvokeData->pstrm->Release(); // if we failed to create the thread, then we must release the stream
}
}
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
CAtlFile writePipe(hWritePipe);
if (FAILED(hr))
CString commandLine;
commandLine.Format(_T("\"%s\""), lpApplicationName);
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;
if (pici)
{
startupInfo.wShowWindow = pici->nShow;
}
else
{
startupInfo.wShowWindow = SW_SHOWNORMAL;
}
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;
}
// psiItemArray is NULL if called from InvokeCommand. This part is used for the MSI installer. It is not NULL if it is called from Invoke (MSIX).
if (!psiItemArray)
{
// Stream the input files
HDropIterator i(m_spdo);
for (i.First(); !i.IsDone(); i.Next())
{
delete pInvokeData;
CString fileName(i.CurrentItem());
// File name can't contain '?'
fileName.Append(_T("?"));
writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR));
}
}
Trace::InvokedRet(hr);
else
{
//m_pdtobj will be NULL when invoked from the MSIX build as Initialize is never called (IShellExtInit functions aren't called in case of MSIX).
DWORD fileCount = 0;
// Gets the list of files currently selected using the IShellItemArray
psiItemArray->GetCount(&fileCount);
// Iterate over the list of files
for (DWORD i = 0; i < fileCount; i++)
{
IShellItem* shellItem;
psiItemArray->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(hr);
return hr;
}
DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
{
InvokeStruct* pInvokeData = static_cast<InvokeStruct*>(pData);
CComPtr<IUnknown> dataSource;
HRESULT hr = CoGetInterfaceAndReleaseStream(pInvokeData->pstrm, IID_PPV_ARGS(&dataSource));
if (SUCCEEDED(hr))
{
// Create the rename manager
CComPtr<IPowerRenameManager> spsrm;
hr = CPowerRenameManager::s_CreateInstance(&spsrm);
if (SUCCEEDED(hr))
{
// Create the factory for our items
CComPtr<IPowerRenameItemFactory> spsrif;
hr = CPowerRenameItem::s_CreateInstance(nullptr, IID_PPV_ARGS(&spsrif));
if (SUCCEEDED(hr))
{
// Pass the factory to the manager
hr = spsrm->PutRenameItemFactory(spsrif);
if (SUCCEEDED(hr))
{
// Create the rename UI instance and pass the rename manager
CComPtr<IPowerRenameUI> spsrui;
hr = CPowerRenameUI::s_CreateInstance(spsrm, dataSource, false, &spsrui);
if (SUCCEEDED(hr))
{
IDataObject* dummy;
// If we're running on a local COM server, we need to decrement module refcount, which was previously incremented in CPowerRenameMenu::Invoke.
if (SUCCEEDED(dataSource->QueryInterface(IID_IShellItemArray, reinterpret_cast<void**>(&dummy))))
{
ModuleRelease();
}
// Call blocks until we are done
spsrui->Show(pInvokeData->hwndParent);
spsrui->Close();
}
}
}
// Need to call shutdown to break circular dependencies
spsrm->Shutdown();
}
}
delete pInvokeData;
Trace::UIShownRet(hr);
return 0;
}
HRESULT __stdcall CPowerRenameMenu::GetTitle(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszName)
{
return SHStrDup(app_name.c_str(), ppszName);
@@ -261,7 +299,7 @@ HRESULT __stdcall CPowerRenameMenu::Invoke(IShellItemArray* psiItemArray, IBindC
}
// Prevent Shutting down before PowerRenameUI is created
ModuleAddRef();
hr = SHCreateThread(s_PowerRenameUIThreadProc, pInvokeData, CTF_COINIT | CTF_PROCESS_REF, nullptr) ? S_OK : E_FAIL;
hr = RunPowerRename(nullptr, psiItemArray);
}
Trace::InvokedRet(hr);
return S_OK;

View File

@@ -49,6 +49,8 @@ public:
return E_NOTIMPL;
}
HRESULT RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemArray* psiItemArray);
// Inherited via IExplorerCommand
virtual HRESULT __stdcall GetTitle(IShellItemArray* psiItemArray, LPWSTR* ppszName) override;
virtual HRESULT __stdcall GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon) override;
@@ -60,7 +62,6 @@ public:
virtual HRESULT __stdcall EnumSubCommands(IEnumExplorerCommand** ppEnum) override;
static HRESULT s_CreateInstance(_In_opt_ IUnknown* punkOuter, _In_ REFIID riid, _Outptr_ void** ppv);
static DWORD WINAPI s_PowerRenameUIThreadProc(_In_ void* pData);
static bool SetEnabled(_In_ bool enabled);
static bool IsEnabled();

View File

@@ -21,7 +21,7 @@
<AdditionalIncludeDirectories>..\lib\;..\ui\;..\;..\..\..\;..\..\..\common\telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Pathcch.lib;comctl32.lib;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>PowerRenameExt.def</ModuleDefinitionFile>
<DelayLoadDLLs>gdi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
@@ -60,9 +60,6 @@
<ProjectReference Include="..\lib\PowerRenameLib.vcxproj">
<Project>{51920f1f-c28c-4adf-8660-4238766796c2}</Project>
</ProjectReference>
<ProjectReference Include="..\ui\PowerRenameUI.vcxproj">
<Project>{0e072714-d127-460b-afad-b4c40b412798}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx" />

View File

@@ -10,6 +10,9 @@
#include <unknwn.h>
#include <shlwapi.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlfile.h>
#include <atlstr.h>
#include <Shobjidl.h>
#include <Shlobj.h>
#include "CLSID.h"