1 // RegistryContextMenu.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/StringConvert.h"
6
7 #include "../../../Windows/Registry.h"
8
9 #include "RegistryContextMenu.h"
10
11 using namespace NWindows;
12 using namespace NRegistry;
13
14 #ifndef UNDER_CE
15
16 // does extension can work, if Approved is removed ?
17 // CLISID (and Approved ?) items are separated for 32-bit and 64-bit code.
18 // shellex items shared by 32-bit and 64-bit code?
19
20 #define k_Clsid_A "{23170F69-40C1-278A-1000-000100020000}"
21
22 static LPCTSTR const k_Clsid = TEXT(k_Clsid_A);
23 static LPCTSTR const k_ShellExtName = TEXT("7-Zip Shell Extension");
24
25 static LPCTSTR const k_Approved = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved");
26 static LPCTSTR const k_Inproc = TEXT("InprocServer32");
27
28 static LPCSTR const k_KeyPostfix_ContextMenu = "\\shellex\\ContextMenuHandlers\\7-Zip";
29 static LPCSTR const k_KeyPostfix_DragDrop = "\\shellex\\DragDropHandlers\\7-Zip";
30
31 static LPCSTR const k_KeyName_File = "*";
32 static LPCSTR const k_KeyName_Folder = "Folder";
33 static LPCSTR const k_KeyName_Directory = "Directory";
34 static LPCSTR const k_KeyName_Drive = "Drive";
35
36 static LPCSTR const k_shellex_Prefixes[] =
37 {
38 k_KeyName_File,
39 k_KeyName_Folder,
40 k_KeyName_Directory,
41 k_KeyName_Drive
42 };
43
44 static const bool k_shellex_Statuses[2][4] =
45 {
46 { true, true, true, false },
47 { false, false, true, true }
48 };
49
50
51 // RegDeleteKeyExW is supported starting from win2003sp1/xp-pro-x64
52 // Z7_WIN32_WINNT_MIN < 0x0600 // Vista
53 #if !defined(Z7_WIN32_WINNT_MIN) \
54 || Z7_WIN32_WINNT_MIN < 0x0502 /* < win2003 */ \
55 || Z7_WIN32_WINNT_MIN == 0x0502 && !defined(_M_AMD64)
56 #define Z7_USE_DYN_RegDeleteKeyExW
57 #endif
58
59 #ifdef Z7_USE_DYN_RegDeleteKeyExW
60 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
61 typedef
62 // WINADVAPI
63 LONG (APIENTRY *Func_RegDeleteKeyExW)(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved);
64 static Func_RegDeleteKeyExW func_RegDeleteKeyExW;
65
Init_RegDeleteKeyExW()66 static void Init_RegDeleteKeyExW()
67 {
68 if (!func_RegDeleteKeyExW)
69 func_RegDeleteKeyExW = Z7_GET_PROC_ADDRESS(
70 Func_RegDeleteKeyExW, GetModuleHandleW(L"advapi32.dll"),
71 "RegDeleteKeyExW");
72 }
73 #define INIT_REG_WOW if (wow != 0) Init_RegDeleteKeyExW();
74 #else
75 #define INIT_REG_WOW
76 #endif
77
78
MyRegistry_DeleteKey(HKEY parentKey,LPCTSTR name,UInt32 wow)79 static LONG MyRegistry_DeleteKey(HKEY parentKey, LPCTSTR name, UInt32 wow)
80 {
81 if (wow == 0)
82 return RegDeleteKey(parentKey, name);
83
84 #ifdef Z7_USE_DYN_RegDeleteKeyExW
85 if (!func_RegDeleteKeyExW)
86 return E_NOTIMPL;
87 return func_RegDeleteKeyExW
88 #else
89 return RegDeleteKeyExW
90 #endif
91 (parentKey, GetUnicodeString(name), wow, 0);
92 }
93
MyRegistry_DeleteKey_HKCR(LPCTSTR name,UInt32 wow)94 static LONG MyRegistry_DeleteKey_HKCR(LPCTSTR name, UInt32 wow)
95 {
96 return MyRegistry_DeleteKey(HKEY_CLASSES_ROOT, name, wow);
97 }
98
99 // static NSynchronization::CCriticalSection g_CS;
100
Get_ContextMenuHandler_KeyName(LPCSTR keyName)101 static AString Get_ContextMenuHandler_KeyName(LPCSTR keyName)
102 { return (AString)keyName + k_KeyPostfix_ContextMenu; }
103
104 /*
105 static CSysString Get_DragDropHandler_KeyName(LPCTSTR keyName)
106 { return (AString)keyName + k_KeyPostfix_DragDrop); }
107 */
108
CheckHandlerCommon(const AString & keyName,UInt32 wow)109 static bool CheckHandlerCommon(const AString &keyName, UInt32 wow)
110 {
111 CKey key;
112 if (key.Open(HKEY_CLASSES_ROOT, (CSysString)keyName, KEY_READ | wow) != ERROR_SUCCESS)
113 return false;
114 CSysString value;
115 if (key.QueryValue(NULL, value) != ERROR_SUCCESS)
116 return false;
117 return StringsAreEqualNoCase_Ascii(value, k_Clsid_A);
118 }
119
CheckContextMenuHandler(const UString & path,UInt32 wow)120 bool CheckContextMenuHandler(const UString &path, UInt32 wow)
121 {
122 // NSynchronization::CCriticalSectionLock lock(g_CS);
123
124 CSysString s ("CLSID\\");
125 s += k_Clsid_A;
126 s += "\\InprocServer32";
127
128 {
129 NRegistry::CKey key;
130 if (key.Open(HKEY_CLASSES_ROOT, s, KEY_READ | wow) != ERROR_SUCCESS)
131 return false;
132 UString regPath;
133 if (key.QueryValue(NULL, regPath) != ERROR_SUCCESS)
134 return false;
135 if (!path.IsEqualTo_NoCase(regPath))
136 return false;
137 }
138
139 return
140 CheckHandlerCommon(Get_ContextMenuHandler_KeyName(k_KeyName_File), wow);
141 /*
142 && CheckHandlerCommon(Get_ContextMenuHandler_KeyName(k_KeyName_Directory), wow)
143 // && CheckHandlerCommon(Get_ContextMenuHandler_KeyName(k_KeyName_Folder))
144
145 && CheckHandlerCommon(Get_DragDropHandler_KeyName(k_KeyName_Directory), wow)
146 && CheckHandlerCommon(Get_DragDropHandler_KeyName(k_KeyName_Drive), wow);
147 */
148 }
149
150
MyCreateKey(CKey & key,HKEY parentKey,LPCTSTR keyName,UInt32 wow)151 static LONG MyCreateKey(CKey &key, HKEY parentKey, LPCTSTR keyName, UInt32 wow)
152 {
153 return key.Create(parentKey, keyName, REG_NONE,
154 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | wow);
155 }
156
SetContextMenuHandler(bool setMode,const UString & path,UInt32 wow)157 LONG SetContextMenuHandler(bool setMode, const UString &path, UInt32 wow)
158 {
159 // NSynchronization::CCriticalSectionLock lock(g_CS);
160
161 INIT_REG_WOW
162
163 LONG res;
164
165 {
166 CSysString s ("CLSID\\");
167 s += k_Clsid_A;
168
169 if (setMode)
170 {
171 {
172 CKey key;
173 res = MyCreateKey(key, HKEY_CLASSES_ROOT, s, wow);
174 if (res == ERROR_SUCCESS)
175 {
176 key.SetValue(NULL, k_ShellExtName);
177 CKey keyInproc;
178 res = MyCreateKey(keyInproc, key, k_Inproc, wow);
179 if (res == ERROR_SUCCESS)
180 {
181 res = keyInproc.SetValue(NULL, path);
182 keyInproc.SetValue(TEXT("ThreadingModel"), TEXT("Apartment"));
183 }
184 }
185 }
186
187 {
188 CKey key;
189 if (MyCreateKey(key, HKEY_LOCAL_MACHINE, k_Approved, wow) == ERROR_SUCCESS)
190 key.SetValue(k_Clsid, k_ShellExtName);
191 }
192 }
193 else
194 {
195 CSysString s2 (s);
196 s2 += "\\InprocServer32";
197
198 MyRegistry_DeleteKey_HKCR(s2, wow);
199 res = MyRegistry_DeleteKey_HKCR(s, wow);
200 }
201 }
202
203 // shellex items probably are shared beween 32-bit and 64-bit apps. So we don't delete items for delete operation.
204 if (setMode)
205 for (unsigned i = 0; i < 2; i++)
206 {
207 for (unsigned k = 0; k < Z7_ARRAY_SIZE(k_shellex_Prefixes); k++)
208 {
209 CSysString s (k_shellex_Prefixes[k]);
210 s += (i == 0 ? k_KeyPostfix_ContextMenu : k_KeyPostfix_DragDrop);
211 if (k_shellex_Statuses[i][k])
212 {
213 CKey key;
214 MyCreateKey(key, HKEY_CLASSES_ROOT, s, wow);
215 key.SetValue(NULL, k_Clsid);
216 }
217 else
218 MyRegistry_DeleteKey_HKCR(s, wow);
219 }
220 }
221
222 return res;
223 }
224
225 #endif
226