From 2ea53b71c9989ba8e3695f97bb2555670eb57edd Mon Sep 17 00:00:00 2001 From: offhub <6871698+offhub@users.noreply.github.com> Date: Wed, 18 Mar 2026 17:54:22 +0300 Subject: [PATCH] Add error handling and logging for WinRT package ops Improved stability for package and DLL registration. Registration errors are now logged and handled properly instead of causing crashes. The installer tracks registration success on a separate thread, and the Windows UI only refreshes when changes actually occur. --- Installer.cpp | 101 +++++++++++++++++++++++++++++++++++++--------- LoggingHelper.cpp | 7 ++++ LoggingHelper.h | 1 + dllmain.cpp | 64 +++++++++++++++++++++++++++-- 4 files changed, 151 insertions(+), 22 deletions(-) diff --git a/Installer.cpp b/Installer.cpp index 0664d89..bb2bd18 100644 --- a/Installer.cpp +++ b/Installer.cpp @@ -5,6 +5,9 @@ #include "PathHelper.h" #include "AclHelper.h" #include "RegistryKey.h" +#include "LoggingHelper.h" + +#include #define GUID_STRING_SIZE 40 @@ -19,6 +22,7 @@ using namespace NppShell::Registry; extern HMODULE g_module; +extern LoggingHelper g_loggingHelper; const wstring SparsePackageName = L"NotepadPlusPlus"; constexpr int FirstWindows11BuildNumber = 22000; @@ -310,20 +314,45 @@ HRESULT NppShell::Installer::UnregisterOldContextMenu() return S_OK; } -void ReRegisterSparsePackage() +HRESULT ReRegisterSparsePackage() noexcept { if (::GetSystemMetrics(SM_CLEANBOOT) > 0) { - return; // Sparse package reg/unreg cannot be done in the Windows OS SafeMode. + return S_FALSE; // Sparse package reg/unreg cannot be done in the Windows OS SafeMode. } - winrt::init_apartment(); + try + { + winrt::init_apartment(); - // Since we are on Windows 11, we unregister the sparse package as well. - UnregisterSparsePackage(); + // Since we are on Windows 11, we unregister the sparse package as well. + UnregisterSparsePackage(); - // And then we register it again. - RegisterSparsePackage(); + // And then we register it again. + return RegisterSparsePackage(); + } + catch (const winrt::hresult_error& ex) + { + g_loggingHelper.LogMessage(L"ReRegisterSparsePackage", L"WinRT exception: " + LoggingHelper::FormatHResult(ex.code())); + return ex.code(); + } + catch (const exception& ex) + { + wstring whatMessage; + if (ex.what() != nullptr) + { + const string message(ex.what()); + whatMessage.assign(message.begin(), message.end()); + } + + g_loggingHelper.LogMessage(L"ReRegisterSparsePackage", L"std::exception: " + whatMessage); + return E_FAIL; + } + catch (...) + { + g_loggingHelper.LogMessage(L"ReRegisterSparsePackage", L"Unknown exception."); + return E_FAIL; + } } HRESULT NppShell::Installer::Install() @@ -331,6 +360,7 @@ HRESULT NppShell::Installer::Install() const bool isWindows11 = IsWindows11Installation(); HRESULT result; + HRESULT sparsePackageResult = S_OK; // Clean up the 8.5 Windows 11 hack if present. CleanupHack(); @@ -341,8 +371,19 @@ HRESULT NppShell::Installer::Install() UnregisterOldContextMenu(); // To register the sparse package, we need to do it on another thread due to WinRT requirements. - thread reRegisterThread(ReRegisterSparsePackage); + promise sparseRegistrationPromise; + future sparseRegistrationFuture = sparseRegistrationPromise.get_future(); + + thread reRegisterThread([&sparseRegistrationPromise]() noexcept { + sparseRegistrationPromise.set_value(ReRegisterSparsePackage()); + }); reRegisterThread.join(); + + sparsePackageResult = sparseRegistrationFuture.get(); + if (FAILED(sparsePackageResult)) + { + g_loggingHelper.LogMessage(L"Installer::Install", L"Sparse package registration failed: " + LoggingHelper::FormatHResult(sparsePackageResult)); + } } result = RegisterOldContextMenu(); @@ -398,22 +439,44 @@ HRESULT NppShell::Installer::Uninstall() return S_OK; } -void EnsureRegistrationOnCurrentUserWorker() +void EnsureRegistrationOnCurrentUserWorker() noexcept { - // Initialize the WinRT apartment. - winrt::init_apartment(); + try + { + // Initialize the WinRT apartment. + winrt::init_apartment(); + + // Get the package to check if it is already installed for the current user. + Package existingPackage = GetSparsePackage(); - // Get the package to check if it is already installed for the current user. - Package existingPackage = GetSparsePackage(); + if (existingPackage == NULL) + { + // The package is not installed for the current user - but we know that Notepad++ is. + // If it wasn't, this code wouldn't be running, so it is safe to just register the package. + RegisterSparsePackage(); - if (existingPackage == NULL) + // Finally we notify the shell that we have made changes, so it reloads the right click menu items. + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0); + } + } + catch (const winrt::hresult_error& ex) + { + g_loggingHelper.LogMessage(L"EnsureRegistrationOnCurrentUserWorker", L"WinRT exception: " + LoggingHelper::FormatHResult(ex.code())); + } + catch (const exception& ex) { - // The package is not installed for the current user - but we know that Notepad++ is. - // If it wasn't, this code wouldn't be running, so it is safe to just register the package. - RegisterSparsePackage(); + wstring whatMessage; + if (ex.what() != nullptr) + { + const string message(ex.what()); + whatMessage.assign(message.begin(), message.end()); + } - // Finally we notify the shell that we have made changes, so it reloads the right click menu items. - SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0); + g_loggingHelper.LogMessage(L"EnsureRegistrationOnCurrentUserWorker", L"std::exception: " + whatMessage); + } + catch (...) + { + g_loggingHelper.LogMessage(L"EnsureRegistrationOnCurrentUserWorker", L"Unknown exception."); } } diff --git a/LoggingHelper.cpp b/LoggingHelper.cpp index b902184..eca7cc0 100644 --- a/LoggingHelper.cpp +++ b/LoggingHelper.cpp @@ -115,6 +115,13 @@ wstring LoggingHelper::GetTimestamp() return wstring(buffer); } +wstring LoggingHelper::FormatHResult(HRESULT hr) +{ + wchar_t buffer[16]; + swprintf_s(buffer, L"0x%08X", static_cast(hr)); + return wstring(buffer); +} + bool LoggingHelper::IsAppDataLoggingEnabled() { const wstring environmentVariableName = L"NPPSHELL_LOGGING_ENABLED"; diff --git a/LoggingHelper.h b/LoggingHelper.h index 4f6bdbe..3660377 100644 --- a/LoggingHelper.h +++ b/LoggingHelper.h @@ -7,6 +7,7 @@ namespace NppShell::Helpers public: LoggingHelper(); void LogMessage(const wstring& source, const wstring& message); + static wstring FormatHResult(HRESULT hr); private: wstring GetRoamingAppDataFolderPath(); diff --git a/dllmain.cpp b/dllmain.cpp index beffa1b..3c47474 100644 --- a/dllmain.cpp +++ b/dllmain.cpp @@ -21,7 +21,15 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser { case DLL_PROCESS_ATTACH: g_module = hModule; - NppShell::Installer::EnsureRegistrationOnCurrentUser(); + try + { + NppShell::Installer::EnsureRegistrationOnCurrentUser(); + } + catch (...) + { + // Never crash process attach due to optional registration checks. + g_loggingHelper.LogMessage(L"DllMain", L"Unknown exception during EnsureRegistrationOnCurrentUser."); + } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: @@ -34,12 +42,62 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser STDAPI DllRegisterServer() { - return NppShell::Installer::Install(); + try + { + return NppShell::Installer::Install(); + } + catch (const winrt::hresult_error& ex) + { + g_loggingHelper.LogMessage(L"DllRegisterServer", L"WinRT exception: " + LoggingHelper::FormatHResult(ex.code())); + return ex.code(); + } + catch (const exception& ex) + { + wstring whatMessage; + if (ex.what() != nullptr) + { + const string message(ex.what()); + whatMessage.assign(message.begin(), message.end()); + } + + g_loggingHelper.LogMessage(L"DllRegisterServer", L"std::exception: " + whatMessage); + return E_FAIL; + } + catch (...) + { + g_loggingHelper.LogMessage(L"DllRegisterServer", L"Unknown exception."); + return E_FAIL; + } } STDAPI DllUnregisterServer() { - return NppShell::Installer::Uninstall(); + try + { + return NppShell::Installer::Uninstall(); + } + catch (const winrt::hresult_error& ex) + { + g_loggingHelper.LogMessage(L"DllUnregisterServer", L"WinRT exception: " + LoggingHelper::FormatHResult(ex.code())); + return ex.code(); + } + catch (const exception& ex) + { + wstring whatMessage; + if (ex.what() != nullptr) + { + const string message(ex.what()); + whatMessage.assign(message.begin(), message.end()); + } + + g_loggingHelper.LogMessage(L"DllUnregisterServer", L"std::exception: " + whatMessage); + return E_FAIL; + } + catch (...) + { + g_loggingHelper.LogMessage(L"DllUnregisterServer", L"Unknown exception."); + return E_FAIL; + } } __control_entrypoint(DllExport)