Skip to content

Commit a279f3f

Browse files
committed
Fix unable to launch copy on Windows 10.
1 parent bb4876f commit a279f3f

File tree

4 files changed

+176
-37
lines changed

4 files changed

+176
-37
lines changed

FastCopy/FileContextMenu.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,26 @@
77
#include <Windows.h>
88
#include <gdiplus.h>
99

10+
class GdiplusInitializer
11+
{
12+
public:
13+
GdiplusInitializer()
14+
{
15+
Gdiplus::GdiplusStartup(&gdiplusToken, &input, nullptr);
16+
}
17+
18+
~GdiplusInitializer()
19+
{
20+
Gdiplus::GdiplusShutdown(gdiplusToken);
21+
}
22+
private:
23+
Gdiplus::GdiplusStartupInput input;
24+
ULONG_PTR gdiplusToken;
25+
};
26+
1027
winrt::Microsoft::UI::Xaml::Media::ImageSource FileContextMenu::getIconFromWin32Menu(HBITMAP menuItemInfoBitmap)
1128
{
29+
static GdiplusInitializer s_gdiPlusInitializer;
1230
if (!menuItemInfoBitmap)
1331
return nullptr;
1432

@@ -62,6 +80,9 @@ winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItem FileContextMenu::makeMenuFl
6280

6381
void FileContextMenu::ShowAt(winrt::Microsoft::UI::Xaml::Controls::MenuFlyout& flyout)
6482
{
83+
if (flyout.Items().Size() != 0)
84+
return;
85+
6586
winrt::com_ptr<IShellItem> item = CreateItemFromParsingName(m_path.data()), folderItem;
6687
item->GetParent(folderItem.put());
6788

FastCopy/Package.appxmanifest

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<Identity
1414
Name="FastCopy"
1515
Publisher="CN=Peter"
16-
Version="1.1.0.0" />
16+
Version="1.2.0.0" />
1717

1818
<Properties>
1919
<DisplayName>FastCopy</DisplayName>

FastCopyShellExtension/Recorder.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ static auto GetLocalDataFolder()
2424

2525
static auto GetTimeString()
2626
{
27-
auto ret = std::format(L"{}", std::chrono::zoned_time{ std::chrono::current_zone(), std::chrono::system_clock::now() }.get_local_time());
27+
//std::chrono::current_zone() gives exceptions on Windows 10, 17763 with MSVC cl.exe version 19.36.32535
28+
//auto ret = std::format(L"{}", std::chrono::zoned_time{ std::chrono::current_zone(), std::chrono::system_clock::now() }.get_local_time());
29+
auto ret = std::format(L"{}", std::chrono::system_clock::now());
2830
std::ranges::replace_if(ret, [](auto c) {return c == L'.' || c == L':'; }, L'-');
2931
return ret;
3032
}

FastCopyShellExtension/dllmain.cpp

Lines changed: 151 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,10 @@
2525

2626
void 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

4134
class 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-
6447
class __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

174262
class 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

338453
void 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

Comments
 (0)