xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/Explorer/RegistryContextMenu.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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