Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PowerRename] Handle many items w/o crashing and OOM #26761

Merged
merged 11 commits into from
Jul 13, 2023
31 changes: 30 additions & 1 deletion src/modules/powerrename/PowerRenameUILib/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,36 @@
</Setter.Value>
</Setter>
</Style>

<!-- Copied from https://github.com/microsoft/microsoft-ui-xaml/blob/210349d9d1e0aeb8ee144ec027071a68ac578bdb/dev/CommonStyles/CheckBox_themeresources.xaml#L794 to remove the AnimatedIcon that caused crashes (see #18478). -->
<Style x:Key="CheckBoxDefaultStyleOverride" TargetType="CheckBox">
<Setter Property="Background"
Value="{ThemeResource CheckBoxBackgroundUnchecked}" />
<Setter Property="Foreground"
Value="{ThemeResource CheckBoxForegroundUnchecked}" />
<Setter Property="BorderBrush"
Value="{ThemeResource CheckBoxBorderBrushUnchecked}" />
<Setter Property="Padding"
Value="{StaticResource CheckBoxPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="FontFamily"
Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize"
Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="MinWidth"
Value="{StaticResource CheckBoxMinWidth}" />
<Setter Property="MinHeight"
Value="{StaticResource CheckBoxHeight}" />
<Setter Property="UseSystemFocusVisuals"
Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin"
Value="{StaticResource CheckBoxFocusVisualMargin}" />
<Setter Property="CornerRadius"
Value="{ThemeResource ControlCornerRadius}" />

</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
54 changes: 50 additions & 4 deletions src/modules/powerrename/PowerRenameUILib/App.xaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <vector>
#include <string>
#include <filesystem>

#include <common/logger/logger.h>
#include <common/logger/logger_settings.h>
Expand All @@ -18,8 +19,9 @@ using namespace winrt::Microsoft::UI::Xaml::Navigation;
using namespace PowerRenameUI;
using namespace PowerRenameUI::implementation;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace fs = std::filesystem;

//#define DEBUG_BENCHMARK_100K_ENTRIES

std::vector<std::wstring> g_files;

Expand All @@ -34,8 +36,7 @@ App::App()
InitializeComponent();

#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e)
{
UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e) {
if (IsDebuggerPresent())
{
auto errorMessage = e.Message();
Expand Down Expand Up @@ -112,6 +113,50 @@ void App::OnLaunched(LaunchActivatedEventArgs const&)
ExitProcess(1);
}

#ifdef DEBUG_BENCHMARK_100K_ENTRIES
yuyoyuppe marked this conversation as resolved.
Show resolved Hide resolved
const std::wstring_view ROOT_PATH = L"R:\\PowerRenameBenchmark";

std::wstring subdirectory_name = L"0";
std::error_code _;

#if 1
constexpr bool recreate_files = true;
#else
constexpr bool recreate_files = false;
#endif
if constexpr (recreate_files)
fs::remove_all(ROOT_PATH, _);

g_files.push_back(fs::path{ ROOT_PATH });
constexpr int pow2_threshold = 10;
constexpr int num_files = 100'000;
for (int i = 0; i < num_files; ++i)
{
fs::path file_path{ ROOT_PATH };
// Create a subdirectory for each subsequent 2^pow2_threshold files, o/w filesystem becomes too slow to create them in a reasonable time.
if ((i & ((1 << pow2_threshold) - 1)) == 0)
{
subdirectory_name = std::to_wstring(i >> pow2_threshold);
}

file_path /= subdirectory_name;

if constexpr (recreate_files)
{
fs::create_directories(file_path, _);
file_path /= std::to_wstring(i) + L".txt";
HANDLE hFile = CreateFileW(
file_path.c_str(),
GENERIC_WRITE,
0,
nullptr,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
nullptr);
CloseHandle(hFile);
}
}
#else
#define BUFSIZE 4096 * 4

BOOL bSuccess;
Expand Down Expand Up @@ -139,6 +184,7 @@ void App::OnLaunched(LaunchActivatedEventArgs const&)
break;
}
CloseHandle(hStdin);
#endif

Logger::debug(L"Starting PowerRename with {} files selected", g_files.size());

Expand Down
89 changes: 63 additions & 26 deletions src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "ExplorerItem.g.cpp"
#endif

#include <ExplorerItemsSource.h>
yuyoyuppe marked this conversation as resolved.
Show resolved Hide resolved

using namespace winrt;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::Windows::ApplicationModel::Resources;
Expand Down Expand Up @@ -41,22 +43,63 @@ namespace

namespace winrt::PowerRenameUI::implementation
{
DependencyProperty ExplorerItem::_CheckedProperty{ nullptr };

void ExplorerItem::_InitializeProperties()
{
if (!_CheckedProperty)
{
_CheckedProperty =
DependencyProperty::Register(
L"Checked",
xaml_typename<bool>(),
xaml_typename<PowerRenameUI::ExplorerItem>(),
PropertyMetadata{ box_value(true) });
}
}

ExplorerItem::ExplorerItem()
{
_InitializeProperties();
}

ExplorerItem::ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked) :
m_id{ id }, m_idStr{ std::to_wstring(id) }, m_original{ original }, m_renamed{ renamed }, m_type{ type }, m_depth{ depth }, m_checked{ checked }
m_id{ id }, m_idStr{ std::to_wstring(id) }, m_original{ original }, m_renamed{ renamed }, m_type{ type }, m_depth{ depth }
{
_InitializeProperties();

m_imagePath = (m_type == static_cast<UINT>(ExplorerItemType::Folder)) ? folderImagePath : fileImagePath;
Checked(checked);
}

int32_t ExplorerItem::Id()
{
return m_id;
}

void ExplorerItem::Id(const int32_t value)
{
if (m_id != value)
{
m_id = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Id" });
}
}

hstring ExplorerItem::IdStr()
{
return m_idStr;
}

void ExplorerItem::IdStr(hstring const& value)
{
if (m_idStr != value)
{
m_idStr = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"IdStr" });
}
}

hstring ExplorerItem::Original()
{
return m_original;
Expand Down Expand Up @@ -90,45 +133,40 @@ namespace winrt::PowerRenameUI::implementation
return static_cast<double>(m_depth) * 12;
}

hstring ExplorerItem::ImagePath()
void ExplorerItem::Indentation(double value)
{
return m_imagePath;
if (m_depth != value)
{
m_depth = static_cast<uint32_t>(value / 12);
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Indentation" });
}
}

int32_t ExplorerItem::Type()
hstring ExplorerItem::ImagePath()
{
return m_type;
return m_imagePath;
}

void ExplorerItem::Type(int32_t value)
void ExplorerItem::ImagePath(hstring const& value)
{
if (m_type != value)
if (m_imagePath != value)
{
m_type = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Type" });
m_imagePath = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"ImagePath" });
}
}

bool ExplorerItem::Checked()
int32_t ExplorerItem::Type()
{
return m_checked;
return m_type;
}

void ExplorerItem::Checked(bool value)
void ExplorerItem::Type(int32_t value)
{
if (m_checked != value)
if (m_type != value)
{
m_checked = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Checked" });

if (m_checked && !m_renamed.empty())
{
VisualStateManager::GoToState(*this, PowerRenameItemRenameStatusToString(m_state), false);
}
else
{
VisualStateManager::GoToState(*this, L"Normal", false);
}
m_type = value;
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Type" });
}
}

Expand Down Expand Up @@ -166,7 +204,7 @@ namespace winrt::PowerRenameUI::implementation
{
static auto factory = winrt::get_activation_factory<ResourceManager, IResourceManagerFactory>();
static ResourceManager manager = factory.CreateInstance(L"resources.pri");
static auto invalid_char_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_InvalidChar").ValueAsString();
static auto invalid_char_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_InvalidChar").ValueAsString();
static auto name_too_long_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_FileNameTooLong").ValueAsString();

switch (m_state)
Expand All @@ -182,7 +220,6 @@ namespace winrt::PowerRenameUI::implementation
default:
return {};
}

}

}
20 changes: 16 additions & 4 deletions src/modules/powerrename/PowerRenameUILib/ExplorerItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "winrt/Microsoft.UI.Xaml.Controls.Primitives.h"
#include "ExplorerItem.g.h"
#include "PowerRenameInterfaces.h"
#include "Utils.h"

namespace winrt::PowerRenameUI::implementation
{
Expand All @@ -17,27 +18,39 @@ namespace winrt::PowerRenameUI::implementation
File = 1
};

ExplorerItem() = default;
ExplorerItem();

ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked);
int32_t Id();
void Id(int32_t);

hstring IdStr();
void IdStr(hstring const& value);

hstring Original();
void Original(hstring const& value);

hstring Renamed();
void Renamed(hstring const& value);

double Indentation();
void Indentation(double value);

hstring ImagePath();
void ImagePath(hstring const& value);
int32_t Type();
void Type(int32_t value);
bool Checked();
void Checked(bool value);
int32_t State();
void State(int32_t value);
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
void PropertyChanged(winrt::event_token const& token) noexcept;

DEPENDENCY_PROPERTY(bool, Checked);

private:

static void _InitializeProperties();

std::wstring StateToErrorMessage();

int32_t m_id{};
Expand All @@ -47,7 +60,6 @@ namespace winrt::PowerRenameUI::implementation
uint32_t m_depth{};
hstring m_imagePath;
int32_t m_type{};
bool m_checked{};
PowerRenameItemRenameStatus m_state{};
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
};
Expand Down
11 changes: 7 additions & 4 deletions src/modules/powerrename/PowerRenameUILib/ExplorerItem.idl
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ namespace PowerRenameUI
{
ExplorerItem();
ExplorerItem(Int32 id, String original, String renamed, Int32 type, UInt32 depth, Boolean checked);
Int32 Id { get; };
String IdStr { get; };

Int32 Id;
String IdStr;
String Original;
String Renamed;
Double Indentation { get; };
String ImagePath { get; };
Double Indentation;
String ImagePath;
Int32 Type;
Boolean Checked;
Int32 State;

static Microsoft.UI.Xaml.DependencyProperty CheckedProperty { get; };
stefansjfw marked this conversation as resolved.
Show resolved Hide resolved
}
}
2 changes: 2 additions & 0 deletions src/modules/powerrename/PowerRenameUILib/ExplorerItem.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
IsChecked="{x:Bind Checked, Mode=TwoWay}"
IsTabStop="True"
TabIndex="0"
Style="{StaticResource CheckBoxDefaultStyleOverride}"
XYFocusKeyboardNavigation="Enabled" />

<Image
Grid.Column="2"
Width="16"
Expand Down
Loading