Skip to content

Commit 01fdd92

Browse files
committed
Refactor IShellItem, implement ContextMenu click handler in duplicate file view, add Readme about installation
1 parent 888d2a3 commit 01fdd92

File tree

14 files changed

+166
-95
lines changed

14 files changed

+166
-95
lines changed

FastCopy/CopyDialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ namespace winrt::FastCopy::implementation
151151

152152
void CopyDialog::ShowMenu(winrt::hstring path, winrt::Microsoft::UI::Xaml::Controls::MenuFlyout flyout)
153153
{
154-
FileContextMenu(path).ShowAt(flyout);
154+
std::make_shared<FileContextMenu>(path)->ShowAt(flyout);
155155
}
156156

157157
}

FastCopy/FileContextMenu.cpp

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,62 @@
77
#include <Windows.h>
88
#include <gdiplus.h>
99

10-
void FileContextMenu::ShowAt(winrt::Microsoft::UI::Xaml::Controls::MenuFlyout& flyout)
10+
winrt::Microsoft::UI::Xaml::Media::ImageSource FileContextMenu::getIconFromWin32Menu(HBITMAP menuItemInfoBitmap)
1111
{
12-
winrt::com_ptr<IShellItem> item, folderItem;
13-
SHCreateItemFromParsingName(m_path.data(), NULL, IID_PPV_ARGS(item.put()));
12+
if (!menuItemInfoBitmap)
13+
return nullptr;
14+
15+
auto pbitmap = Gdiplus::Bitmap::FromHBITMAP(menuItemInfoBitmap, NULL);
16+
INT const height = pbitmap->GetHeight();
17+
INT const width = pbitmap->GetWidth();
18+
19+
Gdiplus::Color c{Gdiplus::Color::MakeARGB(255, 0, 0, 0)};
20+
21+
Gdiplus::Rect bmBounds{0, 0, (INT)width, (INT)height};
22+
Gdiplus::BitmapData data{};
23+
pbitmap->LockBits(&bmBounds, Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, &data);
24+
25+
Gdiplus::Bitmap tmp{width, height, data.Stride, PixelFormat32bppARGB, (BYTE*)data.Scan0};
26+
Gdiplus::Bitmap clone{width, height, PixelFormat32bppARGB};
1427

28+
Gdiplus::Graphics* gr = Gdiplus::Graphics::FromImage(&clone);
29+
gr->DrawImage(&tmp, bmBounds);
30+
31+
HBITMAP hbitmap{};
32+
clone.GetHBITMAP(c, &hbitmap);
33+
return HBitmapToWriteableBitmap(hbitmap);
34+
}
35+
36+
winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItem FileContextMenu::makeMenuFlyout(MENUITEMINFO const& menuItemInfo)
37+
{
38+
winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItem item;
39+
item.Text(menuItemInfo.dwTypeData);
40+
41+
item.Click([thisPtr = shared_from_this()](auto self, ...) {
42+
int const id = winrt::unbox_value<decltype(m_menuData.size())>(self.as<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItem>().Tag());
43+
auto const& data = thisPtr->m_menuData[id];
44+
CMINVOKECOMMANDINFOEX invokeInfo
45+
{
46+
.cbSize = sizeof(invokeInfo),
47+
.lpVerb = reinterpret_cast<decltype(invokeInfo.lpVerb)>(MAKEINTRESOURCEW(data.menuId)),
48+
.nShow = SW_SHOWNORMAL,
49+
};
50+
thisPtr->m_menu->InvokeCommand(reinterpret_cast<CMINVOKECOMMANDINFO*>(&invokeInfo));
51+
});
52+
53+
if (auto source = getIconFromWin32Menu(menuItemInfo.hbmpItem))
54+
{
55+
winrt::Microsoft::UI::Xaml::Controls::ImageIcon icon;
56+
icon.Source(source);
57+
item.Icon(icon);
58+
}
59+
item.Tag(winrt::box_value(m_menuData.size()));
60+
return item;
61+
}
62+
63+
void FileContextMenu::ShowAt(winrt::Microsoft::UI::Xaml::Controls::MenuFlyout& flyout)
64+
{
65+
winrt::com_ptr<IShellItem> item = CreateItemFromParsingName(m_path.data()), folderItem;
1566
item->GetParent(folderItem.put());
1667

1768
winrt::com_ptr<IShellFolder> folder;
@@ -25,12 +76,10 @@ void FileContextMenu::ShowAt(winrt::Microsoft::UI::Xaml::Controls::MenuFlyout& f
2576

2677
LPCITEMIDLIST idl[2]{ last };
2778

28-
winrt::com_ptr<IContextMenu> menu;
29-
auto hr = folder->GetUIObjectOf(NULL, 1, &idl[0], IID_IContextMenu, nullptr, menu.put_void());
79+
auto hr = folder->GetUIObjectOf(NULL, 1, &idl[0], IID_IContextMenu, nullptr, m_menu.put_void());
3080

3181
auto hMenu = CreatePopupMenu();
32-
//menu->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_OPTIMIZEFORINVOKE);
33-
menu->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_NORMAL);
82+
m_menu->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_NORMAL);
3483

3584
auto const itemCount = GetMenuItemCount(hMenu);
3685
MENUITEMINFO menuItemInfo{
@@ -43,43 +92,12 @@ void FileContextMenu::ShowAt(winrt::Microsoft::UI::Xaml::Controls::MenuFlyout& f
4392
menuItemInfo.dwTypeData = buffer;
4493
menuItemInfo.cch = std::size(buffer);
4594
GetMenuItemInfo(hMenu, i, true, &menuItemInfo);
46-
if (!std::wstring_view{buffer}.empty() && menu)
95+
if (!std::wstring_view{buffer}.empty() && m_menu)
4796
{
48-
winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItem item;
49-
item.Text(buffer);
50-
51-
if (menuItemInfo.hbmpItem)
52-
{
53-
winrt::Microsoft::UI::Xaml::Controls::ImageIcon icon;
54-
{
55-
auto pbitmap = Gdiplus::Bitmap::FromHBITMAP(menuItemInfo.hbmpItem, NULL);
56-
INT const height = pbitmap->GetHeight();
57-
INT const width = pbitmap->GetWidth();
58-
59-
Gdiplus::Color c{Gdiplus::Color::MakeARGB(255, 0, 0, 0)};
60-
61-
Gdiplus::Rect bmBounds{0, 0, (INT)width, (INT)height};
62-
Gdiplus::BitmapData data{};
63-
pbitmap->LockBits(&bmBounds, Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, &data);
64-
65-
Gdiplus::Bitmap tmp{width, height, data.Stride, PixelFormat32bppARGB, (BYTE*)data.Scan0};
66-
Gdiplus::Bitmap clone{width, height, PixelFormat32bppARGB};
67-
68-
Gdiplus::Graphics* gr = Gdiplus::Graphics::FromImage(&clone);
69-
gr->DrawImage(&tmp, bmBounds);
70-
71-
HBITMAP hbitmap{};
72-
clone.GetHBITMAP(c, &hbitmap);
73-
if (auto source = HBitmapToWriteableBitmap(hbitmap); source != nullptr)
74-
{
75-
icon.Source(source);
76-
item.Icon(icon);
77-
}
78-
}
79-
}
80-
flyout.Items().Append(item);
97+
flyout.Items().Append(makeMenuFlyout(menuItemInfo));
98+
std::wstring command(100, {});
99+
m_menu->GetCommandString(menuItemInfo.wID - 1, GCS_VERB, nullptr, reinterpret_cast<char*>(command.data()), command.size());
100+
m_menuData.push_back(MenuData{ .menuId = menuItemInfo.wID - 1, .verb = std::move(command) });
81101
}
82-
OutputDebugString(buffer);
83-
OutputDebugString(L"\n");
84102
}
85103
}

FastCopy/FileContextMenu.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@
33
#include <winrt/Microsoft.UI.Xaml.Controls.h>
44
#include <vector>
55
#include "PathUtils.h"
6+
#include <memory>
67

78
struct MenuData
89
{
9-
int menuId;
10+
11+
unsigned int menuId;
1012
std::wstring verb;
1113
};
1214

13-
class FileContextMenu
15+
class FileContextMenu : public std::enable_shared_from_this<FileContextMenu>
1416
{
1517
std::vector<MenuData> m_menuData;
1618
std::wstring m_path;
1719
void onMenuItemClick() {}
20+
winrt::com_ptr<IContextMenu> m_menu;
21+
22+
static winrt::Microsoft::UI::Xaml::Media::ImageSource getIconFromWin32Menu(HBITMAP menuItemInfoBitmap);
23+
winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItem makeMenuFlyout(MENUITEMINFO const& menuItemInfo);
1824
public:
1925
FileContextMenu(winrt::hstring path) : m_path{ ToBackslash(path)} {}
2026

FastCopy/FileInfoViewModel.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,22 @@ namespace winrt::FastCopy::implementation
5757

5858
static std::wstring GetShellPropStringFromPath(LPCWSTR pPath, PROPERTYKEY const& key)
5959
{
60-
// Use CComPtr to automatically release the IShellItem2 interface when the function returns
61-
// or an exception is thrown.
62-
winrt::com_ptr<IShellItem2> pItem;
63-
HRESULT hr = SHCreateItemFromParsingName(pPath, nullptr, IID_PPV_ARGS(pItem.put()));
64-
if (FAILED(hr))
65-
throw std::system_error(hr, std::system_category(), "SHCreateItemFromParsingName() failed");
60+
try
61+
{
62+
// Use CComPtr to automatically release the IShellItem2 interface when the function returns
63+
// or an exception is thrown.
64+
winrt::com_ptr<IShellItem2> pItem = CreateItemFromParsingName<IShellItem2>(pPath);
6665

67-
// Use CComHeapPtr to automatically release the string allocated by the shell when the function returns
68-
// or an exception is thrown (calls CoTaskMemFree).
69-
CComHeapPtr<WCHAR> pValue;
70-
hr = pItem->GetString(key, &pValue);
71-
if (FAILED(hr))
72-
return {};
66+
// Use CComHeapPtr to automatically release the string allocated by the shell when the function returns
67+
// or an exception is thrown (calls CoTaskMemFree).
68+
CComHeapPtr<WCHAR> pValue;
69+
winrt::check_hresult(pItem->GetString(key, &pValue));
7370

74-
// Copy to wstring for convenience
75-
return std::wstring(pValue);
71+
// Copy to wstring for convenience
72+
return std::wstring(pValue);
73+
}
74+
catch (...) {}
75+
return {};
7676
}
7777

7878
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> FileInfoViewModel::tooltipInfo()

FastCopy/ImageUtils.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ HICON GetHIconFromFile(std::wstring_view path)
3030

3131
HBITMAP GetHBitmapFromFile(std::wstring_view path)
3232
{
33-
winrt::com_ptr<IShellItem> item;
34-
SHCreateItemFromParsingName(ToBackslash(path).data(), NULL, IID_PPV_ARGS(item.put()));
33+
winrt::com_ptr<IShellItem> item = CreateItemFromParsingName(ToBackslash(path).data());
3534
HBITMAP bitmap{};
3635
item.as<IShellItemImageFactory>()->GetImage({ 96, 96 }, SIIGBF_BIGGERSIZEOK, &bitmap);
3736
return bitmap;

FastCopy/MainPage.xaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,9 @@
569569
</local:SettingContainer>
570570

571571
<local:SettingContainer x:Uid="MultipleInstanceBehaviorSettingItem">
572+
<ToolTipService.ToolTip>
573+
<ToolTip x:Uid="UnderDevelopmentTooltip"/>
574+
</ToolTipService.ToolTip>
572575
<local:SettingContainer.Symbol>
573576
<FontIcon Glyph="{StaticResource MultipleWindowGlyph}"/>
574577
</local:SettingContainer.Symbol>

FastCopy/PathUtils.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include <string_view>
33
#include <string>
4+
#include <ShObjIdl_core.h>
45

56
/**
67
* @brief Convert forward slash to back slash, returning a new string
@@ -11,3 +12,15 @@ inline std::wstring ToBackslash(std::wstring_view source)
1112
std::transform(source.begin(), source.end(), ret.begin(), [](wchar_t c) {return c == L'/' ? L'\\' : c; });
1213
return ret;
1314
}
15+
16+
/**
17+
* @brief Returns a `IShellItem`(default) or `IShellItem2` from `path`
18+
* @warning Throws if operation failed
19+
*/
20+
template<typename ShellItemInterface = IShellItem>
21+
auto CreateItemFromParsingName(wchar_t const* path)
22+
{
23+
winrt::com_ptr<ShellItemInterface> item;
24+
winrt::check_hresult(SHCreateItemFromParsingName(path, NULL, IID_PPV_ARGS(item.put())));
25+
return item;
26+
}

FastCopy/SegmentedButton.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#endif
66

77

8-
98
namespace winrt::FastCopy::implementation
109
{
1110
winrt::Microsoft::UI::Xaml::DependencyProperty SegmentedButton::m_content1 =
@@ -73,5 +72,15 @@ namespace winrt::FastCopy::implementation
7372
}
7473
void SegmentedButton::Selection(int value)
7574
{
75+
if (value == 0)
76+
{
77+
Button1().IsChecked(true);
78+
Button2().IsChecked(false);
79+
}
80+
else if(value == 1)
81+
{
82+
Button1().IsChecked(false);
83+
Button2().IsChecked(true);
84+
}
7685
}
7786
}

FastCopy/ShellCopy.cpp

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,18 @@ namespace ShellCopy
1616

1717
bool Move(std::wstring_view source, std::wstring_view destination)
1818
{
19-
winrt::com_ptr<IFileOperation> pfo;
20-
auto hr = CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(pfo.put()));
21-
22-
if (!SUCCEEDED(hr))
23-
return false;
24-
25-
if (!SUCCEEDED(pfo->SetOperationFlags(FOF_NO_UI)))
26-
return false;
27-
28-
winrt::com_ptr<IShellItem> psiFrom, psiDest;
29-
std::wstring from = ToBackslash(source);
30-
if (hr = SHCreateItemFromParsingName(from.data(), NULL, IID_PPV_ARGS(psiFrom.put())); !SUCCEEDED(hr))
31-
return false;
32-
33-
std::wstring to = ToBackslash(destination);
34-
if (hr = SHCreateItemFromParsingName(to.data(), NULL, IID_PPV_ARGS(psiDest.put())); !SUCCEEDED(hr))
35-
return false;
36-
37-
if (!SUCCEEDED(pfo->MoveItem(psiFrom.get(), psiDest.get(), nullptr, nullptr)))
38-
return false;
39-
40-
41-
if (!SUCCEEDED(pfo->PerformOperations()))
42-
return false;
19+
try
20+
{
21+
winrt::com_ptr<IFileOperation> pfo;
22+
winrt::check_hresult(CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(pfo.put())));
23+
winrt::check_hresult(pfo->SetOperationFlags(FOF_NO_UI));
24+
25+
winrt::com_ptr<IShellItem> psiFrom = CreateItemFromParsingName(ToBackslash(source).data()),
26+
psiDest = CreateItemFromParsingName(ToBackslash(destination).data());
27+
winrt::check_hresult(pfo->MoveItem(psiFrom.get(), psiDest.get(), nullptr, nullptr));
28+
winrt::check_hresult(pfo->PerformOperations());
29+
}
30+
catch (...) { return false; }
4331

4432
return true;
4533
}

FastCopy/Strings/en-US/Resources.resw

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@
213213
<data name="ToText.Text" xml:space="preserve">
214214
<value>to</value>
215215
</data>
216+
<data name="UnderDevelopmentTooltip.Content" xml:space="preserve">
217+
<value>This functionality is under development!</value>
218+
</data>
216219
<data name="UseDestinationCheckBox.Content" xml:space="preserve">
217220
<value>Use destination</value>
218221
</data>

0 commit comments

Comments
 (0)