Compare commits

..

3 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
f9679b937d Address review: handle NavigationFailed gracefully without rethrowing
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 16:20:42 +08:00
copilot-swe-agent[bot]
36300d3c75 Fix NullReferenceException in Frame_NavigationFailed when e.Exception is null
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/af982aa1-504b-47f1-9ec0-93b29602b2af

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 08:49:32 +00:00
copilot-swe-agent[bot]
1cde68ae04 Initial plan 2026-04-29 08:48:31 +00:00
4 changed files with 15 additions and 112 deletions

View File

@@ -14,7 +14,6 @@
#include <shellapi.h>
#include <Windows.h>
#include <shlobj.h>
#include <thread>
#include <vector>
#include <system_error>
#include <memory>

View File

@@ -334,105 +334,6 @@ namespace newplus::utilities
}
}
// Watches for the user renaming a newly-created folder, then resolves $PARENT_FOLDER_NAME
// in its child files using the final (user-given) name.
// If no rename is detected within timeout_ms, resolves using the original folder name.
// Must be called from a background thread; runs until the rename is detected or timeout expires.
inline void watch_for_rename_then_resolve_variables(
const std::filesystem::path& parent_dir,
const std::wstring& original_folder_name,
const DWORD timeout_ms = 30000)
{
std::filesystem::path final_target_path = parent_dir / original_folder_name;
HANDLE dir_handle = CreateFileW(
parent_dir.c_str(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
nullptr);
if (dir_handle != INVALID_HANDLE_VALUE)
{
HANDLE event_handle = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (event_handle)
{
OVERLAPPED overlapped = {};
overlapped.hEvent = event_handle;
std::vector<BYTE> buffer(32768, 0);
if (ReadDirectoryChangesW(
dir_handle,
buffer.data(),
static_cast<DWORD>(buffer.size()),
FALSE,
FILE_NOTIFY_CHANGE_DIR_NAME,
nullptr,
&overlapped,
nullptr))
{
const DWORD wait_result = WaitForSingleObject(event_handle, timeout_ms);
if (wait_result == WAIT_TIMEOUT)
{
CancelIo(dir_handle);
}
// Wait for the I/O to fully complete (either normally or after cancellation)
// before touching the OVERLAPPED or closing handles.
DWORD bytes_returned = 0;
const bool io_succeeded = GetOverlappedResult(dir_handle, &overlapped, &bytes_returned, TRUE) != FALSE;
if (wait_result == WAIT_OBJECT_0 && io_succeeded && bytes_returned > 0)
{
auto* notify_info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer.data());
bool found_old_name = false;
while (notify_info)
{
const std::wstring filename(
notify_info->FileName,
notify_info->FileNameLength / sizeof(wchar_t));
if (!found_old_name &&
notify_info->Action == FILE_ACTION_RENAMED_OLD_NAME &&
wstring_same_when_comparing_ignore_case(filename, original_folder_name))
{
found_old_name = true;
}
else if (found_old_name && notify_info->Action == FILE_ACTION_RENAMED_NEW_NAME)
{
final_target_path = parent_dir / filename;
break;
}
if (notify_info->NextEntryOffset == 0)
break;
notify_info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(
reinterpret_cast<BYTE*>(notify_info) + notify_info->NextEntryOffset);
}
}
}
CloseHandle(event_handle);
CloseHandle(dir_handle);
}
else
{
CloseHandle(dir_handle);
}
}
// Resolve $PARENT_FOLDER_NAME in child files using the final folder name
if (std::filesystem::exists(final_target_path) && std::filesystem::is_directory(final_target_path))
{
helpers::variables::resolve_variables_in_filename_and_rename_files(final_target_path);
}
}
inline HRESULT copy_template(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder)
{
HRESULT hr = S_OK;
@@ -475,18 +376,10 @@ namespace newplus::utilities
// Finally copy file/folder/subfolders
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
// Resolve variables and rename files in newly copied folders and subfolders.
// For directory templates the top-level folder name is only known after the user
// completes the rename-on-creation prompt, so defer resolution until then so that
// $PARENT_FOLDER_NAME picks up the user-given name rather than the template name.
// The watcher must be started before enter_rename_mode so the rename event is not missed.
// Resolve variables and rename files in newly copied folders and subfolders and files
if (utilities::get_newplus_setting_resolve_variables() && helpers::filesystem::is_directory(target_final_fullpath))
{
auto parent_dir = target_final_fullpath.parent_path();
auto original_folder_name = target_final_fullpath.filename().wstring();
std::thread([parent_dir = std::move(parent_dir), original_folder_name = std::move(original_folder_name)]() {
watch_for_rename_then_resolve_variables(parent_dir, original_folder_name);
}).detach();
helpers::variables::resolve_variables_in_filename_and_rename_files(target_final_fullpath);
}
// Touch all files and set last modified to "now"

View File

@@ -15,7 +15,6 @@
#include <Windows.h>
#include <shlobj.h>
#include <algorithm>
#include <thread>
#include <vector>
#include <system_error>
#include <memory>

View File

@@ -9,6 +9,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
@@ -135,7 +136,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw e.Exception;
var sourcePage = e.SourcePageType?.FullName ?? "<unknown>";
if (e.Exception is null)
{
Logger.LogWarning($"Navigation to '{sourcePage}' failed without an exception.");
}
else
{
Logger.LogError($"Navigation to '{sourcePage}' failed.", e.Exception);
}
e.Handled = true;
}
private void Frame_Navigated(object sender, NavigationEventArgs e)