2525
2626void Print (IShellItemArray* items)
2727{
28- if (!items)
29- return ;
30-
31- if (ShellItemArray{ items }.size () == 0 )
32- return ;
33-
34- for (auto item : ShellItemArray{ items })
35- {
36- OutputDebugString (item.GetDisplayName ());
37- OutputDebugString (L" \n " );
38- }
28+ #ifdef DEBUG
29+ auto const count = ShellItemArray{ items }.size ();
30+ OutputDebugString (std::to_wstring (count).data ());
31+ #endif
3932}
4033
4134class SubCommand ;
@@ -51,16 +44,6 @@ static std::wstring const& GetCurrentDllPath()
5144 return ret;
5245}
5346
54-
55- static void CheckResult (HRESULT hr, LPCWSTR prompt = nullptr )
56- {
57- if (FAILED (hr) && prompt)
58- {
59- MessageBox (NULL , prompt, L" " , 0 );
60- }
61- }
62-
63-
6447class __declspec (uuid(" 3282E233-C5D3-4533-9B25-44B8AAAFACFA" )) TestExplorerCommandRoot :
6548 public Microsoft::WRL::RuntimeClass<
6649 Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
@@ -87,17 +70,26 @@ class __declspec(uuid("3282E233-C5D3-4533-9B25-44B8AAAFACFA")) TestExplorerComma
8770 #pragma region IExplorerCommand
8871 HRESULT GetTitle ( IShellItemArray* items, PWSTR* name)
8972 {
73+ Print (items);
9074 return SHStrDup (CommandRootTitle, name);
9175 }
9276 HRESULT GetIcon (IShellItemArray* items, PWSTR* icon)
9377 {
78+ Print (items);
79+
9480 SHStrDup (std::format (L" {},{}" , GetCurrentDllPath (), -IDI_ICON1).data (), icon);
9581 return S_OK;
9682 }
97- HRESULT GetToolTip ( IShellItemArray*, PWSTR* infoTip) { *infoTip = nullptr ; return E_NOTIMPL; }
83+ HRESULT GetToolTip ( IShellItemArray* items, PWSTR* infoTip)
84+ {
85+ Print (items);
86+ *infoTip = nullptr ; return E_NOTIMPL;
87+ }
9888 HRESULT GetCanonicalName (GUID* guidCommandName) { *guidCommandName = GUID_NULL; return E_NOTIMPL; }
9989 HRESULT GetState ( IShellItemArray* selection, BOOL okToBeSlow, EXPCMDSTATE* cmdState)
10090 {
91+ Print (selection);
92+
10193 init (selection);
10294 if (m_hasInit && !ptrs.empty ())
10395 *cmdState = ECS_ENABLED;
@@ -114,6 +106,7 @@ class __declspec(uuid("3282E233-C5D3-4533-9B25-44B8AAAFACFA")) TestExplorerComma
114106
115107 HRESULT Invoke ( IShellItemArray* selection, IBindCtx*)
116108 {
109+ Print (selection);
117110 return E_NOTIMPL;
118111 }
119112
@@ -170,6 +163,101 @@ class __declspec(uuid("3282E233-C5D3-4533-9B25-44B8AAAFACFA")) TestExplorerComma
170163 #pragma endregion
171164};
172165
166+ #include < atlcomcli.h> // for COM smart pointers
167+ #include < atlbase.h> // for COM smart pointers
168+ #include < ShlObj_core.h>
169+ #include < shlobj.h>
170+
171+ void ThrowIfFailed (...) {}
172+ // Deleter for a PIDL allocated by the shell.
173+ struct CoTaskMemDeleter
174+ {
175+ void operator ()(ITEMIDLIST* pidl) const { ::CoTaskMemFree (pidl); }
176+ };
177+ using UniquePidlPtr = std::unique_ptr< ITEMIDLIST, CoTaskMemDeleter >;
178+ // Return value of GetCurrentExplorerFolders()
179+ struct ExplorerFolderInfo
180+ {
181+ HWND hwnd = nullptr ; // window handle of explorer
182+ UniquePidlPtr pidl; // PIDL that points to current folder
183+ };
184+
185+ // Get information about all currently open explorer windows.
186+ // Throws std::system_error exception to report errors.
187+ static std::vector< ExplorerFolderInfo > GetCurrentExplorerFolders ()
188+ {
189+ CComPtr< IShellWindows > pshWindows;
190+ ThrowIfFailed (
191+ pshWindows.CoCreateInstance (CLSID_ShellWindows),
192+ " Could not create instance of IShellWindows" );
193+
194+ long count = 0 ;
195+ ThrowIfFailed (
196+ pshWindows->get_Count (&count),
197+ " Could not get number of shell windows" );
198+
199+ std::vector< ExplorerFolderInfo > result;
200+ result.reserve (count);
201+
202+ for (long i = 0 ; i < count; ++i)
203+ {
204+ ExplorerFolderInfo info;
205+
206+ CComVariant vi{ i };
207+ CComPtr< IDispatch > pDisp;
208+ ThrowIfFailed (
209+ pshWindows->Item (vi, &pDisp),
210+ " Could not get item from IShellWindows" );
211+
212+ if (!pDisp)
213+ // Skip - this shell window was registered with a NULL IDispatch
214+ continue ;
215+
216+ CComQIPtr< IWebBrowserApp > pApp{ pDisp };
217+ if (!pApp)
218+ // This window doesn't implement IWebBrowserApp
219+ continue ;
220+
221+ // Get the window handle.
222+ pApp->get_HWND (reinterpret_cast <SHANDLE_PTR*>(&info.hwnd ));
223+
224+ CComQIPtr< IServiceProvider > psp{ pApp };
225+ if (!psp)
226+ // This window doesn't implement IServiceProvider
227+ continue ;
228+
229+ CComPtr< IShellBrowser > pBrowser;
230+ if (FAILED (psp->QueryService (SID_STopLevelBrowser, &pBrowser)))
231+ // This window doesn't provide IShellBrowser
232+ continue ;
233+
234+ CComPtr< IShellView > pShellView;
235+ if (FAILED (pBrowser->QueryActiveShellView (&pShellView)))
236+ // For some reason there is no active shell view
237+ continue ;
238+
239+ CComQIPtr< IFolderView > pFolderView{ pShellView };
240+ if (!pFolderView)
241+ // The shell view doesn't implement IFolderView
242+ continue ;
243+
244+ // Get the interface from which we can finally query the PIDL of
245+ // the current folder.
246+ CComPtr< IPersistFolder2 > pFolder;
247+ if (FAILED (pFolderView->GetFolder (IID_IPersistFolder2, (void **)&pFolder)))
248+ continue ;
249+
250+ LPITEMIDLIST pidl = nullptr ;
251+ if (SUCCEEDED (pFolder->GetCurFolder (&pidl)))
252+ {
253+ // Take ownership of the PIDL via std::unique_ptr.
254+ info.pidl = UniquePidlPtr{ pidl };
255+ result.push_back (std::move (info));
256+ }
257+ }
258+
259+ return result;
260+ }
173261
174262class SubCommand final :
175263 public Microsoft::WRL::RuntimeClass<
@@ -229,6 +317,8 @@ class SubCommand final :
229317
230318 virtual const EXPCMDSTATE State ( IShellItemArray* selection)
231319 {
320+ Print (selection);
321+
232322 switch (m_op)
233323 {
234324 case CopyOperation::Copy:
@@ -246,11 +336,13 @@ class SubCommand final :
246336 // IExplorerCommand
247337 HRESULT GetTitle ( IShellItemArray* items, PWSTR* name)
248338 {
339+ Print (items);
249340 SHStrDup (getName ().data (), name);
250341 return S_OK;
251342 }
252- HRESULT GetIcon (IShellItemArray*, PWSTR* icon)
343+ HRESULT GetIcon (IShellItemArray* items , PWSTR* icon)
253344 {
345+ Print (items);
254346 SHStrDup (std::format (
255347 L" {},{}" ,
256348 GetCurrentDllPath (),
@@ -259,28 +351,51 @@ class SubCommand final :
259351 );
260352 return S_OK;
261353 }
262- HRESULT GetToolTip (IShellItemArray*, PWSTR* infoTip) { *infoTip = nullptr ; return E_NOTIMPL; }
354+ HRESULT GetToolTip (IShellItemArray* items, PWSTR* infoTip)
355+ {
356+ Print (items);
357+ *infoTip = nullptr ;
358+ return E_NOTIMPL;
359+ }
263360 HRESULT GetCanonicalName (GUID* guidCommandName) { *guidCommandName = GUID_NULL; return S_OK; }
264361 HRESULT GetState (IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState)
265362 {
363+ Print (selection);
364+
266365 auto const state = State (selection);
267366 if (m_op == CopyOperation::Delete)
268367 *cmdState = ECS_DISABLED;
269368 else
270369 *cmdState = state == ECS_DISABLED ? ECS_HIDDEN : ECS_ENABLED;
271370 return S_OK;
272371 }
273- HRESULT Invoke ( IShellItemArray* selection, IBindCtx*) noexcept
372+
373+
374+ HRESULT Invoke ( IShellItemArray* selection, IBindCtx* ctx) noexcept
274375 {
275376 /*
276- if no files are selected, selection contains 1 element to the current invoked folder
377+ if no files are selected, selection contains 1 element to the current invoked folder (Windows 11 only)
378+ On Windows 10, `selection` is `nullptr`
277379 */
380+
381+ auto hwnd = GetForegroundWindow ();
278382 switch (m_op)
279383 {
280384 case CopyOperation::Copy:
281385 case CopyOperation::Move: return recordFilesImpl (selection);
282386 case CopyOperation::Paste:
283387 {
388+
389+ for (auto const & info : GetCurrentExplorerFolders ())
390+ {
391+ if (info.hwnd == hwnd)
392+ {
393+ TCHAR* ptr;
394+ SHGetNameFromIDList (info.pidl .get (), SIGDN_FILESYSPATH, &ptr);
395+ return callMainProgramImpl (ptr);
396+ }
397+ }
398+
284399 ShellItem psi{ selection ? ShellItemArray{ selection }[0 ] : m_parent };
285400 return callMainProgramImpl (psi.GetDisplayName ());
286401 }
@@ -337,17 +452,21 @@ BOOL APIENTRY DllMain( HMODULE hModule,
337452
338453void TestExplorerCommandRoot::init (IShellItemArray* items)
339454{
340- DWORD const count = items ? ShellItemArray{ items }.size () : 0 ;
341-
342- // MessageBox(NULL, std::to_wstring(count).data(), L"", 0);
455+ DWORD const count = ShellItemArray{ items }.size ();
456+ Print (items);
343457 if (!m_hasInit)
344458 {
345459 if (count == 0 && hasInvokedCopyOrCut ())
346460 {
461+ auto pasteCommand = Microsoft::WRL::Make<SubCommand>(CopyOperation::Paste);
347462 if (ptrs.empty ())
348- ptrs.push_back (Microsoft::WRL::Make<SubCommand>(CopyOperation::Paste));
463+ {
464+ ptrs.push_back (pasteCommand);
465+ }
349466 else
350- ptrs.insert (ptrs.begin () + 2 , Microsoft::WRL::Make<SubCommand>(CopyOperation::Paste));
467+ {
468+ ptrs.insert (ptrs.begin () + 2 , pasteCommand);
469+ }
351470 m_hasInit = true ;
352471 }
353472 }
@@ -363,11 +482,8 @@ void TestExplorerCommandRoot::init(IShellItemArray* items)
363482 }
364483
365484 if (!ptrs.empty () && std::none_of (ptrs.begin (), ptrs.end (), [items](Microsoft::WRL::ComPtr<SubCommand> const & subcommand) {
366- OutputDebugString (subcommand->getName ().data ());
367- OutputDebugString (std::to_wstring (subcommand->State (nullptr ) == ECS_ENABLED).data ());
368- OutputDebugString (L" \n " );
369485 return subcommand->State (nullptr ) == ECS_ENABLED;
370- }))
486+ }))
371487 {
372488 ptrs.emplace_back (Microsoft::WRL::Make<SubCommand>(CopyOperation::Paste));
373489 ptrs.back ()->SetParentForPasteForWindows10 (ShellItemArray{ items }.front ().GetPtr ());
0 commit comments