4
4
#include " ClassicEditWithNppExplorerCommandHandler.h"
5
5
#include " PathHelper.h"
6
6
#include " AclHelper.h"
7
+ #include " RegistryKey.h"
7
8
8
9
#define GUID_STRING_SIZE 40
9
10
@@ -14,6 +15,7 @@ using namespace winrt::Windows::Management::Deployment;
14
15
15
16
using namespace NppShell ::Helpers;
16
17
using namespace NppShell ::Installer;
18
+ using namespace NppShell ::Registry;
17
19
18
20
extern HMODULE thisModule;
19
21
@@ -30,37 +32,12 @@ const wstring ShellExtensionKey = L"Software\\Classes\\*\\shellex\\ContextMenuHa
30
32
31
33
bool IsWindows11Installation ()
32
34
{
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" ) ;
35
37
36
- bool result = false ;
38
+ const int buildNumber = stoi (buildNumberString) ;
37
39
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;
64
41
}
65
42
66
43
wstring GetCLSIDString ()
@@ -90,120 +67,63 @@ wstring GetCLSIDString()
90
67
return guid;
91
68
}
92
69
93
- LRESULT RemoveRegistryKeyIfFound (HKEY hive, wstring keyName )
70
+ void inline CleanupRegistry ( const wstring& guid )
94
71
{
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))
99
74
{
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 () ;
102
77
}
103
78
104
- if (lResult != ERROR_SUCCESS)
79
+ // Then we remove the shell extension key if it exists.
80
+ if (RegistryKey::KeyExists (HKEY_LOCAL_MACHINE, ShellExtensionKey))
105
81
{
106
- return lResult;
82
+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, ShellExtensionKey, KEY_READ | KEY_WRITE);
83
+ registryKey.DeleteKey ();
107
84
}
108
85
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" ))
120
88
{
121
- return lResult;
89
+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, L" Notepad++_file\\ shellex" , KEY_READ | KEY_WRITE);
90
+ registryKey.DeleteKey ();
122
91
}
123
92
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))
142
95
{
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 ();
158
98
}
99
+ }
159
100
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" ))
163
105
{
164
- return result ;
106
+ return ;
165
107
}
166
108
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.
173
110
wstring valueName = L" MUIVerb" ;
111
+ RegistryKey registryKey (HKEY_LOCAL_MACHINE, L" SOFTWARE\\ Classes\\ *\\ shell\\ pintohome" , KEY_READ | KEY_WRITE);
174
112
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))
182
114
{
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 ;
197
116
}
198
117
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;
200
121
122
+ // If we found the text, we delete the entire key.
201
123
if (found)
202
124
{
203
- result = RegDeleteTree (HKEY_LOCAL_MACHINE, keyName. c_str () );
125
+ registryKey. DeleteKey ( );
204
126
}
205
-
206
- return result;
207
127
}
208
128
209
129
HRESULT MoveFileToTempAndScheduleDeletion (const wstring& filePath, bool moveToTempDirectory)
@@ -215,7 +135,10 @@ HRESULT MoveFileToTempAndScheduleDeletion(const wstring& filePath, bool moveToTe
215
135
216
136
if (moveToTempDirectory)
217
137
{
138
+ // First we get the path to the temporary directory.
218
139
GetTempPath (MAX_PATH, &tempPath[0 ]);
140
+
141
+ // Then we get a temporary filename in the temporary directory.
219
142
GetTempFileName (tempPath.c_str (), L" tempFileName" , 0 , &tempFileName[0 ]);
220
143
221
144
// 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()
320
243
321
244
HRESULT NppShell::Installer::RegisterOldContextMenu ()
322
245
{
323
- const wstring installationPath = GetContextMenuPath ();
246
+ const wstring contextMenuFullName = GetContextMenuFullName ();
324
247
const wstring guid = GetCLSIDString ();
325
248
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" );
332
262
333
263
return S_OK;
334
264
}
@@ -356,8 +286,11 @@ HRESULT NppShell::Installer::Install()
356
286
{
357
287
// We need to unregister the old menu on Windows 11 to prevent double entries in the old menu.
358
288
UnregisterOldContextMenu ();
289
+
290
+ // Since we are on Windows 11, we unregister the sparse package as well.
359
291
UnregisterSparsePackage ();
360
292
293
+ // And then we register it again.
361
294
result = RegisterSparsePackage ();
362
295
}
363
296
@@ -377,6 +310,7 @@ HRESULT NppShell::Installer::Install()
377
310
MoveFileToTempAndScheduleDeletion (GetApplicationPath () + L" \\ NppModernShell.dll" , false );
378
311
MoveFileToTempAndScheduleDeletion (GetApplicationPath () + L" \\ NppModernShell.msix" , false );
379
312
313
+ // Finally we notify the shell that we have made changes, so it refreshes the context menus.
380
314
SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0 , 0 );
381
315
382
316
return result;
@@ -387,6 +321,8 @@ HRESULT NppShell::Installer::Uninstall()
387
321
const bool isWindows11 = IsWindows11Installation ();
388
322
389
323
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.
390
326
result = UnregisterOldContextMenu ();
391
327
392
328
if (result != S_OK)
@@ -396,6 +332,7 @@ HRESULT NppShell::Installer::Uninstall()
396
332
397
333
if (isWindows11)
398
334
{
335
+ // Since we are on Windows 11, we unregister the sparse package as well.
399
336
result = UnregisterSparsePackage ();
400
337
401
338
if (result != S_OK)
@@ -404,15 +341,18 @@ HRESULT NppShell::Installer::Uninstall()
404
341
}
405
342
}
406
343
344
+ // Finally we notify the shell that we have made changes, so it refreshes the context menus.
407
345
SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0 , 0 );
408
346
409
347
return S_OK;
410
348
}
411
349
412
350
STDAPI CleanupDll ()
413
351
{
352
+ // First we get the full path to this DLL.
414
353
wstring currentFilePath (MAX_PATH, L' \0 ' );
415
354
GetModuleFileName (thisModule, ¤tFilePath[0 ], MAX_PATH);
416
355
356
+ // Then we get it moved out of the way and scheduled for deletion.
417
357
return MoveFileToTempAndScheduleDeletion (currentFilePath, true );
418
358
}
0 commit comments