xref: /aosp_15_r20/external/lzma/C/Util/7zipUninstall/7zipUninstall.c (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 /* 7zipUninstall.c - 7-Zip Uninstaller
2 2024-03-21 : Igor Pavlov : Public domain */
3 
4 #include "Precomp.h"
5 
6 // #define SZ_ERROR_ABORT 100
7 
8 #include "../../7zTypes.h"
9 #include "../../7zWindows.h"
10 
11 #if defined(_MSC_VER) && _MSC_VER < 1600
12 #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
13 #endif
14 
15 #ifdef Z7_OLD_WIN_SDK
16 struct IShellView;
17 #define SHFOLDERAPI  EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE
18 SHFOLDERAPI SHGetFolderPathW(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
19 #define BIF_NEWDIALOGSTYLE     0x0040   // Use the new dialog layout with the ability to resize
20 typedef enum {
21     SHGFP_TYPE_CURRENT  = 0,   // current value for user, verify it exists
22     SHGFP_TYPE_DEFAULT  = 1,   // default value, may not exist
23 } SHGFP_TYPE;
24 #endif
25 #if defined(__MINGW32__) || defined(__MINGW64__)
26 #include <shlobj.h>
27 #else
28 #include <ShlObj.h>
29 #endif
30 
31 #include "../../7zVersion.h"
32 
33 #include "resource.h"
34 
35 
36 
37 
38 #define LLL_(quote) L##quote
39 #define LLL(quote) LLL_(quote)
40 
41 #define wcscat lstrcatW
42 #define wcslen (size_t)lstrlenW
43 #define wcscpy lstrcpyW
44 
45 // static LPCWSTR const k_7zip = L"7-Zip";
46 
47 // #define Z7_64BIT_INSTALLER 1
48 
49 #ifdef _WIN64
50   #define Z7_64BIT_INSTALLER 1
51 #endif
52 
53 #define k_7zip_with_Ver_base L"7-Zip " LLL(MY_VERSION)
54 
55 #ifdef Z7_64BIT_INSTALLER
56 
57   // #define USE_7ZIP_32_DLL
58 
59   #if defined(_M_ARM64) || defined(_M_ARM)
60     #define k_Postfix  L" (arm64)"
61   #else
62     #define k_Postfix  L" (x64)"
63     #define USE_7ZIP_32_DLL
64   #endif
65 #else
66   #if defined(_M_ARM64) || defined(_M_ARM)
67     #define k_Postfix  L" (arm)"
68   #else
69     // #define k_Postfix  L" (x86)"
70     #define k_Postfix
71   #endif
72 #endif
73 
74 #define k_7zip_with_Ver  k_7zip_with_Ver_base k_Postfix
75 
76 static LPCWSTR const k_7zip_with_Ver_Uninstall = k_7zip_with_Ver L" Uninstall";
77 
78 static LPCWSTR const k_Reg_Software_7zip = L"Software\\7-Zip";
79 
80 static LPCWSTR const k_Reg_Path = L"Path";
81 
82 static LPCWSTR const k_Reg_Path32 = L"Path"
83   #ifdef Z7_64BIT_INSTALLER
84     L"64"
85   #else
86     L"32"
87   #endif
88   ;
89 
90 #if defined(Z7_64BIT_INSTALLER) && !defined(_WIN64)
91   #define k_Reg_WOW_Flag KEY_WOW64_64KEY
92 #else
93   #define k_Reg_WOW_Flag 0
94 #endif
95 
96 #ifdef USE_7ZIP_32_DLL
97 #ifdef _WIN64
98   #define k_Reg_WOW_Flag_32 KEY_WOW64_32KEY
99 #else
100   #define k_Reg_WOW_Flag_32 0
101 #endif
102 #endif
103 
104 #define k_7zip_CLSID L"{23170F69-40C1-278A-1000-000100020000}"
105 
106 static LPCWSTR const k_Reg_CLSID_7zip = L"CLSID\\" k_7zip_CLSID;
107 static LPCWSTR const k_Reg_CLSID_7zip_Inproc = L"CLSID\\" k_7zip_CLSID L"\\InprocServer32";
108 
109 
110 #define g_AllUsers True
111 
112 static BoolInt g_Install_was_Pressed;
113 static BoolInt g_Finished;
114 static BoolInt g_SilentMode;
115 
116 static HWND g_HWND;
117 static HWND g_Path_HWND;
118 static HWND g_InfoLine_HWND;
119 static HWND g_Progress_HWND;
120 
121 // RegDeleteKeyExW is supported starting from win2003sp1/xp-pro-x64
122 // Z7_WIN32_WINNT_MIN < 0x0600  // Vista
123 #if !defined(Z7_WIN32_WINNT_MIN) \
124     || Z7_WIN32_WINNT_MIN  < 0x0502  /* < win2003 */ \
125     || Z7_WIN32_WINNT_MIN == 0x0502 && !defined(_M_AMD64)
126 #define Z7_USE_DYN_RegDeleteKeyExW
127 #endif
128 
129 #ifdef Z7_USE_DYN_RegDeleteKeyExW
130 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
131 typedef LONG (APIENTRY *Func_RegDeleteKeyExW)(HKEY hKey, LPCWSTR lpSubKey, REGSAM samDesired, DWORD Reserved);
132 static Func_RegDeleteKeyExW func_RegDeleteKeyExW;
133 #endif
134 
135 static WCHAR cmd[MAX_PATH + 4];
136 static WCHAR cmdError[MAX_PATH + 4];
137 static WCHAR path[MAX_PATH * 2 + 40];
138 static WCHAR workDir[MAX_PATH + 10];
139 static WCHAR modulePath[MAX_PATH + 10];
140 static WCHAR modulePrefix[MAX_PATH + 10];
141 static WCHAR tempPath[MAX_PATH * 2 + 40];
142 static WCHAR cmdLine[MAX_PATH * 3 + 40];
143 static WCHAR copyPath[MAX_PATH * 2 + 40];
144 
145 static LPCWSTR const kUninstallExe = L"Uninstall.exe";
146 
147 #define MAKE_CHAR_UPPER(c) ((((c) >= 'a' && (c) <= 'z') ? (c) - 0x20 : (c)))
148 
149 
CpyAscii(wchar_t * dest,const char * s)150 static void CpyAscii(wchar_t *dest, const char *s)
151 {
152   for (;;)
153   {
154     const Byte b = (Byte)*s++;
155     *dest++ = b;
156     if (b == 0)
157       return;
158   }
159 }
160 
CatAscii(wchar_t * dest,const char * s)161 static void CatAscii(wchar_t *dest, const char *s)
162 {
163   dest += wcslen(dest);
164   CpyAscii(dest, s);
165 }
166 
PrintErrorMessage(const char * s1,const wchar_t * s2)167 static void PrintErrorMessage(const char *s1, const wchar_t *s2)
168 {
169   WCHAR m[MAX_PATH + 512];
170   m[0] = 0;
171   CatAscii(m, "ERROR:");
172   if (s1)
173   {
174     CatAscii(m, "\n");
175     CatAscii(m, s1);
176   }
177   if (s2)
178   {
179     CatAscii(m, "\n");
180     wcscat(m, s2);
181   }
182   MessageBoxW(g_HWND, m, k_7zip_with_Ver_Uninstall, MB_ICONERROR | MB_OK);
183 }
184 
185 
AreStringsEqual_NoCase(const wchar_t * s1,const wchar_t * s2)186 static BoolInt AreStringsEqual_NoCase(const wchar_t *s1, const wchar_t *s2)
187 {
188   for (;;)
189   {
190     wchar_t c1 = *s1++;
191     wchar_t c2 = *s2++;
192     if (c1 != c2 && MAKE_CHAR_UPPER(c1) != MAKE_CHAR_UPPER(c2))
193       return False;
194     if (c2 == 0)
195       return True;
196   }
197 }
198 
IsString1PrefixedByString2_NoCase(const wchar_t * s1,const wchar_t * s2)199 static BoolInt IsString1PrefixedByString2_NoCase(const wchar_t *s1, const wchar_t *s2)
200 {
201   for (;;)
202   {
203     wchar_t c1;
204     const wchar_t c2 = *s2++;
205     if (c2 == 0)
206       return True;
207     c1 = *s1++;
208     if (c1 != c2 && MAKE_CHAR_UPPER(c1) != MAKE_CHAR_UPPER(c2))
209       return False;
210   }
211 }
212 
NormalizePrefix(WCHAR * s)213 static void NormalizePrefix(WCHAR *s)
214 {
215   const size_t len = wcslen(s);
216   if (len != 0)
217     if (s[len - 1] != WCHAR_PATH_SEPARATOR)
218     {
219       s[len] = WCHAR_PATH_SEPARATOR;
220       s[len + 1] = 0;
221     }
222 }
223 
MyRegistry_QueryString(HKEY hKey,LPCWSTR name,LPWSTR dest)224 static int MyRegistry_QueryString(HKEY hKey, LPCWSTR name, LPWSTR dest)
225 {
226   DWORD cnt = MAX_PATH * sizeof(name[0]);
227   DWORD type = 0;
228   const LONG res = RegQueryValueExW(hKey, name, NULL, &type, (LPBYTE)dest, &cnt);
229   if (type != REG_SZ)
230     return False;
231   return res == ERROR_SUCCESS;
232 }
233 
MyRegistry_QueryString2(HKEY hKey,LPCWSTR keyName,LPCWSTR valName,LPWSTR dest)234 static int MyRegistry_QueryString2(HKEY hKey, LPCWSTR keyName, LPCWSTR valName, LPWSTR dest)
235 {
236   HKEY key = 0;
237   const LONG res = RegOpenKeyExW(hKey, keyName, 0, KEY_READ | k_Reg_WOW_Flag, &key);
238   if (res != ERROR_SUCCESS)
239     return False;
240   {
241     const BoolInt res2 = MyRegistry_QueryString(key, valName, dest);
242     RegCloseKey(key);
243     return res2;
244   }
245 }
246 
MyRegistry_OpenKey_ReadWrite(HKEY parentKey,LPCWSTR name,HKEY * destKey)247 static LONG MyRegistry_OpenKey_ReadWrite(HKEY parentKey, LPCWSTR name, HKEY *destKey)
248 {
249   return RegOpenKeyExW(parentKey, name, 0, KEY_READ | KEY_WRITE | k_Reg_WOW_Flag, destKey);
250 }
251 
MyRegistry_DeleteKey(HKEY parentKey,LPCWSTR name)252 static LONG MyRegistry_DeleteKey(HKEY parentKey, LPCWSTR name)
253 {
254 #if k_Reg_WOW_Flag != 0
255 #ifdef Z7_USE_DYN_RegDeleteKeyExW
256     if (!func_RegDeleteKeyExW)
257       return E_FAIL;
258     return func_RegDeleteKeyExW
259 #else
260     return      RegDeleteKeyExW
261 #endif
262       (parentKey, name, k_Reg_WOW_Flag, 0);
263 #else
264     return RegDeleteKeyW(parentKey, name);
265 #endif
266 }
267 
268 #ifdef USE_7ZIP_32_DLL
269 
MyRegistry_QueryString2_32(HKEY hKey,LPCWSTR keyName,LPCWSTR valName,LPWSTR dest)270 static int MyRegistry_QueryString2_32(HKEY hKey, LPCWSTR keyName, LPCWSTR valName, LPWSTR dest)
271 {
272   HKEY key = 0;
273   const LONG res = RegOpenKeyExW(hKey, keyName, 0, KEY_READ | k_Reg_WOW_Flag_32, &key);
274   if (res != ERROR_SUCCESS)
275     return False;
276   {
277     const BoolInt res2 = MyRegistry_QueryString(key, valName, dest);
278     RegCloseKey(key);
279     return res2;
280   }
281 }
282 
MyRegistry_OpenKey_ReadWrite_32(HKEY parentKey,LPCWSTR name,HKEY * destKey)283 static LONG MyRegistry_OpenKey_ReadWrite_32(HKEY parentKey, LPCWSTR name, HKEY *destKey)
284 {
285   return RegOpenKeyExW(parentKey, name, 0, KEY_READ | KEY_WRITE | k_Reg_WOW_Flag_32, destKey);
286 }
287 
MyRegistry_DeleteKey_32(HKEY parentKey,LPCWSTR name)288 static LONG MyRegistry_DeleteKey_32(HKEY parentKey, LPCWSTR name)
289 {
290 #if k_Reg_WOW_Flag_32 != 0
291 #ifdef Z7_USE_DYN_RegDeleteKeyExW
292     if (!func_RegDeleteKeyExW)
293       return E_FAIL;
294     return func_RegDeleteKeyExW
295 #else
296     return      RegDeleteKeyExW
297 #endif
298       (parentKey, name, k_Reg_WOW_Flag_32, 0);
299 #else
300     return RegDeleteKeyW(parentKey, name);
301 #endif
302 }
303 
304 #endif
305 
306 
307 
308 
MyReg_DeleteVal_Path_if_Equal(HKEY hKey,LPCWSTR name)309 static void MyReg_DeleteVal_Path_if_Equal(HKEY hKey, LPCWSTR name)
310 {
311   WCHAR s[MAX_PATH + 10];
312   if (MyRegistry_QueryString(hKey, name, s))
313   {
314     NormalizePrefix(s);
315     if (AreStringsEqual_NoCase(s, path))
316       RegDeleteValueW(hKey, name);
317   }
318 }
319 
SetRegKey_Path2(HKEY parentKey)320 static void SetRegKey_Path2(HKEY parentKey)
321 {
322   HKEY key = 0;
323   const LONG res = MyRegistry_OpenKey_ReadWrite(parentKey, k_Reg_Software_7zip, &key);
324   if (res == ERROR_SUCCESS)
325   {
326     MyReg_DeleteVal_Path_if_Equal(key, k_Reg_Path32);
327     MyReg_DeleteVal_Path_if_Equal(key, k_Reg_Path);
328 
329     RegCloseKey(key);
330     // MyRegistry_DeleteKey(parentKey, k_Reg_Software_7zip);
331   }
332 }
333 
SetRegKey_Path(void)334 static void SetRegKey_Path(void)
335 {
336   SetRegKey_Path2(HKEY_CURRENT_USER);
337   SetRegKey_Path2(HKEY_LOCAL_MACHINE);
338 }
339 
CreateShellLink(LPCWSTR srcPath,LPCWSTR targetPath)340 static HRESULT CreateShellLink(LPCWSTR srcPath, LPCWSTR targetPath)
341 {
342   IShellLinkW *sl;
343 
344   // CoInitialize has already been called.
345   HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl);
346 
347   if (SUCCEEDED(hres))
348   {
349     IPersistFile *pf;
350 
351     hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (LPVOID *)&pf);
352 
353     if (SUCCEEDED(hres))
354     {
355       WCHAR s[MAX_PATH + 10];
356       hres = pf->lpVtbl->Load(pf, srcPath, TRUE);
357       pf->lpVtbl->Release(pf);
358 
359       if (SUCCEEDED(hres))
360       {
361         hres = sl->lpVtbl->GetPath(sl, s, MAX_PATH, NULL, 0); // SLGP_RAWPATH
362         if (!AreStringsEqual_NoCase(s, targetPath))
363           hres = S_FALSE;
364       }
365     }
366 
367     sl->lpVtbl->Release(sl);
368   }
369 
370   return hres;
371 }
372 
SetShellProgramsGroup(HWND hwndOwner)373 static void SetShellProgramsGroup(HWND hwndOwner)
374 {
375   #ifdef UNDER_CE
376 
377   UNUSED_VAR(hwndOwner)
378 
379   #else
380 
381   unsigned i = (g_AllUsers ? 1 : 2);
382 
383   for (; i < 3; i++)
384   {
385     // BoolInt isOK = True;
386     WCHAR link[MAX_PATH + 40];
387     WCHAR destPath[MAX_PATH + 40];
388 
389     link[0] = 0;
390 
391     if (SHGetFolderPathW(hwndOwner,
392         i == 1 ? CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS,
393         NULL, SHGFP_TYPE_CURRENT, link) != S_OK)
394       continue;
395 
396     NormalizePrefix(link);
397     CatAscii(link, "7-Zip\\");
398 
399     {
400       const size_t baseLen = wcslen(link);
401       unsigned k;
402       BoolInt needDelete = False;
403 
404       for (k = 0; k < 2; k++)
405       {
406         CpyAscii(link + baseLen, k == 0 ?
407             "7-Zip File Manager.lnk" :
408             "7-Zip Help.lnk");
409         wcscpy(destPath, path);
410         CatAscii(destPath, k == 0 ?
411             "7zFM.exe" :
412             "7-zip.chm");
413 
414         if (CreateShellLink(link, destPath) == S_OK)
415         {
416           needDelete = True;
417           DeleteFileW(link);
418         }
419       }
420 
421       if (needDelete)
422       {
423         link[baseLen] = 0;
424         RemoveDirectoryW(link);
425       }
426     }
427   }
428 
429   #endif
430 }
431 
432 
433 static LPCSTR const k_ShellEx_Items[] =
434 {
435     "*\\shellex\\ContextMenuHandlers"
436   , "Directory\\shellex\\ContextMenuHandlers"
437   , "Folder\\shellex\\ContextMenuHandlers"
438   , "Directory\\shellex\\DragDropHandlers"
439   , "Drive\\shellex\\DragDropHandlers"
440 };
441 
442 static LPCWSTR const k_Shell_Approved = L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved";
443 
444 static LPCWSTR const k_AppPaths_7zFm = L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\7zFM.exe";
445 #define k_REG_Uninstall L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
446 static LPCWSTR const k_Uninstall_7zip = k_REG_Uninstall L"7-Zip";
447 
448 
RemoveQuotes(wchar_t * s)449 static void RemoveQuotes(wchar_t *s)
450 {
451   const size_t len = wcslen(s);
452   size_t i;
453   if (len == 0 || s[0] != '\"' || s[len - 1] != '\"')
454     return;
455   for (i = 0; i < len; i++)
456     s[i] = s[i + 1];
457   s[len - 2] = 0;
458 }
459 
AreEqual_Path_PrefixName(const wchar_t * s,const wchar_t * prefix,const wchar_t * name)460 static BoolInt AreEqual_Path_PrefixName(const wchar_t *s, const wchar_t *prefix, const wchar_t *name)
461 {
462   if (!IsString1PrefixedByString2_NoCase(s, prefix))
463     return False;
464   return AreStringsEqual_NoCase(s + wcslen(prefix), name);
465 }
466 
WriteCLSID(void)467 static void WriteCLSID(void)
468 {
469   WCHAR s[MAX_PATH + 30];
470 
471   if (MyRegistry_QueryString2(HKEY_CLASSES_ROOT, k_Reg_CLSID_7zip_Inproc, NULL, s))
472   {
473     if (AreEqual_Path_PrefixName(s, path, L"7-zip.dll"))
474     {
475       {
476         const LONG res = MyRegistry_DeleteKey(HKEY_CLASSES_ROOT, k_Reg_CLSID_7zip_Inproc);
477         if (res == ERROR_SUCCESS)
478           MyRegistry_DeleteKey(HKEY_CLASSES_ROOT, k_Reg_CLSID_7zip);
479       }
480 
481       {
482         unsigned i;
483         for (i = 0; i < Z7_ARRAY_SIZE(k_ShellEx_Items); i++)
484         {
485           WCHAR destPath[MAX_PATH];
486           CpyAscii(destPath, k_ShellEx_Items[i]);
487           CatAscii(destPath, "\\7-Zip");
488 
489           MyRegistry_DeleteKey(HKEY_CLASSES_ROOT, destPath);
490         }
491       }
492 
493       {
494         HKEY destKey = 0;
495         const LONG res = MyRegistry_OpenKey_ReadWrite(HKEY_LOCAL_MACHINE, k_Shell_Approved, &destKey);
496         if (res == ERROR_SUCCESS)
497         {
498           RegDeleteValueW(destKey, k_7zip_CLSID);
499           /* res = */ RegCloseKey(destKey);
500         }
501       }
502     }
503   }
504 
505 
506   #ifdef USE_7ZIP_32_DLL
507 
508   if (MyRegistry_QueryString2_32(HKEY_CLASSES_ROOT, k_Reg_CLSID_7zip_Inproc, NULL, s))
509   {
510     if (AreEqual_Path_PrefixName(s, path, L"7-zip32.dll"))
511     {
512       {
513         const LONG res = MyRegistry_DeleteKey_32(HKEY_CLASSES_ROOT, k_Reg_CLSID_7zip_Inproc);
514         if (res == ERROR_SUCCESS)
515           MyRegistry_DeleteKey_32(HKEY_CLASSES_ROOT, k_Reg_CLSID_7zip);
516       }
517 
518       {
519         unsigned i;
520         for (i = 0; i < Z7_ARRAY_SIZE(k_ShellEx_Items); i++)
521         {
522           WCHAR destPath[MAX_PATH];
523           CpyAscii(destPath, k_ShellEx_Items[i]);
524           CatAscii(destPath, "\\7-Zip");
525 
526           MyRegistry_DeleteKey_32(HKEY_CLASSES_ROOT, destPath);
527         }
528       }
529 
530       {
531         HKEY destKey = 0;
532         const LONG res = MyRegistry_OpenKey_ReadWrite_32(HKEY_LOCAL_MACHINE, k_Shell_Approved, &destKey);
533         if (res == ERROR_SUCCESS)
534         {
535           RegDeleteValueW(destKey, k_7zip_CLSID);
536           /* res = */ RegCloseKey(destKey);
537         }
538       }
539     }
540   }
541 
542   #endif
543 
544 
545   if (MyRegistry_QueryString2(HKEY_LOCAL_MACHINE, k_AppPaths_7zFm, NULL, s))
546   {
547     // RemoveQuotes(s);
548     if (AreEqual_Path_PrefixName(s, path, L"7zFM.exe"))
549       MyRegistry_DeleteKey(HKEY_LOCAL_MACHINE, k_AppPaths_7zFm);
550   }
551 
552   if (MyRegistry_QueryString2(HKEY_LOCAL_MACHINE, k_Uninstall_7zip, L"UninstallString", s))
553   {
554     RemoveQuotes(s);
555     if (AreEqual_Path_PrefixName(s, path, kUninstallExe))
556       MyRegistry_DeleteKey(HKEY_LOCAL_MACHINE, k_Uninstall_7zip);
557   }
558 }
559 
560 
GetCmdParam(const wchar_t * s)561 static const wchar_t *GetCmdParam(const wchar_t *s)
562 {
563   unsigned pos = 0;
564   BoolInt quoteMode = False;
565   for (;; s++)
566   {
567     const wchar_t c = *s;
568     if (c == 0 || (c == L' ' && !quoteMode))
569       break;
570     if (c == L'\"')
571     {
572       quoteMode = !quoteMode;
573       continue;
574     }
575     if (pos >= Z7_ARRAY_SIZE(cmd) - 1)
576       exit(1);
577     cmd[pos++] = c;
578   }
579   cmd[pos] = 0;
580   return s;
581 }
582 
583 /*
584 static void RemoveQuotes(wchar_t *s)
585 {
586   const wchar_t *src = s;
587   for (;;)
588   {
589     wchar_t c = *src++;
590     if (c == '\"')
591       continue;
592     *s++ = c;
593     if (c == 0)
594       return;
595   }
596 }
597 */
598 
DoesFileOrDirExist(void)599 static BoolInt DoesFileOrDirExist(void)
600 {
601   return (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES);
602 }
603 
RemoveFileAfterReboot2(const WCHAR * s)604 static BOOL RemoveFileAfterReboot2(const WCHAR *s)
605 {
606   #ifndef UNDER_CE
607   return MoveFileExW(s, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
608   #else
609   UNUSED_VAR(s)
610   return TRUE;
611   #endif
612 }
613 
RemoveFileAfterReboot(void)614 static BOOL RemoveFileAfterReboot(void)
615 {
616   return RemoveFileAfterReboot2(path);
617 }
618 
619 // #define IS_LIMIT_CHAR(c) (c == 0 || c == ' ')
620 
IsThereSpace(const wchar_t * s)621 static BoolInt IsThereSpace(const wchar_t *s)
622 {
623   for (;;)
624   {
625     const wchar_t c = *s++;
626     if (c == 0)
627       return False;
628     if (c == ' ')
629       return True;
630   }
631 }
632 
AddPathParam(wchar_t * dest,const wchar_t * src)633 static void AddPathParam(wchar_t *dest, const wchar_t *src)
634 {
635   const BoolInt needQuote = IsThereSpace(src);
636   if (needQuote)
637     CatAscii(dest, "\"");
638   wcscat(dest, src);
639   if (needQuote)
640     CatAscii(dest, "\"");
641 }
642 
643 
644 
GetErrorMessage(DWORD errorCode,WCHAR * message)645 static BoolInt GetErrorMessage(DWORD errorCode, WCHAR *message)
646 {
647   LPWSTR msgBuf;
648   if (FormatMessageW(
649           FORMAT_MESSAGE_ALLOCATE_BUFFER
650         | FORMAT_MESSAGE_FROM_SYSTEM
651         | FORMAT_MESSAGE_IGNORE_INSERTS,
652         NULL, errorCode, 0, (LPWSTR) &msgBuf, 0, NULL) == 0)
653     return False;
654   wcscpy(message, msgBuf);
655   LocalFree(msgBuf);
656   return True;
657 }
658 
RemoveDir(void)659 static BOOL RemoveDir(void)
660 {
661   const DWORD attrib = GetFileAttributesW(path);
662   if (attrib == INVALID_FILE_ATTRIBUTES)
663     return TRUE;
664   if (RemoveDirectoryW(path))
665     return TRUE;
666   return RemoveFileAfterReboot();
667 }
668 
669 
670 
671 
672 
673 #define k_Lang "Lang"
674 
675 // NUM_LANG_TXT_FILES files are placed before en.ttt
676 #define NUM_LANG_TXT_FILES 92
677 
678 #ifdef USE_7ZIP_32_DLL
679   #define NUM_EXTRA_FILES_64BIT 1
680 #else
681   #define NUM_EXTRA_FILES_64BIT 0
682 #endif
683 
684 #define NUM_FILES (NUM_LANG_TXT_FILES + 1 + 13 + NUM_EXTRA_FILES_64BIT)
685 
686 static const char * const k_Names =
687   "af an ar ast az ba be bg bn br ca co cs cy da de el eo es et eu ext"
688   " fa fi fr fur fy ga gl gu he hi hr hu hy id io is it ja ka kaa kab kk ko ku ku-ckb ky"
689   " lij lt lv mk mn mng mng2 mr ms nb ne nl nn pa-in pl ps pt pt-br ro ru"
690   " sa si sk sl sq sr-spc sr-spl sv sw ta tg th tk tr tt ug uk uz uz-cyrl va vi yo zh-cn zh-tw"
691   " en.ttt"
692   " descript.ion"
693   " History.txt"
694   " License.txt"
695   " readme.txt"
696   " 7-zip.chm"
697   " 7z.sfx"
698   " 7zCon.sfx"
699   " 7z.exe"
700   " 7zG.exe"
701   " 7z.dll"
702   " 7zFM.exe"
703   #ifdef USE_7ZIP_32_DLL
704   " 7-zip32.dll"
705   #endif
706   " 7-zip.dll"
707   " Uninstall.exe";
708 
709 
710 
Install(void)711 static int Install(void)
712 {
713   SRes res = SZ_OK;
714   WRes winRes = 0;
715 
716   // BoolInt needReboot = False;
717   const size_t pathLen = wcslen(path);
718 
719   if (!g_SilentMode)
720   {
721     ShowWindow(g_Progress_HWND, SW_SHOW);
722     ShowWindow(g_InfoLine_HWND, SW_SHOW);
723     SendMessage(g_Progress_HWND, PBM_SETRANGE32, 0, NUM_FILES);
724   }
725 
726   {
727     unsigned i;
728     const char *curName = k_Names;
729 
730     for (i = 0; *curName != 0; i++)
731     {
732       WCHAR *temp;
733 
734       if (!g_SilentMode)
735       {
736         MSG msg;
737 
738         // g_HWND
739         while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
740         {
741           if (!IsDialogMessage(g_HWND, &msg))
742           {
743             TranslateMessage(&msg);
744             DispatchMessage(&msg);
745           }
746           if (!g_HWND)
747             return 1;
748         }
749 
750         // Sleep(1);
751         SendMessage(g_Progress_HWND, PBM_SETPOS, i, 0);
752       }
753 
754       path[pathLen] = 0;
755       temp = path + pathLen;
756 
757       if (i <= NUM_LANG_TXT_FILES)
758         CpyAscii(temp, k_Lang "\\");
759 
760       {
761         WCHAR *dest = temp + wcslen(temp);
762 
763         for (;;)
764         {
765           const char c = *curName;
766           if (c == 0)
767             break;
768           curName++;
769           if (c == ' ')
770             break;
771           *dest++ = (Byte)c;
772         }
773 
774         *dest = 0;
775       }
776 
777       if (i < NUM_LANG_TXT_FILES)
778         CatAscii(temp, ".txt");
779 
780       if (!g_SilentMode)
781         SetWindowTextW(g_InfoLine_HWND, temp);
782 
783       {
784         const DWORD attrib = GetFileAttributesW(path);
785         if (attrib == INVALID_FILE_ATTRIBUTES)
786           continue;
787         if (attrib & FILE_ATTRIBUTE_READONLY)
788           SetFileAttributesW(path, 0);
789         if (!DeleteFileW(path))
790         {
791           if (!RemoveFileAfterReboot())
792           {
793             winRes = GetLastError();
794           }
795           /*
796           else
797             needReboot = True;
798           */
799         }
800       }
801     }
802 
803     CpyAscii(path + pathLen, k_Lang);
804     RemoveDir();
805 
806     path[pathLen] = 0;
807     RemoveDir();
808 
809     if (!g_SilentMode)
810       SendMessage(g_Progress_HWND, PBM_SETPOS, i, 0);
811 
812     if (*curName == 0)
813     {
814       SetRegKey_Path();
815       WriteCLSID();
816       SetShellProgramsGroup(g_HWND);
817       if (!g_SilentMode)
818         SetWindowTextW(g_InfoLine_HWND, k_7zip_with_Ver L" is uninstalled");
819     }
820   }
821 
822   if (winRes != 0)
823     res = SZ_ERROR_FAIL;
824 
825   if (res == SZ_OK)
826   {
827     // if (!g_SilentMode && needReboot);
828     return 0;
829   }
830 
831   if (!g_SilentMode)
832   {
833     WCHAR m[MAX_PATH + 100];
834     m[0] = 0;
835     if (winRes == 0 || !GetErrorMessage(winRes, m))
836       CpyAscii(m, "ERROR");
837     PrintErrorMessage("System ERROR:", m);
838   }
839 
840   return 1;
841 }
842 
843 
OnClose(void)844 static void OnClose(void)
845 {
846   if (g_Install_was_Pressed && !g_Finished)
847   {
848     if (MessageBoxW(g_HWND,
849         L"Do you want to cancel uninstallation?",
850         k_7zip_with_Ver_Uninstall,
851         MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES)
852       return;
853   }
854   DestroyWindow(g_HWND);
855   g_HWND = NULL;
856 }
857 
858 static
859 #ifdef Z7_OLD_WIN_SDK
860   BOOL
861 #else
862   INT_PTR
863 #endif
MyDlgProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)864 CALLBACK MyDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
865 {
866   UNUSED_VAR(lParam)
867 
868   switch (message)
869   {
870     case WM_INITDIALOG:
871       g_Path_HWND = GetDlgItem(hwnd, IDE_EXTRACT_PATH);
872       g_InfoLine_HWND = GetDlgItem(hwnd, IDT_CUR_FILE);
873       g_Progress_HWND = GetDlgItem(hwnd, IDC_PROGRESS);
874 
875       SetWindowTextW(hwnd, k_7zip_with_Ver_Uninstall);
876       SetDlgItemTextW(hwnd, IDE_EXTRACT_PATH, path);
877 
878       ShowWindow(g_Progress_HWND, SW_HIDE);
879       ShowWindow(g_InfoLine_HWND, SW_HIDE);
880 
881       break;
882 
883     case WM_COMMAND:
884       switch (LOWORD(wParam))
885       {
886         case IDOK:
887         {
888           if (g_Finished)
889           {
890             OnClose();
891             break;
892           }
893           if (!g_Install_was_Pressed)
894           {
895             SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)(void *)GetDlgItem(hwnd, IDCANCEL), TRUE);
896 
897             EnableWindow(g_Path_HWND, FALSE);
898             EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
899 
900             g_Install_was_Pressed = True;
901             return TRUE;
902           }
903           break;
904         }
905 
906         case IDCANCEL:
907         {
908           OnClose();
909           break;
910         }
911 
912         default: return FALSE;
913       }
914       break;
915 
916     case WM_CLOSE:
917       OnClose();
918       break;
919     /*
920     case WM_DESTROY:
921       PostQuitMessage(0);
922       return TRUE;
923     */
924     default:
925       return FALSE;
926   }
927 
928   return TRUE;
929 }
930 
931 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,int nCmdShow)932 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
933     #ifdef UNDER_CE
934       LPWSTR
935     #else
936       LPSTR
937     #endif
938     lpCmdLine, int nCmdShow)
939 {
940   const wchar_t *cmdParams;
941   BoolInt useTemp = True;
942 
943   UNUSED_VAR(hPrevInstance)
944   UNUSED_VAR(lpCmdLine)
945   UNUSED_VAR(nCmdShow)
946 
947 #ifndef UNDER_CE
948   CoInitialize(NULL);
949 #endif
950 
951 #ifndef UNDER_CE
952 #ifdef Z7_USE_DYN_RegDeleteKeyExW
953    func_RegDeleteKeyExW =
954   (Func_RegDeleteKeyExW) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandleW(L"advapi32.dll"),
955        "RegDeleteKeyExW");
956 #endif
957 #endif
958 
959   {
960     const wchar_t *s = GetCommandLineW();
961 
962     #ifndef UNDER_CE
963     s = GetCmdParam(s);
964     #endif
965 
966     cmdParams = s;
967 
968     for (;;)
969     {
970       {
971         wchar_t c = *s;
972         if (c == 0)
973           break;
974         if (c == ' ')
975         {
976           s++;
977           continue;
978         }
979       }
980 
981       {
982         const wchar_t *s2 = GetCmdParam(s);
983         BoolInt error = True;
984         if (cmd[0] == '/')
985         {
986           if (cmd[1] == 'S')
987           {
988             if (cmd[2] == 0)
989             {
990               g_SilentMode = True;
991               error = False;
992             }
993           }
994           else if (cmd[1] == 'N')
995           {
996             if (cmd[2] == 0)
997             {
998               useTemp = False;
999               error = False;
1000             }
1001           }
1002           else if (cmd[1] == 'D' && cmd[2] == '=')
1003           {
1004             wcscpy(workDir, cmd + 3);
1005             // RemoveQuotes(workDir);
1006             useTemp = False;
1007             error = False;
1008           }
1009         }
1010         s = s2;
1011         if (error && cmdError[0] == 0)
1012           wcscpy(cmdError, cmd);
1013       }
1014     }
1015 
1016     if (cmdError[0] != 0)
1017     {
1018       if (!g_SilentMode)
1019         PrintErrorMessage("Unsupported command:", cmdError);
1020       return 1;
1021     }
1022   }
1023 
1024   {
1025     wchar_t *name;
1026     const DWORD len = GetModuleFileNameW(NULL, modulePath, MAX_PATH);
1027     if (len == 0 || len > MAX_PATH)
1028       return 1;
1029 
1030     name = NULL;
1031     wcscpy(modulePrefix, modulePath);
1032 
1033     {
1034       wchar_t *s = modulePrefix;
1035       for (;;)
1036       {
1037         const wchar_t c = *s++;
1038         if (c == 0)
1039           break;
1040         if (c == WCHAR_PATH_SEPARATOR)
1041           name = s;
1042       }
1043     }
1044 
1045     if (!name)
1046       return 1;
1047 
1048     if (!AreStringsEqual_NoCase(name, kUninstallExe))
1049       useTemp = False;
1050 
1051     *name = 0; // keep only prefix for modulePrefix
1052   }
1053 
1054 
1055   if (useTemp)
1056   {
1057     DWORD winRes = GetTempPathW(MAX_PATH, path);
1058 
1059     // GetTempPath: the returned string ends with a backslash
1060     /*
1061       {
1062         WCHAR s[MAX_PATH + 1];
1063         wcscpy(s, path);
1064         GetLongPathNameW(s, path, MAX_PATH);
1065       }
1066     */
1067 
1068     if (winRes != 0 && winRes <= MAX_PATH + 1
1069         && !IsString1PrefixedByString2_NoCase(modulePrefix, path))
1070     {
1071       unsigned i;
1072       DWORD d;
1073 
1074       const size_t pathLen = wcslen(path);
1075       d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
1076 
1077       for (i = 0; i < 100; i++, d += GetTickCount())
1078       {
1079         CpyAscii(path + pathLen, "7z");
1080 
1081         {
1082           wchar_t *s = path + wcslen(path);
1083           UInt32 value = d;
1084           unsigned k;
1085           for (k = 0; k < 8; k++)
1086           {
1087             const unsigned t = value & 0xF;
1088             value >>= 4;
1089             s[7 - k] = (wchar_t)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
1090           }
1091           s[k] = 0;
1092         }
1093 
1094         if (DoesFileOrDirExist())
1095           continue;
1096         if (CreateDirectoryW(path, NULL))
1097         {
1098           CatAscii(path, STRING_PATH_SEPARATOR);
1099           wcscpy(tempPath, path);
1100           break;
1101         }
1102         if (GetLastError() != ERROR_ALREADY_EXISTS)
1103           break;
1104       }
1105 
1106       if (tempPath[0] != 0)
1107       {
1108         wcscpy(copyPath, tempPath);
1109         CatAscii(copyPath, "Uninst.exe"); // we need not "Uninstall.exe" here
1110 
1111         if (CopyFileW(modulePath, copyPath, TRUE))
1112         {
1113           RemoveFileAfterReboot2(copyPath);
1114           RemoveFileAfterReboot2(tempPath);
1115 
1116           {
1117             STARTUPINFOW si;
1118             PROCESS_INFORMATION pi;
1119             cmdLine[0] = 0;
1120 
1121             // maybe CreateProcess supports path with spaces even without quotes.
1122             AddPathParam(cmdLine, copyPath);
1123             CatAscii(cmdLine, " /N /D=");
1124             AddPathParam(cmdLine, modulePrefix);
1125 
1126             if (cmdParams[0] != 0 && wcslen(cmdParams) < MAX_PATH * 2 + 10)
1127               wcscat(cmdLine, cmdParams);
1128 
1129             memset(&si, 0, sizeof(si));
1130             si.cb = sizeof(si);
1131 
1132             if (CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, tempPath, &si, &pi))
1133             {
1134               CloseHandle(pi.hThread);
1135               if (pi.hProcess)
1136               {
1137                 CloseHandle(pi.hProcess);
1138                 return 0;
1139               }
1140             }
1141           }
1142         }
1143       }
1144     }
1145   }
1146 
1147   wcscpy(path, modulePrefix);
1148 
1149   if (workDir[0] != 0)
1150   {
1151     wcscpy(path, workDir);
1152     NormalizePrefix(path);
1153   }
1154 
1155   /*
1156   if (path[0] == 0)
1157   {
1158     HKEY key = 0;
1159     BoolInt ok = False;
1160     LONG res = RegOpenKeyExW(HKEY_CURRENT_USER, k_Reg_Software_7zip, 0, KEY_READ | k_Reg_WOW_Flag, &key);
1161     if (res == ERROR_SUCCESS)
1162     {
1163       ok = MyRegistry_QueryString(key, k_Reg_Path32, path);
1164       // ok = MyRegistry_QueryString(key, k_Reg_Path, path);
1165       RegCloseKey(key);
1166     }
1167   }
1168   */
1169 
1170 
1171   if (g_SilentMode)
1172     return Install();
1173 
1174   {
1175     int retCode = 1;
1176     g_HWND = CreateDialog(
1177         hInstance,
1178         // GetModuleHandle(NULL),
1179         MAKEINTRESOURCE(IDD_INSTALL), NULL, MyDlgProc);
1180     if (!g_HWND)
1181       return 1;
1182 
1183     {
1184       const HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
1185       // SendMessage(g_HWND, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
1186       SendMessage(g_HWND, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
1187     }
1188 
1189     {
1190       BOOL bRet;
1191       MSG msg;
1192 
1193       while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
1194       {
1195         if (bRet == -1)
1196           return retCode;
1197         if (!g_HWND)
1198           return retCode;
1199 
1200         if (!IsDialogMessage(g_HWND, &msg))
1201         {
1202           TranslateMessage(&msg);
1203           DispatchMessage(&msg);
1204         }
1205         if (!g_HWND)
1206           return retCode;
1207 
1208         if (g_Install_was_Pressed && !g_Finished)
1209         {
1210           retCode = Install();
1211           g_Finished = True;
1212           if (retCode != 0)
1213             break;
1214           if (!g_HWND)
1215             break;
1216           {
1217             SetDlgItemTextW(g_HWND, IDOK, L"Close");
1218             EnableWindow(GetDlgItem(g_HWND, IDOK), TRUE);
1219             EnableWindow(GetDlgItem(g_HWND, IDCANCEL), FALSE);
1220             SendMessage(g_HWND, WM_NEXTDLGCTL, (WPARAM)(void *)GetDlgItem(g_HWND, IDOK), TRUE);
1221           }
1222         }
1223       }
1224 
1225       if (g_HWND)
1226       {
1227         DestroyWindow(g_HWND);
1228         g_HWND = NULL;
1229       }
1230     }
1231 
1232     return retCode;
1233   }
1234 }
1235