diff --git a/Installer.cpp b/Installer.cpp index 20f42c2..4e9fe61 100644 --- a/Installer.cpp +++ b/Installer.cpp @@ -18,6 +18,7 @@ using namespace NppShell::Installer; using namespace NppShell::Registry; extern HMODULE thisModule; +extern thread ensureRegistrationThread; const wstring SparsePackageName = L"NotepadPlusPlus"; constexpr int FirstWindows11BuildNumber = 22000; @@ -178,6 +179,33 @@ void ResetAclPermissionsOnApplicationFolder() aclHelper.ResetAcl(applicationPath); } +Package GetSparsePackage() +{ + PackageManager packageManager; + IIterable packages; + + try + { + packages = packageManager.FindPackagesForUser(L""); + } + catch (winrt::hresult_error) + { + return NULL; + } + + for (const Package& package : packages) + { + if (package.Id().Name() != SparsePackageName) + { + continue; + } + + return package; + } + + return NULL; +} + HRESULT NppShell::Installer::RegisterSparsePackage() { PackageManager packageManager; @@ -207,32 +235,20 @@ HRESULT NppShell::Installer::UnregisterSparsePackage() PackageManager packageManager; IIterable packages; - try + Package package = GetSparsePackage(); + + if (package == NULL) { - packages = packageManager.FindPackagesForUser(L""); - } - catch (winrt::hresult_error const& ex) - { - return ex.code(); + return S_FALSE; } - for (const Package& package : packages) - { - if (package.Id().Name() != SparsePackageName) - { - continue; - } - - winrt::hstring fullName = package.Id().FullName(); - auto deploymentOperation = packageManager.RemovePackageAsync(fullName, RemovalOptions::None); - auto deployResult = deploymentOperation.get(); - - if (!SUCCEEDED(deployResult.ExtendedErrorCode())) - { - return deployResult.ExtendedErrorCode(); - } + winrt::hstring fullName = package.Id().FullName(); + auto deploymentOperation = packageManager.RemovePackageAsync(fullName, RemovalOptions::None); + auto deployResult = deploymentOperation.get(); - break; + if (!SUCCEEDED(deployResult.ExtendedErrorCode())) + { + return deployResult.ExtendedErrorCode(); } // After unregistering the sparse package, we reset the folder permissions of the folder where we are installed. @@ -347,6 +363,41 @@ HRESULT NppShell::Installer::Uninstall() return S_OK; } +void EnsureRegistrationOnCurrentUserWorker() +{ + // Initialize the WinRT apartment. + winrt::init_apartment(); + + // 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(); + + // 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); + } +} + +void NppShell::Installer::EnsureRegistrationOnCurrentUser() +{ + // First we find the name of the process the DLL is being loaded into. + wstring moduleName = GetExecutingModuleName(); + + if (moduleName == L"explorer.exe") + { + // We are being loaded into explorer.exe, so we can continue. + // Explorer.exe only loads the DLL on the first time a user right-clicks a file + // after that it stays in memory for the rest of their session. + // Since we are here, we spawn a thread and call the EnsureRegistrationOnCurrentUserWorker function. + ensureRegistrationThread = thread(EnsureRegistrationOnCurrentUserWorker); + ensureRegistrationThread.detach(); + } +} + STDAPI CleanupDll() { // First we get the full path to this DLL. diff --git a/Installer.h b/Installer.h index c1d4441..563d611 100644 --- a/Installer.h +++ b/Installer.h @@ -11,6 +11,8 @@ namespace NppShell::Installer HRESULT Install(); HRESULT Uninstall(); + + void EnsureRegistrationOnCurrentUser(); } STDAPI CleanupDll(); \ No newline at end of file diff --git a/PathHelper.cpp b/PathHelper.cpp index 27f5d07..d074c0a 100644 --- a/PathHelper.cpp +++ b/PathHelper.cpp @@ -29,4 +29,16 @@ const wstring NppShell::Helpers::GetContextMenuFullName() { path modulePath = GetThisModulePath(); return modulePath.wstring(); +} + +const wstring NppShell::Helpers::GetExecutingModuleName() +{ + wchar_t pathBuffer[FILENAME_MAX] = { 0 }; + GetModuleFileNameW(NULL, pathBuffer, FILENAME_MAX); + PathStripPathW(pathBuffer); + + wstring moduleName(pathBuffer); + transform(moduleName.begin(), moduleName.end(), moduleName.begin(), towlower); + + return moduleName; } \ No newline at end of file diff --git a/PathHelper.h b/PathHelper.h index b876c2b..5e0d8a8 100644 --- a/PathHelper.h +++ b/PathHelper.h @@ -8,4 +8,5 @@ namespace NppShell::Helpers const wstring GetApplicationPath(); const wstring GetContextMenuPath(); const wstring GetContextMenuFullName(); + const wstring GetExecutingModuleName(); } \ No newline at end of file diff --git a/dllmain.cpp b/dllmain.cpp index dc37d16..3c27014 100644 --- a/dllmain.cpp +++ b/dllmain.cpp @@ -9,6 +9,7 @@ using namespace NppShell::CommandHandlers; using namespace NppShell::Factories; HMODULE thisModule; +thread ensureRegistrationThread; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { @@ -18,6 +19,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser { case DLL_PROCESS_ATTACH: thisModule = hModule; + NppShell::Installer::EnsureRegistrationOnCurrentUser(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: