44#include " ClassicEditWithNppExplorerCommandHandler.h"
55#include " PathHelper.h"
66#include " AclHelper.h"
7+ #include " RegistryKey.h"
78
89#define GUID_STRING_SIZE 40
910
@@ -14,6 +15,7 @@ using namespace winrt::Windows::Management::Deployment;
1415
1516using namespace NppShell ::Helpers;
1617using namespace NppShell ::Installer;
18+ using namespace NppShell ::Registry;
1719
1820extern HMODULE thisModule;
1921
@@ -30,37 +32,12 @@ const wstring ShellExtensionKey = L"Software\\Classes\\*\\shellex\\ContextMenuHa
3032
3133bool IsWindows11Installation ()
3234{
33- wstring keyName = L" SOFTWARE\\ Microsoft\\ Windows NT\\ CurrentVersion" ;
34- wstring valueName = L" CurrentBuildNumber" ;
35+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, L" SOFTWARE\\ Microsoft\\ Windows NT\\ CurrentVersion" ) ;
36+ wstring buildNumberString = registryKey. GetStringValue ( L" CurrentBuildNumber" ) ;
3537
36- bool result = false ;
38+ const int buildNumber = stoi (buildNumberString) ;
3739
38- HKEY hkey;
39- HRESULT hResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, keyName.c_str (), 0 , KEY_READ, &hkey);
40-
41- if (hResult == ERROR_SUCCESS)
42- {
43- DWORD cbData = 0 ;
44- hResult = RegGetValue (hkey, nullptr , valueName.c_str (), RRF_RT_REG_SZ, nullptr , nullptr , &cbData);
45-
46- if (hResult == ERROR_SUCCESS)
47- {
48- vector<BYTE> buffer (cbData + 1 );
49-
50- cbData = (DWORD)buffer.size ();
51- hResult = RegGetValue (hkey, nullptr , valueName.c_str (), RRF_RT_REG_SZ, nullptr , &buffer[0 ], &cbData);
52-
53- wstring buildNumberString (reinterpret_cast <wchar_t *>(&buffer[0 ]));
54-
55- const int buildNumber = stoi (buildNumberString);
56-
57- result = buildNumber >= FirstWindows11BuildNumber;
58- }
59- }
60-
61- RegCloseKey (hkey);
62-
63- return result;
40+ return buildNumber >= FirstWindows11BuildNumber;
6441}
6542
6643wstring GetCLSIDString ()
@@ -90,120 +67,63 @@ wstring GetCLSIDString()
9067 return guid;
9168}
9269
93- LRESULT RemoveRegistryKeyIfFound (HKEY hive, wstring keyName )
70+ void inline CleanupRegistry ( const wstring& guid )
9471{
95- HKEY hkey;
96- const LRESULT lResult = RegOpenKeyEx (hive, keyName.c_str (), 0 , KEY_READ, &hkey);
97-
98- if (lResult == ERROR_FILE_NOT_FOUND)
72+ // First we remove the shell key if it exists.
73+ if (RegistryKey::KeyExists (HKEY_LOCAL_MACHINE, ShellKey))
9974 {
100- // Does not exist, so nothing to remove
101- return ERROR_SUCCESS ;
75+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, ShellKey, KEY_READ | KEY_WRITE);
76+ registryKey. DeleteKey () ;
10277 }
10378
104- if (lResult != ERROR_SUCCESS)
79+ // Then we remove the shell extension key if it exists.
80+ if (RegistryKey::KeyExists (HKEY_LOCAL_MACHINE, ShellExtensionKey))
10581 {
106- return lResult;
82+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, ShellExtensionKey, KEY_READ | KEY_WRITE);
83+ registryKey.DeleteKey ();
10784 }
10885
109- RegCloseKey (hkey);
110-
111- return RegDeleteTree (hive, keyName.c_str ());
112- }
113-
114- LRESULT CreateRegistryKey (const HKEY hive, const wstring& key, const wstring& name, const wstring& value)
115- {
116- HKEY regKey;
117- LRESULT lResult = RegCreateKeyEx (hive, key.data (), 0 , NULL , REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL , ®Key, NULL );
118-
119- if (lResult != ERROR_SUCCESS)
86+ // Then we remove the Notepad++_file key if it exists.
87+ if (RegistryKey::KeyExists (HKEY_LOCAL_MACHINE, L" Notepad++_file\\ shellex" ))
12088 {
121- return lResult;
89+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, L" Notepad++_file\\ shellex" , KEY_READ | KEY_WRITE);
90+ registryKey.DeleteKey ();
12291 }
12392
124- lResult = RegSetKeyValue (regKey, NULL , name.empty () ? NULL : name.data (), REG_SZ, value.data (), static_cast <DWORD>(value.length () * sizeof TCHAR));
125-
126- RegCloseKey (regKey);
127-
128- return lResult;
129- }
130-
131- LRESULT CleanupRegistry (const wstring& guid)
132- {
133- constexpr int bufferSize = MAX_PATH + GUID_STRING_SIZE;
134- WCHAR buffer[bufferSize];
135- const auto arraySize = bufferSize * sizeof (WCHAR);
136-
137- LRESULT result;
138-
139- result = RemoveRegistryKeyIfFound (HKEY_LOCAL_MACHINE, ShellKey);
140-
141- if (result != ERROR_SUCCESS)
93+ // Finally we remove the CLSID key if it exists.
94+ if (RegistryKey::KeyExists (HKEY_LOCAL_MACHINE, L" Software\\ Classes\\ CLSID\\ " + guid))
14295 {
143- return result;
144- }
145-
146- result = RemoveRegistryKeyIfFound (HKEY_LOCAL_MACHINE, ShellExtensionKey);
147-
148- if (result != ERROR_SUCCESS)
149- {
150- return result;
151- }
152-
153- StringCbPrintf (buffer, arraySize, L" Notepad++_file\\ shellex" );
154- result = RemoveRegistryKeyIfFound (HKEY_CLASSES_ROOT, buffer);
155- if (result != ERROR_SUCCESS)
156- {
157- return result;
96+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, L" Software\\ Classes\\ CLSID\\ " + guid, KEY_READ | KEY_WRITE);
97+ registryKey.DeleteKey ();
15898 }
99+ }
159100
160- StringCbPrintf (buffer, arraySize, L" Software\\ Classes\\ CLSID\\ %s" , guid.c_str ());
161- result = RemoveRegistryKeyIfFound (HKEY_LOCAL_MACHINE, buffer);
162- if (result != ERROR_SUCCESS)
101+ void inline CleanupHack ()
102+ {
103+ // First we test if the key even exists.
104+ if (!RegistryKey::KeyExists (HKEY_LOCAL_MACHINE, L" SOFTWARE\\ Classes\\ *\\ shell\\ pintohome" ))
163105 {
164- return result ;
106+ return ;
165107 }
166108
167- return ERROR_SUCCESS;
168- }
169-
170- LRESULT CleanupHack ()
171- {
172- wstring keyName = L" SOFTWARE\\ Classes\\ *\\ shell\\ pintohome" ;
109+ // If it does, we open it and check if the value exists.
173110 wstring valueName = L" MUIVerb" ;
111+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, L" SOFTWARE\\ Classes\\ *\\ shell\\ pintohome" , KEY_READ | KEY_WRITE);
174112
175- LRESULT result = 0 ;
176- bool found = false ;
177-
178- HKEY hkey;
179- HRESULT hResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, keyName.c_str (), 0 , KEY_READ, &hkey);
180-
181- if (hResult == ERROR_SUCCESS)
113+ if (!registryKey.ValueExists (valueName))
182114 {
183- DWORD cbData = 0 ;
184- hResult = RegGetValue (hkey, nullptr , valueName.c_str (), RRF_RT_REG_SZ, nullptr , nullptr , &cbData);
185-
186- if (hResult == ERROR_SUCCESS)
187- {
188- vector<BYTE> buffer (cbData + 1 );
189-
190- cbData = (DWORD)buffer.size ();
191- hResult = RegGetValue (hkey, nullptr , valueName.c_str (), RRF_RT_REG_SZ, nullptr , &buffer[0 ], &cbData);
192-
193- wstring valueString (reinterpret_cast <wchar_t *>(&buffer[0 ]));
194-
195- found = valueString.find (L" Notepad++" ) != wstring::npos;
196- }
115+ return ;
197116 }
198117
199- RegCloseKey (hkey);
118+ // Then we get the value and see if it contains the text "Notepad++"
119+ wstring currentValue = registryKey.GetStringValue (valueName);
120+ bool found = currentValue.find (L" Notepad++" ) != wstring::npos;
200121
122+ // If we found the text, we delete the entire key.
201123 if (found)
202124 {
203- result = RegDeleteTree (HKEY_LOCAL_MACHINE, keyName. c_str () );
125+ registryKey. DeleteKey ( );
204126 }
205-
206- return result;
207127}
208128
209129HRESULT MoveFileToTempAndScheduleDeletion (const wstring& filePath, bool moveToTempDirectory)
@@ -215,7 +135,10 @@ HRESULT MoveFileToTempAndScheduleDeletion(const wstring& filePath, bool moveToTe
215135
216136 if (moveToTempDirectory)
217137 {
138+ // First we get the path to the temporary directory.
218139 GetTempPath (MAX_PATH, &tempPath[0 ]);
140+
141+ // Then we get a temporary filename in the temporary directory.
219142 GetTempFileName (tempPath.c_str (), L" tempFileName" , 0 , &tempFileName[0 ]);
220143
221144 // Move the file into the temp directory - it can be moved even when it is loaded into memory and locked.
@@ -320,15 +243,22 @@ HRESULT NppShell::Installer::UnregisterSparsePackage()
320243
321244HRESULT NppShell::Installer::RegisterOldContextMenu ()
322245{
323- const wstring installationPath = GetContextMenuPath ();
246+ const wstring contextMenuFullName = GetContextMenuFullName ();
324247 const wstring guid = GetCLSIDString ();
325248
326- CreateRegistryKey (HKEY_LOCAL_MACHINE, ShellKey, L" ExplorerCommandHandler" , guid.c_str ());
327- CreateRegistryKey (HKEY_LOCAL_MACHINE, ShellKey, L" " , L" Notepad++ Context menu" );
328- CreateRegistryKey (HKEY_LOCAL_MACHINE, ShellKey, L" NeverDefault" , L" " );
329- CreateRegistryKey (HKEY_LOCAL_MACHINE, L" Software\\ Classes\\ CLSID\\ " + guid, L" " , L" notepad++" );
330- CreateRegistryKey (HKEY_LOCAL_MACHINE, L" Software\\ Classes\\ CLSID\\ " + guid + L" \\ InProcServer32" , L" " , installationPath + L" \\ NppShell.dll" );
331- CreateRegistryKey (HKEY_LOCAL_MACHINE, L" Software\\ Classes\\ CLSID\\ " + guid + L" \\ InProcServer32" , L" ThreadingModel" , L" Apartment" );
249+ // First we set the shell extension values.
250+ RegistryKey regKeyExtension (HKEY_LOCAL_MACHINE, ShellKey, KEY_READ | KEY_WRITE, true );
251+ regKeyExtension.SetStringValue (L" " , L" Notepad++ Context menu" );
252+ regKeyExtension.SetStringValue (L" ExplorerCommandHandler" , guid);
253+ regKeyExtension.SetStringValue (L" NeverDefault" , L" " );
254+
255+ // Then we create the CLSID for the handler with it's values.
256+ RegistryKey regKeyClsid (HKEY_LOCAL_MACHINE, L" Software\\ Classes\\ CLSID\\ " + guid, KEY_READ | KEY_WRITE, true );
257+ regKeyClsid.SetStringValue (L" " , L" notepad++" );
258+
259+ RegistryKey regKeyInProc = regKeyClsid.GetSubKey (L" InProcServer32" , true );
260+ regKeyInProc.SetStringValue (L" " , contextMenuFullName);
261+ regKeyInProc.SetStringValue (L" ThreadingModel" , L" Apartment" );
332262
333263 return S_OK;
334264}
@@ -356,8 +286,11 @@ HRESULT NppShell::Installer::Install()
356286 {
357287 // We need to unregister the old menu on Windows 11 to prevent double entries in the old menu.
358288 UnregisterOldContextMenu ();
289+
290+ // Since we are on Windows 11, we unregister the sparse package as well.
359291 UnregisterSparsePackage ();
360292
293+ // And then we register it again.
361294 result = RegisterSparsePackage ();
362295 }
363296
@@ -377,6 +310,7 @@ HRESULT NppShell::Installer::Install()
377310 MoveFileToTempAndScheduleDeletion (GetApplicationPath () + L" \\ NppModernShell.dll" , false );
378311 MoveFileToTempAndScheduleDeletion (GetApplicationPath () + L" \\ NppModernShell.msix" , false );
379312
313+ // Finally we notify the shell that we have made changes, so it refreshes the context menus.
380314 SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0 , 0 );
381315
382316 return result;
@@ -387,6 +321,8 @@ HRESULT NppShell::Installer::Uninstall()
387321 const bool isWindows11 = IsWindows11Installation ();
388322
389323 HRESULT result;
324+
325+ // We remove the old context menu in all cases, since both Windows 11 and older versions can have it setup if upgrading.
390326 result = UnregisterOldContextMenu ();
391327
392328 if (result != S_OK)
@@ -396,6 +332,7 @@ HRESULT NppShell::Installer::Uninstall()
396332
397333 if (isWindows11)
398334 {
335+ // Since we are on Windows 11, we unregister the sparse package as well.
399336 result = UnregisterSparsePackage ();
400337
401338 if (result != S_OK)
@@ -404,15 +341,18 @@ HRESULT NppShell::Installer::Uninstall()
404341 }
405342 }
406343
344+ // Finally we notify the shell that we have made changes, so it refreshes the context menus.
407345 SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0 , 0 );
408346
409347 return S_OK;
410348}
411349
412350STDAPI CleanupDll ()
413351{
352+ // First we get the full path to this DLL.
414353 wstring currentFilePath (MAX_PATH, L' \0 ' );
415354 GetModuleFileName (thisModule, ¤tFilePath[0 ], MAX_PATH);
416355
356+ // Then we get it moved out of the way and scheduled for deletion.
417357 return MoveFileToTempAndScheduleDeletion (currentFilePath, true );
418358}
0 commit comments