From 58015feb3ac7b524466d7a9ae20ec298667b67a9 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:51:33 +0100 Subject: [PATCH] [PowerRename] Add PowerRename to directory background context menu (#24522) * Add PowerRename to directory background context menu * Fix analyzer error * Add more checks --- .github/actions/spell-check/expect.txt | 2 +- installer/PowerToysSetup/PowerRename.wxs | 3 ++ .../PowerRenameContextMenu/dllmain.cpp | 12 ++--- .../powerrename/dll/PowerRenameExt.cpp | 44 +++++++++++-------- src/modules/powerrename/lib/Helpers.cpp | 2 +- 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index cc620ed98f..94fb0a8903 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -742,7 +742,7 @@ IDD IDesktop IDirect idl -IDLIST +idlist IDOn IDR idx diff --git a/installer/PowerToysSetup/PowerRename.wxs b/installer/PowerToysSetup/PowerRename.wxs index 6239033894..56de970b29 100644 --- a/installer/PowerToysSetup/PowerRename.wxs +++ b/installer/PowerToysSetup/PowerRename.wxs @@ -28,6 +28,9 @@ + + + diff --git a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp index 6bc8b33ebb..028dc2c31b 100644 --- a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp +++ b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp @@ -93,12 +93,6 @@ public: { *cmdState = ECS_ENABLED; - // We've observed that it's possible that a null gets passed instead of an empty array. Just don't show the context menu in this case. - if (nullptr == selection) { - *cmdState = ECS_HIDDEN; - return S_OK; - } - if (!CSettingsInstance().GetEnabled()) { *cmdState = ECS_HIDDEN; @@ -112,6 +106,12 @@ public: return S_OK; } + // When right clicking directory background, selection is empty. This prevents checking if there + // are renamable items, but internal PowerRename logic will prevent renaming non-renamable items anyway. + if (nullptr == selection) { + return S_OK; + } + // Check if at least one of the selected items is actually renamable. if (!ShellItemArrayContainsRenamableItem(selection)) { diff --git a/src/modules/powerrename/dll/PowerRenameExt.cpp b/src/modules/powerrename/dll/PowerRenameExt.cpp index b4012a1899..95b42089a4 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.cpp +++ b/src/modules/powerrename/dll/PowerRenameExt.cpp @@ -46,14 +46,26 @@ HRESULT CPowerRenameMenu::s_CreateInstance(_In_opt_ IUnknown*, _In_ REFIID riid, } // IShellExtInit -HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject* pdtobj, HKEY) +HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE idlist, _In_ IDataObject* pdtobj, HKEY) { // Check if we have disabled ourselves if (!CSettingsInstance().GetEnabled()) return E_FAIL; // Cache the data object to be used later - m_spdo = pdtobj; + if (idlist != NULL) + { + CComPtr spsia; + if (SUCCEEDED(SHCreateShellItemArrayFromIDLists(1, &idlist, &spsia)) && spsia != NULL) + { + spsia->BindToHandler(NULL, BHID_DataObject, IID_IDataObject, reinterpret_cast(&m_spdo)); + } + } + else + { + m_spdo = pdtobj; + } + return S_OK; } @@ -124,7 +136,7 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr HRESULT hr = E_FAIL; if (CSettingsInstance().GetEnabled() && - (IS_INTRESOURCE(pici->lpVerb)) && + pici && (IS_INTRESOURCE(pici->lpVerb)) && (LOWORD(pici->lpVerb) == 0)) { Trace::Invoked(); @@ -163,14 +175,7 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr startupInfo.cb = sizeof(STARTUPINFO); startupInfo.hStdInput = hReadPipe; startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - if (pici) - { - startupInfo.wShowWindow = static_cast(pici->nShow); - } - else - { - startupInfo.wShowWindow = SW_SHOWNORMAL; - } + startupInfo.wShowWindow = static_cast(pici->nShow); PROCESS_INFORMATION processInformation; @@ -201,15 +206,18 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr // 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()) + if (m_spdo) { - CString fileName(i.CurrentItem()); - // File name can't contain '?' - fileName.Append(_T("?")); + // Stream the input files + HDropIterator i(m_spdo); + 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.Write(fileName, fileName.GetLength() * sizeof(TCHAR)); + } } } else diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp index 8ef61366b1..ecd823e4d7 100644 --- a/src/modules/powerrename/lib/Helpers.cpp +++ b/src/modules/powerrename/lib/Helpers.cpp @@ -551,7 +551,7 @@ bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource) { bool hasRenamable = false; CComPtr spsia; - if (SUCCEEDED(GetShellItemArrayFromDataObject(dataSource, &spsia))) + if (dataSource && SUCCEEDED(GetShellItemArrayFromDataObject(dataSource, &spsia))) { CComPtr spesi; if (SUCCEEDED(spsia->EnumItems(&spesi)))