xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/PanelItemOpen.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // PanelItemOpen.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/MyWindows.h"
6 
7 #include <TlHelp32.h>
8 
9 #include "../../../Common/IntToString.h"
10 
11 #include "../../../Common/AutoPtr.h"
12 
13 #include "../../../Windows/ProcessUtils.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/PropVariant.h"
16 #include "../../../Windows/PropVariantConv.h"
17 
18 #include "../../Common/FileStreams.h"
19 #include "../../Common/StreamObjects.h"
20 
21 #include "../Common/ExtractingFilePath.h"
22 
23 #include "App.h"
24 
25 #include "FileFolderPluginOpen.h"
26 #include "FormatUtils.h"
27 #include "LangUtils.h"
28 #include "PropertyNameRes.h"
29 #include "RegistryUtils.h"
30 #include "UpdateCallback100.h"
31 
32 #include "../GUI/ExtractRes.h"
33 
34 #include "resource.h"
35 
36 using namespace NWindows;
37 using namespace NSynchronization;
38 using namespace NFile;
39 using namespace NDir;
40 
41 extern bool g_RAM_Size_Defined;
42 extern size_t g_RAM_Size;
43 
44 #ifndef _UNICODE
45 extern bool g_IsNT;
46 #endif
47 
48 #define kTempDirPrefix FTEXT("7zO")
49 
50 // #define SHOW_DEBUG_INFO
51 
52 #ifdef SHOW_DEBUG_INFO
53   #define DEBUG_PRINT(s) OutputDebugStringA(s);
54   #define DEBUG_PRINT_W(s) OutputDebugStringW(s);
55   #define DEBUG_PRINT_NUM(s, num) { char ttt[32]; ConvertUInt32ToString(num, ttt); OutputDebugStringA(s); OutputDebugStringA(ttt); }
56 #else
57   #define DEBUG_PRINT(s)
58   #define DEBUG_PRINT_W(s)
59   #define DEBUG_PRINT_NUM(s, num)
60 #endif
61 
62 
63 
64 #ifndef UNDER_CE
65 
66 class CProcessSnapshot
67 {
68   HANDLE _handle;
69 public:
CProcessSnapshot()70   CProcessSnapshot(): _handle(INVALID_HANDLE_VALUE) {}
~CProcessSnapshot()71   ~CProcessSnapshot() { Close(); }
72 
Close()73   bool Close()
74   {
75     if (_handle == INVALID_HANDLE_VALUE)
76       return true;
77     if (!::CloseHandle(_handle))
78       return false;
79     _handle = INVALID_HANDLE_VALUE;
80     return true;
81   }
82 
Create()83   bool Create()
84   {
85     _handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
86     return (_handle != INVALID_HANDLE_VALUE);
87   }
88 
GetFirstProcess(PROCESSENTRY32 * pe)89   bool GetFirstProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32First(_handle, pe)); }
GetNextProcess(PROCESSENTRY32 * pe)90   bool GetNextProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32Next(_handle, pe)); }
91 };
92 
93 #endif
94 
95 
96 /*
97 struct COpenExtProg
98 {
99   const char *Ext;
100   const char *Prog;
101 };
102 
103 static const COpenExtProg g_Progs[] =
104 {
105   { "jpeg jpg png bmp gif", "Microsoft.Photos.exe" },
106   { "html htm pdf", "MicrosoftEdge.exe" },
107   // , { "rrr", "notepad.exe" }
108 };
109 
110 static bool FindExtProg(const char *exts, const char *ext)
111 {
112   unsigned len = (unsigned)strlen(ext);
113   for (;;)
114   {
115     const char *p = exts;
116     for (;; p++)
117     {
118       const char c = *p;
119       if (c == 0 || c == ' ')
120         break;
121     }
122     if (len == (unsigned)(p - exts) && IsString1PrefixedByString2(exts, ext))
123       return true;
124     if (*p == 0)
125       return false;
126     exts = p + 1;
127   }
128 }
129 
130 class CPossibleProgs
131 {
132 public:
133   AStringVector ProgNames;
134 
135   void SetFromExtension(const char *ext) // ext must be low case
136   {
137     ProgNames.Clear();
138     for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Progs); i++)
139       if (FindExtProg(g_Progs[i].Ext, ext))
140       {
141         ProgNames.Add(g_Progs[i].Prog);
142       }
143   }
144 
145   bool IsFromList(const UString &progName) const
146   {
147     FOR_VECTOR (i, ProgNames)
148       if (progName.IsEqualTo_Ascii_NoCase(ProgNames[i]))
149         return true;
150     return false;
151   }
152 };
153 */
154 
155 
156 #ifndef UNDER_CE
157 
158 EXTERN_C_BEGIN
159 
160 /*
161 GetProcessImageFileName
162   returns the path in device form, rather than drive letters:
163     \Device\HarddiskVolume1\WINDOWS\SysWOW64\notepad.exe
164 
165 GetModuleFileNameEx works only after Sleep(something). Why?
166   returns the path
167     C:\WINDOWS\system32\NOTEPAD.EXE
168 */
169 
170 /* Kernel32.dll: Win7, Win2008R2;
171    Psapi.dll: (if PSAPI_VERSION=1) on Win7 and Win2008R2;
172    Psapi.dll: XP, Win2003, Vista, 2008;
173 */
174 
175 typedef DWORD (WINAPI *Func_GetProcessImageFileNameW)(
176     HANDLE hProcess, LPWSTR lpFilename, DWORD nSize);
177 
178 typedef DWORD (WINAPI *Func_GetModuleFileNameExW)(
179     HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
180 
181 typedef DWORD (WINAPI *Func_GetProcessId)(HANDLE process);
182 
183 EXTERN_C_END
184 
185 
186 static HMODULE g_Psapi_dll_module;
187 
188 /*
189 static void My_GetProcessFileName_2(HANDLE hProcess, UString &path)
190 {
191   path.Empty();
192   const unsigned maxPath = 1024;
193   WCHAR temp[maxPath + 1];
194 
195   const char *func_name = "GetModuleFileNameExW";
196   Func_GetModuleFileNameExW my_func = (Func_GetModuleFileNameExW)
197     ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), func_name);
198   if (!my_func)
199   {
200     if (!g_Psapi_dll_module)
201       g_Psapi_dll_module = LoadLibraryW(L"Psapi.dll");
202     if (g_Psapi_dll_module)
203       my_func = (Func_GetModuleFileNameExW)::GetProcAddress(g_Psapi_dll_module, func_name);
204   }
205   if (my_func)
206   {
207     // DWORD num = GetModuleFileNameEx(hProcess, NULL, temp, maxPath);
208     DWORD num = my_func(hProcess, NULL, temp, maxPath);
209     if (num != 0)
210       path = temp;
211   }
212   // FreeLibrary(lib);
213 }
214 */
215 
216 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
217 
My_GetProcessFileName(HANDLE hProcess,UString & path)218 static void My_GetProcessFileName(HANDLE hProcess, UString &path)
219 {
220   path.Empty();
221   const unsigned maxPath = 1024;
222   WCHAR temp[maxPath + 1];
223 
224   const char *func_name =
225       "GetProcessImageFileNameW";
226   Func_GetProcessImageFileNameW my_func = Z7_GET_PROC_ADDRESS(
227   Func_GetProcessImageFileNameW, ::GetModuleHandleA("kernel32.dll"), func_name);
228 
229   if (!my_func)
230   {
231     if (!g_Psapi_dll_module)
232       g_Psapi_dll_module = LoadLibraryW(L"Psapi.dll");
233     if (g_Psapi_dll_module)
234       my_func = Z7_GET_PROC_ADDRESS(
235         Func_GetProcessImageFileNameW, g_Psapi_dll_module, func_name);
236   }
237 
238   if (my_func)
239   {
240     const DWORD num =
241     // GetProcessImageFileNameW(hProcess, temp, maxPath);
242     my_func(hProcess, temp, maxPath);
243     if (num != 0)
244       path = temp;
245   }
246   // FreeLibrary(lib);
247 }
248 
249 struct CSnapshotProcess
250 {
251   DWORD Id;
252   DWORD ParentId;
253   UString Name;
254 };
255 
GetSnapshot(CObjectVector<CSnapshotProcess> & items)256 static void GetSnapshot(CObjectVector<CSnapshotProcess> &items)
257 {
258   items.Clear();
259 
260   CProcessSnapshot snapshot;
261   if (!snapshot.Create())
262     return;
263 
264   DEBUG_PRINT("snapshot.Create() OK");
265   PROCESSENTRY32 pe;
266   CSnapshotProcess item;
267   memset(&pe, 0, sizeof(pe));
268   pe.dwSize = sizeof(pe);
269   BOOL res = snapshot.GetFirstProcess(&pe);
270   while (res)
271   {
272     item.Id = pe.th32ProcessID;
273     item.ParentId = pe.th32ParentProcessID;
274     item.Name = GetUnicodeString(pe.szExeFile);
275     items.Add(item);
276     res = snapshot.GetNextProcess(&pe);
277   }
278 }
279 
280 #endif
281 
282 
283 class CChildProcesses
284 {
285   #ifndef UNDER_CE
286   CRecordVector<DWORD> _ids;
287   #endif
288 
289 public:
290   // bool ProgsWereUsed;
291   CRecordVector<HANDLE> Handles;
292   CRecordVector<bool> NeedWait;
293   // UStringVector Names;
294 
295   #ifndef UNDER_CE
296   UString Path;
297   #endif
298 
299   // CChildProcesses(): ProgsWereUsed(false) {}
~CChildProcesses()300   ~CChildProcesses() { CloseAll(); }
DisableWait(unsigned index)301   void DisableWait(unsigned index) { NeedWait[index] = false; }
302 
CloseAll()303   void CloseAll()
304   {
305     FOR_VECTOR (i, Handles)
306     {
307       HANDLE h = Handles[i];
308       if (h != NULL)
309         CloseHandle(h);
310     }
311 
312     Handles.Clear();
313     NeedWait.Clear();
314     // Names.Clear();
315 
316     #ifndef UNDER_CE
317     // Path.Empty();
318     _ids.Clear();
319     #endif
320   }
321 
SetMainProcess(HANDLE h)322   void SetMainProcess(HANDLE h)
323   {
324     #ifndef UNDER_CE
325     const
326     Func_GetProcessId func = Z7_GET_PROC_ADDRESS(
327     Func_GetProcessId, ::GetModuleHandleA("kernel32.dll"),
328         "GetProcessId");
329     if (func)
330     {
331       const DWORD id = func(h);
332       if (id != 0)
333         _ids.AddToUniqueSorted(id);
334     }
335 
336     My_GetProcessFileName(h, Path);
337     DEBUG_PRINT_W(Path);
338 
339     #endif
340 
341     Handles.Add(h);
342     NeedWait.Add(true);
343   }
344 
345   #ifndef UNDER_CE
346 
Update(bool needFindProcessByPath)347   void Update(bool needFindProcessByPath /* , const CPossibleProgs &progs */)
348   {
349     /*
350     if (_ids.IsEmpty())
351       return;
352     */
353 
354     CObjectVector<CSnapshotProcess> sps;
355     GetSnapshot(sps);
356 
357     const int separ = Path.ReverseFind_PathSepar();
358     const UString mainName = Path.Ptr((unsigned)(separ + 1));
359     if (mainName.IsEmpty())
360       needFindProcessByPath = false;
361 
362     const DWORD currentProcessId = GetCurrentProcessId();
363 
364     for (;;)
365     {
366       bool wasAdded = false;
367 
368       FOR_VECTOR (i, sps)
369       {
370         const CSnapshotProcess &sp = sps[i];
371         const DWORD id = sp.Id;
372 
373         if (id == currentProcessId)
374           continue;
375         if (_ids.FindInSorted(id) >= 0)
376           continue;
377 
378         bool isSameName = false;
379         const UString &name = sp.Name;
380 
381         if (needFindProcessByPath)
382           isSameName = mainName.IsEqualTo_NoCase(name);
383 
384         bool needAdd = false;
385         // bool isFromProgs = false;
386 
387         if (isSameName || _ids.FindInSorted(sp.ParentId) >= 0)
388           needAdd = true;
389         /*
390         else if (progs.IsFromList(name))
391         {
392           needAdd = true;
393           isFromProgs = true;
394         }
395         */
396 
397         if (needAdd)
398         {
399           DEBUG_PRINT("----- OpenProcess -----");
400           DEBUG_PRINT_W(name);
401           HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, id);
402           if (hProcess)
403           {
404             DEBUG_PRINT("----- OpenProcess OK -----");
405             // if (!isFromProgs)
406               _ids.AddToUniqueSorted(id);
407             Handles.Add(hProcess);
408             NeedWait.Add(true);
409             // Names.Add(name);
410             wasAdded = true;
411             // ProgsWereUsed = isFromProgs;
412           }
413         }
414       }
415 
416       if (!wasAdded)
417         break;
418     }
419   }
420 
421   #endif
422 };
423 
424 
425 struct CTmpProcessInfo: public CTempFileInfo
426 {
427   CChildProcesses Processes;
428   HWND Window;
429   UString FullPathFolderPrefix;
430   bool UsePassword;
431   UString Password;
432 
433   bool ReadOnly;
434 
CTmpProcessInfoCTmpProcessInfo435   CTmpProcessInfo(): UsePassword(false), ReadOnly(false) {}
436 };
437 
438 
439 class CTmpProcessInfoRelease
440 {
441   CTmpProcessInfo *_tmpProcessInfo;
442 public:
443   bool _needDelete;
CTmpProcessInfoRelease(CTmpProcessInfo & tpi)444   CTmpProcessInfoRelease(CTmpProcessInfo &tpi):
445       _tmpProcessInfo(&tpi), _needDelete(true) {}
~CTmpProcessInfoRelease()446   ~CTmpProcessInfoRelease()
447   {
448     if (_needDelete)
449       _tmpProcessInfo->DeleteDirAndFile();
450   }
451 };
452 
453 void GetFolderError(CMyComPtr<IFolderFolder> &folder, UString &s);
454 
455 
OpenAsArc(IInStream * inStream,const CTempFileInfo & tempFileInfo,const UString & virtualFilePath,const UString & arcFormat,COpenResult & openRes)456 HRESULT CPanel::OpenAsArc(IInStream *inStream,
457     const CTempFileInfo &tempFileInfo,
458     const UString &virtualFilePath,
459     const UString &arcFormat,
460     COpenResult &openRes)
461 {
462   openRes.Encrypted = false;
463   CFolderLink folderLink;
464   (CTempFileInfo &)folderLink = tempFileInfo;
465 
466   if (inStream)
467     folderLink.IsVirtual = true;
468   else
469   {
470     if (!folderLink.FileInfo.Find(folderLink.FilePath))
471       return GetLastError_noZero_HRESULT();
472     if (folderLink.FileInfo.IsDir())
473       return S_FALSE;
474     folderLink.IsVirtual = false;
475   }
476 
477   folderLink.VirtualPath = virtualFilePath;
478 
479   CFfpOpen ffp;
480   const HRESULT res = ffp.OpenFileFolderPlugin(inStream,
481       folderLink.FilePath.IsEmpty() ? us2fs(virtualFilePath) : folderLink.FilePath,
482       arcFormat, GetParent());
483 
484   openRes.Encrypted = ffp.Encrypted;
485   openRes.ErrorMessage = ffp.ErrorMessage;
486 
487   RINOK(res)
488 
489   folderLink.Password = ffp.Password;
490   folderLink.UsePassword = ffp.Encrypted;
491 
492   if (_folder)
493     folderLink.ParentFolderPath = GetFolderPath(_folder);
494   else
495     folderLink.ParentFolderPath = _currentFolderPrefix;
496 
497   if (!_parentFolders.IsEmpty())
498     folderLink.ParentFolder = _folder;
499 
500   _parentFolders.Add(folderLink);
501   _parentFolders.Back().Library.Attach(_library.Detach());
502 
503   ReleaseFolder();
504   _library.Free();
505   SetNewFolder(ffp.Folder);
506   _library.Attach(ffp.Library.Detach());
507 
508   _flatMode = _flatModeForArc;
509 
510   _thereAreDeletedItems = false;
511 
512   if (!openRes.ErrorMessage.IsEmpty())
513     MessageBox_Error(openRes.ErrorMessage);
514   /*
515   UString s;
516   GetFolderError(_folder, s);
517   if (!s.IsEmpty())
518     MessageBox_Error(s);
519   */
520   // we don't show error here by some reasons:
521   // after MessageBox_Warning it throws exception in nested archives in Debug Mode. why ?.
522   // MessageBox_Warning(L"test error");
523 
524   return S_OK;
525 }
526 
527 
OpenAsArc_Msg(IInStream * inStream,const CTempFileInfo & tempFileInfo,const UString & virtualFilePath,const UString & arcFormat)528 HRESULT CPanel::OpenAsArc_Msg(IInStream *inStream,
529     const CTempFileInfo &tempFileInfo,
530     const UString &virtualFilePath,
531     const UString &arcFormat
532     // , bool &encrypted
533     // , bool showErrorMessage
534     )
535 {
536   COpenResult opRes;
537 
538   HRESULT res = OpenAsArc(inStream, tempFileInfo, virtualFilePath, arcFormat, opRes);
539 
540   if (res == S_OK)
541     return res;
542   if (res == E_ABORT)
543     return res;
544 
545   // if (showErrorMessage)
546   if (opRes.Encrypted || res != S_FALSE) // 17.01 : we show message also for (res != S_FALSE)
547   {
548     UString message;
549     if (res == S_FALSE)
550     {
551       message = MyFormatNew(
552           opRes.Encrypted ?
553             IDS_CANT_OPEN_ENCRYPTED_ARCHIVE :
554             IDS_CANT_OPEN_ARCHIVE,
555           virtualFilePath);
556     }
557     else
558       message = HResultToMessage(res);
559     MessageBox_Error(message);
560   }
561 
562   return res;
563 }
564 
565 
OpenAsArc_Name(const UString & relPath,const UString & arcFormat)566 HRESULT CPanel::OpenAsArc_Name(const UString &relPath, const UString &arcFormat
567     // , bool &encrypted,
568     // , bool showErrorMessage
569     )
570 {
571   CTempFileInfo tfi;
572   tfi.RelPath = relPath;
573   tfi.FolderPath = us2fs(GetFsPath());
574   const UString fullPath = GetFsPath() + relPath;
575   tfi.FilePath = us2fs(fullPath);
576   return OpenAsArc_Msg(NULL, tfi, fullPath, arcFormat /* , encrypted, showErrorMessage */);
577 }
578 
579 
OpenAsArc_Index(unsigned index,const wchar_t * type)580 HRESULT CPanel::OpenAsArc_Index(unsigned index, const wchar_t *type
581     // , bool showErrorMessage
582     )
583 {
584   CDisableTimerProcessing disableTimerProcessing1(*this);
585   CDisableNotify disableNotify(*this);
586 
587   HRESULT res = OpenAsArc_Name(GetItemRelPath2(index), type ? type : L"" /* , encrypted, showErrorMessage */);
588   if (res != S_OK)
589   {
590     RefreshTitle(true); // in case of error we must refresh changed title of 7zFM
591     return res;
592   }
593   RefreshListCtrl();
594   return S_OK;
595 }
596 
597 
OpenParentArchiveFolder()598 HRESULT CPanel::OpenParentArchiveFolder()
599 {
600   CDisableTimerProcessing disableTimerProcessing(*this);
601   CDisableNotify disableNotify(*this);
602   if (_parentFolders.Size() < 2)
603     return S_OK;
604   const CFolderLink &folderLinkPrev = _parentFolders[_parentFolders.Size() - 2];
605   const CFolderLink &folderLink = _parentFolders.Back();
606   NFind::CFileInfo newFileInfo;
607   if (newFileInfo.Find(folderLink.FilePath))
608   {
609     if (folderLink.WasChanged_from_FolderLink(newFileInfo))
610     {
611       const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, folderLink.RelPath);
612       if (::MessageBoxW((HWND)*this, message, L"7-Zip", MB_YESNOCANCEL | MB_ICONQUESTION) == IDYES)
613       {
614         if (OnOpenItemChanged(folderLink.FileIndex, fs2us(folderLink.FilePath),
615             folderLinkPrev.UsePassword, folderLinkPrev.Password) != S_OK)
616         {
617           ::MessageBoxW((HWND)*this, MyFormatNew(IDS_CANNOT_UPDATE_FILE,
618               fs2us(folderLink.FilePath)), L"7-Zip", MB_OK | MB_ICONSTOP);
619           return S_OK;
620         }
621       }
622     }
623   }
624   folderLink.DeleteDirAndFile();
625   return S_OK;
626 }
627 
628 
629 static const char * const kExeExtensions =
630   " exe bat ps1 com lnk"
631   " ";
632 
633 static const char * const kStartExtensions =
634   #ifdef UNDER_CE
635   " cab"
636   #endif
637   " exe bat ps1 com lnk"
638   " chm"
639   " msi doc dot xls ppt pps wps wpt wks xlr wdb vsd pub"
640 
641   " docx docm dotx dotm xlsx xlsm xltx xltm xlsb xps"
642   " xlam pptx pptm potx potm ppam ppsx ppsm vsdx xsn"
643   " mpp"
644   " msg"
645   " dwf"
646 
647   " flv swf"
648 
649   " epub"
650   " odt ods"
651   " wb3"
652   " pdf"
653   " ps"
654   " txt"
655   " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
656   " h hpp hxx c cpp cxx m mm go swift"
657   " awk sed hta js json php php3 php4 php5 phptml pl pm py pyo rb tcl ts vbs"
658   " asm"
659   " mak clw csproj vcproj sln dsp dsw"
660   " ";
661 
662 // bool FindExt(const char *p, const UString &name, AString &s);
663 bool FindExt(const char *p, const UString &name, CStringFinder &finder);
664 
DoItemAlwaysStart(const UString & name)665 static bool DoItemAlwaysStart(const UString &name)
666 {
667   CStringFinder finder;
668   return FindExt(kStartExtensions, name, finder);
669 }
670 
671 UString GetQuotedString(const UString &s);
672 
673 
674 void SplitCmdLineSmart(const UString &cmd, UString &prg, UString &params);
SplitCmdLineSmart(const UString & cmd,UString & prg,UString & params)675 void SplitCmdLineSmart(const UString &cmd, UString &prg, UString &params)
676 {
677   params.Empty();
678   prg = cmd;
679   prg.Trim();
680   if (prg.Len() >= 2 && prg[0] == L'"')
681   {
682     int pos = prg.Find(L'"', 1);
683     if (pos >= 0)
684     {
685       if ((unsigned)(pos + 1) == prg.Len() || prg[pos + 1] == ' ')
686       {
687         params = prg.Ptr((unsigned)(pos + 1));
688         params.Trim();
689         prg.DeleteFrom((unsigned)pos);
690         prg.DeleteFrontal(1);
691       }
692     }
693   }
694 }
695 
696 
StartAppWithParams(const UString & cmd,const UStringVector & paramVector,CProcess & process)697 static WRes StartAppWithParams(const UString &cmd, const UStringVector &paramVector, CProcess &process)
698 {
699   UString param;
700   UString prg;
701 
702   SplitCmdLineSmart(cmd, prg, param);
703 
704   param.Trim();
705 
706   // int pos = params.Find(L"%1");
707 
708   FOR_VECTOR (i, paramVector)
709   {
710     if (!param.IsEmpty() && param.Back() != ' ')
711       param.Add_Space();
712     param += GetQuotedString(paramVector[i]);
713   }
714 
715   return process.Create(prg, param, NULL);
716 }
717 
718 
StartEditApplication(const UString & path,bool useEditor,HWND window,CProcess & process)719 static HRESULT StartEditApplication(const UString &path, bool useEditor, HWND window, CProcess &process)
720 {
721   UString command;
722   ReadRegEditor(useEditor, command);
723   if (command.IsEmpty())
724   {
725     #ifdef UNDER_CE
726     command = "\\Windows\\";
727     #else
728     FString winDir;
729     if (!GetWindowsDir(winDir))
730       return 0;
731     NName::NormalizeDirPathPrefix(winDir);
732     command = fs2us(winDir);
733     #endif
734     command += "notepad.exe";
735   }
736 
737   UStringVector params;
738   params.Add(path);
739 
740   const WRes res = StartAppWithParams(command, params, process);
741   if (res != 0)
742     ::MessageBoxW(window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK  | MB_ICONSTOP);
743   return HRESULT_FROM_WIN32(res);
744 }
745 
746 
DiffFiles()747 void CApp::DiffFiles()
748 {
749   const CPanel &panel = GetFocusedPanel();
750 
751   if (!panel.Is_IO_FS_Folder())
752   {
753     panel.MessageBox_Error_UnsupportOperation();
754     return;
755   }
756 
757   CRecordVector<UInt32> indices;
758   panel.Get_ItemIndices_Selected(indices);
759 
760   UString path1, path2;
761   if (indices.Size() == 2)
762   {
763     path1 = panel.GetItemFullPath(indices[0]);
764     path2 = panel.GetItemFullPath(indices[1]);
765   }
766   else if (indices.Size() == 1 && NumPanels >= 2)
767   {
768     const CPanel &destPanel = Panels[1 - LastFocusedPanel];
769 
770     if (!destPanel.Is_IO_FS_Folder())
771     {
772       panel.MessageBox_Error_UnsupportOperation();
773       return;
774     }
775 
776     path1 = panel.GetItemFullPath(indices[0]);
777     CRecordVector<UInt32> indices2;
778     destPanel.Get_ItemIndices_Selected(indices2);
779     if (indices2.Size() == 1)
780       path2 = destPanel.GetItemFullPath(indices2[0]);
781     else
782     {
783       UString relPath = panel.GetItemRelPath2(indices[0]);
784       if (panel._flatMode && !destPanel._flatMode)
785         relPath = panel.GetItemName(indices[0]);
786       path2 = destPanel._currentFolderPrefix + relPath;
787     }
788   }
789   else
790     return;
791 
792   DiffFiles(path1, path2);
793 }
794 
DiffFiles(const UString & path1,const UString & path2)795 void CApp::DiffFiles(const UString &path1, const UString &path2)
796 {
797   UString command;
798   ReadRegDiff(command);
799   if (command.IsEmpty())
800     return;
801 
802   UStringVector params;
803   params.Add(path1);
804   params.Add(path2);
805 
806   WRes res;
807   {
808     CProcess process;
809     res = StartAppWithParams(command, params, process);
810   }
811   if (res == 0)
812     return;
813   ::MessageBoxW(_window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK  | MB_ICONSTOP);
814 }
815 
816 
817 HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process);
818 void StartApplicationDontWait(const UString &dir, const UString &path, HWND window);
819 
EditItem(unsigned index,bool useEditor)820 void CPanel::EditItem(unsigned index, bool useEditor)
821 {
822   if (!_parentFolders.IsEmpty())
823   {
824     OpenItemInArchive(index, false, true, true, useEditor);
825     return;
826   }
827   CProcess process;
828   StartEditApplication(GetItemFullPath(index), useEditor, (HWND)*this, process);
829 }
830 
831 
OpenFolderExternal(unsigned index)832 void CPanel::OpenFolderExternal(unsigned index)
833 {
834   UString prefix = GetFsPath();
835   UString path = prefix;
836 
837   if (index == kParentIndex)
838   {
839     if (prefix.IsEmpty())
840       return;
841     const wchar_t c = prefix.Back();
842     if (!IS_PATH_SEPAR(c) && c != ':')
843       return;
844     prefix.DeleteBack();
845     int pos = prefix.ReverseFind_PathSepar();
846     if (pos < 0)
847       return;
848     prefix.DeleteFrom((unsigned)(pos + 1));
849     path = prefix;
850   }
851   else
852   {
853     path += GetItemRelPath(index);
854     path.Add_PathSepar();
855   }
856 
857   StartApplicationDontWait(prefix, path, (HWND)*this);
858 }
859 
860 
IsVirus_Message(const UString & name)861 bool CPanel::IsVirus_Message(const UString &name)
862 {
863   UString name2;
864 
865   const wchar_t cRLO = (wchar_t)0x202E;
866   bool isVirus = false;
867   bool isSpaceError = false;
868   name2 = name;
869 
870   if (name2.Find(cRLO) >= 0)
871   {
872     const UString badString(cRLO);
873     name2.Replace(badString, L"[RLO]");
874     isVirus = true;
875   }
876   {
877     const wchar_t * const kVirusSpaces = L"     ";
878     // const unsigned kNumSpaces = strlen(kVirusSpaces);
879     for (;;)
880     {
881       int pos = name2.Find(kVirusSpaces);
882       if (pos < 0)
883         break;
884       isVirus = true;
885       isSpaceError = true;
886       name2.Replace(kVirusSpaces, L" ");
887     }
888   }
889 
890   #ifdef _WIN32
891   {
892     unsigned i;
893     for (i = name2.Len(); i != 0;)
894     {
895       wchar_t c = name2[i - 1];
896       if (c != '.' && c != ' ')
897         break;
898       i--;
899       name2.ReplaceOneCharAtPos(i, '_');
900     }
901     if (i != name2.Len())
902     {
903       CStringFinder finder;
904       UString name3 = name2;
905       name3.DeleteFrom(i);
906       if (FindExt(kExeExtensions, name3, finder))
907         isVirus = true;
908     }
909   }
910   #endif
911 
912   if (!isVirus)
913     return false;
914 
915   UString s = LangString(IDS_VIRUS);
916 
917   if (!isSpaceError)
918   {
919     const int pos1 = s.Find(L'(');
920     if (pos1 >= 0)
921     {
922       const int pos2 = s.Find(L')', (unsigned)pos1 + 1);
923       if (pos2 >= 0)
924       {
925         s.Delete((unsigned)pos1, (unsigned)pos2 + 1 - (unsigned)pos1);
926         if (pos1 > 0 && s[pos1 - 1] == ' ' && s[pos1] == '.')
927           s.Delete(pos1 - 1);
928       }
929     }
930   }
931 
932   UString name3 = name;
933   name3.Replace(L'\n', L'_');
934   name2.Replace(L'\n', L'_');
935 
936   s.Add_LF(); s += name2;
937   s.Add_LF(); s += name3;
938 
939   MessageBox_Error(s);
940   return true;
941 }
942 
943 
OpenItem(unsigned index,bool tryInternal,bool tryExternal,const wchar_t * type)944 void CPanel::OpenItem(unsigned index, bool tryInternal, bool tryExternal, const wchar_t *type)
945 {
946   CDisableTimerProcessing disableTimerProcessing(*this);
947   const UString name = GetItemRelPath2(index);
948 
949   if (tryExternal)
950     if (IsVirus_Message(name))
951       return;
952 
953   if (!_parentFolders.IsEmpty())
954   {
955     OpenItemInArchive(index, tryInternal, tryExternal, false, false, type);
956     return;
957   }
958 
959   CDisableNotify disableNotify(*this);
960   UString prefix = GetFsPath();
961   UString fullPath = prefix + name;
962 
963   if (tryInternal)
964     if (!tryExternal || !DoItemAlwaysStart(name))
965     {
966       HRESULT res = OpenAsArc_Index(index, type
967           // , true
968           );
969       disableNotify.Restore(); // we must restore to allow text notification update
970       InvalidateList();
971       if (res == S_OK || res == E_ABORT)
972         return;
973       if (res != S_FALSE)
974       {
975         MessageBox_Error_HRESULT(res);
976         return;
977       }
978     }
979 
980   if (tryExternal)
981   {
982     // SetCurrentDirectory opens HANDLE to folder!!!
983     // NDirectory::MySetCurrentDirectory(prefix);
984     StartApplicationDontWait(prefix, fullPath, (HWND)*this);
985   }
986 }
987 
988 class CThreadCopyFrom: public CProgressThreadVirt
989 {
990   HRESULT ProcessVirt() Z7_override;
991 public:
992   UString FullPath;
993   UInt32 ItemIndex;
994 
995   CMyComPtr<IFolderOperations> FolderOperations;
996   CMyComPtr<IProgress> UpdateCallback;
997   CUpdateCallback100Imp *UpdateCallbackSpec;
998 };
999 
ProcessVirt()1000 HRESULT CThreadCopyFrom::ProcessVirt()
1001 {
1002   return FolderOperations->CopyFromFile(ItemIndex, FullPath, UpdateCallback);
1003 }
1004 
OnOpenItemChanged(UInt32 index,const wchar_t * fullFilePath,bool usePassword,const UString & password)1005 HRESULT CPanel::OnOpenItemChanged(UInt32 index, const wchar_t *fullFilePath,
1006     bool usePassword, const UString &password)
1007 {
1008   if (!_folderOperations)
1009   {
1010     MessageBox_Error_UnsupportOperation();
1011     return E_FAIL;
1012   }
1013 
1014   CThreadCopyFrom t;
1015   t.UpdateCallbackSpec = new CUpdateCallback100Imp;
1016   t.UpdateCallback = t.UpdateCallbackSpec;
1017   t.UpdateCallbackSpec->ProgressDialog = &t;
1018   t.ItemIndex = index;
1019   t.FullPath = fullFilePath;
1020   t.FolderOperations = _folderOperations;
1021 
1022   t.UpdateCallbackSpec->Init();
1023   t.UpdateCallbackSpec->PasswordIsDefined = usePassword;
1024   t.UpdateCallbackSpec->Password = password;
1025 
1026 
1027   RINOK(t.Create(GetItemName(index), (HWND)*this))
1028   return t.Result;
1029 }
1030 
OnOpenItemChanged(LPARAM lParam)1031 LRESULT CPanel::OnOpenItemChanged(LPARAM lParam)
1032 {
1033   // DEBUG_PRINT_NUM("OnOpenItemChanged", GetCurrentThreadId());
1034 
1035   CTmpProcessInfo &tpi = *(CTmpProcessInfo *)lParam;
1036   if (tpi.FullPathFolderPrefix != _currentFolderPrefix)
1037     return 0;
1038   UInt32 fileIndex = tpi.FileIndex;
1039   UInt32 numItems;
1040   _folder->GetNumberOfItems(&numItems);
1041 
1042   // This code is not 100% OK for cases when there are several files with
1043   // tpi.RelPath name and there are changes in archive before update.
1044   // So tpi.FileIndex can point to another file.
1045 
1046   if (fileIndex >= numItems || GetItemRelPath(fileIndex) != tpi.RelPath)
1047   {
1048     UInt32 i;
1049     for (i = 0; i < numItems; i++)
1050       if (GetItemRelPath(i) == tpi.RelPath)
1051         break;
1052     if (i == numItems)
1053       return 0;
1054     fileIndex = i;
1055   }
1056 
1057   CSelectedState state;
1058   SaveSelectedState(state);
1059 
1060   CDisableNotify disableNotify(*this); // do we need it??
1061 
1062   HRESULT result = OnOpenItemChanged(fileIndex, fs2us(tpi.FilePath), tpi.UsePassword, tpi.Password);
1063   RefreshListCtrl(state);
1064   if (result != S_OK)
1065     return 0;
1066   return 1;
1067 }
1068 
1069 
1070 CExitEventLauncher g_ExitEventLauncher;
1071 
Exit(bool hardExit)1072 void CExitEventLauncher::Exit(bool hardExit)
1073 {
1074   if (_needExit)
1075   {
1076     _exitEvent.Set();
1077     _needExit = false;
1078   }
1079 
1080   if (_numActiveThreads == 0)
1081     return;
1082 
1083   FOR_VECTOR (i, _threads)
1084   {
1085     ::CThread &th = _threads[i];
1086     if (Thread_WasCreated(&th))
1087     {
1088       const DWORD waitResult = WaitForSingleObject(th, hardExit ? 100 : INFINITE);
1089       // Thread_Wait(&th);
1090       // if (waitResult == WAIT_TIMEOUT) wait = 1;
1091       if (!hardExit && waitResult != WAIT_OBJECT_0)
1092         continue;
1093       Thread_Close(&th);
1094       _numActiveThreads--;
1095     }
1096   }
1097 }
1098 
1099 
1100 
MyThreadFunction(void * param)1101 static THREAD_FUNC_DECL MyThreadFunction(void *param)
1102 {
1103   DEBUG_PRINT("==== MyThreadFunction ====");
1104 
1105   CMyUniquePtr<CTmpProcessInfo> tpi((CTmpProcessInfo *)param);
1106   CChildProcesses &processes = tpi->Processes;
1107 
1108   const bool mainProcessWasSet = !processes.Handles.IsEmpty();
1109 
1110   bool isComplexMode = true;
1111 
1112   if (!processes.Handles.IsEmpty())
1113   {
1114 
1115   const DWORD startTime = GetTickCount();
1116 
1117   /*
1118   CPossibleProgs progs;
1119   {
1120     const UString &name = tpi->RelPath;
1121     int slashPos = name.ReverseFind_PathSepar();
1122     int dotPos = name.ReverseFind_Dot();
1123     if (dotPos > slashPos)
1124     {
1125       const UString ext = name.Ptr(dotPos + 1);
1126       AString extA = UnicodeStringToMultiByte(ext);
1127       extA.MakeLower_Ascii();
1128       progs.SetFromExtension(extA);
1129     }
1130   }
1131   */
1132 
1133   bool firstPass = true;
1134 
1135   for (;;)
1136   {
1137     CRecordVector<HANDLE> handles;
1138     CUIntVector indices;
1139 
1140     FOR_VECTOR (i, processes.Handles)
1141     {
1142       if (handles.Size() > 60)
1143         break;
1144       if (processes.NeedWait[i])
1145       {
1146         handles.Add(processes.Handles[i]);
1147         indices.Add(i);
1148       }
1149     }
1150 
1151     bool needFindProcessByPath = false;
1152 
1153     if (handles.IsEmpty())
1154     {
1155       if (!firstPass)
1156         break;
1157     }
1158     else
1159     {
1160       handles.Add(g_ExitEventLauncher._exitEvent);
1161 
1162       DWORD waitResult = WaitForMultiObj_Any_Infinite(handles.Size(), handles.ConstData());
1163 
1164       waitResult -= WAIT_OBJECT_0;
1165 
1166       if (waitResult >= handles.Size() - 1)
1167       {
1168         processes.CloseAll();
1169         /*
1170         if (waitResult == handles.Size() - 1)
1171         {
1172           // exit event
1173           // we want to delete temp files, if progs were used
1174           if (processes.ProgsWereUsed)
1175             break;
1176         }
1177         */
1178         return waitResult >= (DWORD)handles.Size() ? 1 : 0;
1179       }
1180 
1181       if (firstPass && indices.Size() == 1)
1182       {
1183         const DWORD curTime = GetTickCount() - startTime;
1184 
1185         /*
1186         if (curTime > 5 * 1000)
1187           progs.ProgNames.Clear();
1188         */
1189 
1190         needFindProcessByPath = (curTime < 2 * 1000);
1191 
1192         if (needFindProcessByPath)
1193         {
1194           NFind::CFileInfo newFileInfo;
1195           if (newFileInfo.Find(tpi->FilePath))
1196             if (tpi->WasChanged_from_TempFileInfo(newFileInfo))
1197               needFindProcessByPath = false;
1198         }
1199 
1200         DEBUG_PRINT_NUM(" -- firstPass -- time = ", curTime)
1201       }
1202 
1203       processes.DisableWait(indices[(unsigned)waitResult]);
1204     }
1205 
1206     firstPass = false;
1207 
1208     // Sleep(300);
1209     #ifndef UNDER_CE
1210     processes.Update(needFindProcessByPath /* , progs */);
1211     #endif
1212   }
1213 
1214 
1215   const DWORD curTime = GetTickCount() - startTime;
1216 
1217   DEBUG_PRINT_NUM("after time = ", curTime)
1218 
1219   processes.CloseAll();
1220 
1221   isComplexMode = (curTime < 2 * 1000);
1222 
1223   }
1224 
1225   bool needCheckTimestamp = true;
1226 
1227   for (;;)
1228   {
1229     NFind::CFileInfo newFileInfo;
1230 
1231     if (!newFileInfo.Find(tpi->FilePath))
1232       break;
1233 
1234     if (mainProcessWasSet)
1235     {
1236       if (tpi->WasChanged_from_TempFileInfo(newFileInfo))
1237       {
1238         UString m = MyFormatNew(IDS_CANNOT_UPDATE_FILE, fs2us(tpi->FilePath));
1239         if (tpi->ReadOnly)
1240         {
1241           m.Add_LF();
1242           AddLangString(m, IDS_PROP_READ_ONLY);
1243           m.Add_LF();
1244           m += tpi->FullPathFolderPrefix;
1245           ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP);
1246           return 0;
1247         }
1248         {
1249           const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, tpi->RelPath);
1250           if (::MessageBoxW(g_HWND, message, L"7-Zip", MB_YESNOCANCEL | MB_ICONQUESTION) == IDYES)
1251           {
1252             // DEBUG_PRINT_NUM("SendMessage", GetCurrentThreadId());
1253             if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi.get()) != 1)
1254             {
1255               ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP);
1256               return 0;
1257             }
1258           }
1259           needCheckTimestamp = false;
1260           break;
1261         }
1262       }
1263 
1264       if (!isComplexMode)
1265         break;
1266     }
1267 
1268     // DEBUG_PRINT("WaitForSingleObject");
1269     DWORD waitResult = ::WaitForSingleObject(g_ExitEventLauncher._exitEvent, INFINITE);
1270     // DEBUG_PRINT("---");
1271 
1272     if (waitResult == WAIT_OBJECT_0)
1273       break;
1274 
1275     return 1;
1276   }
1277 
1278   {
1279     NFind::CFileInfo newFileInfo;
1280     const bool finded = newFileInfo.Find(tpi->FilePath);
1281     if (!needCheckTimestamp
1282         || !finded
1283         || !tpi->WasChanged_from_TempFileInfo(newFileInfo))
1284     {
1285       DEBUG_PRINT("Delete Temp file");
1286       tpi->DeleteDirAndFile();
1287     }
1288   }
1289 
1290   return 0;
1291 }
1292 
1293 
1294 /*
1295 #if defined(_WIN32) && !defined(UNDER_CE)
1296 static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier");
1297 #endif
1298 
1299 
1300 #ifndef UNDER_CE
1301 
1302 static void ReadZoneFile(CFSTR fileName, CByteBuffer &buf)
1303 {
1304   buf.Free();
1305   NIO::CInFile file;
1306   if (!file.Open(fileName))
1307     return;
1308   UInt64 fileSize;
1309   if (!file.GetLength(fileSize))
1310     return;
1311   if (fileSize == 0 || fileSize >= ((UInt32)1 << 20))
1312     return;
1313   buf.Alloc((size_t)fileSize);
1314   size_t processed;
1315   if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize)
1316     return;
1317   buf.Free();
1318 }
1319 
1320 static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf)
1321 {
1322   NIO::COutFile file;
1323   if (!file.Create(fileName, true))
1324     return false;
1325   UInt32 processed;
1326   if (!file.Write(buf, (UInt32)buf.Size(), processed))
1327     return false;
1328   return processed == buf.Size();
1329 }
1330 
1331 #endif
1332 */
1333 
1334 /*
1335 Z7_CLASS_IMP_COM_1(
1336   CBufSeqOutStream_WithFile
1337   , ISequentialOutStream
1338 )
1339   Byte *_buffer;
1340   size_t _size;
1341   size_t _pos;
1342 
1343   size_t _fileWritePos;
1344   bool fileMode;
1345 public:
1346 
1347   bool IsStreamInMem() const { return !fileMode; }
1348   size_t GetMemStreamWrittenSize() const { return _pos; }
1349 
1350   // ISequentialOutStream *FileStream;
1351   FString FilePath;
1352   COutFileStream *outFileStreamSpec;
1353   CMyComPtr<ISequentialOutStream> outFileStream;
1354 
1355   CBufSeqOutStream_WithFile(): outFileStreamSpec(NULL) {}
1356 
1357   void Init(Byte *buffer, size_t size)
1358   {
1359     fileMode = false;
1360     _buffer = buffer;
1361     _pos = 0;
1362     _size = size;
1363     _fileWritePos = 0;
1364   }
1365 
1366   HRESULT FlushToFile();
1367   size_t GetPos() const { return _pos; }
1368 };
1369 
1370 static const UInt32 kBlockSize = ((UInt32)1 << 31);
1371 
1372 STDMETHODIMP CBufSeqOutStream_WithFile::Write(const void *data, UInt32 size, UInt32 *processedSize)
1373 {
1374   if (processedSize)
1375     *processedSize = 0;
1376   if (!fileMode)
1377   {
1378     if (_size - _pos >= size)
1379     {
1380       if (size != 0)
1381       {
1382         memcpy(_buffer + _pos, data, size);
1383         _pos += size;
1384       }
1385       if (processedSize)
1386         *processedSize = (UInt32)size;
1387       return S_OK;
1388     }
1389 
1390     fileMode = true;
1391   }
1392   RINOK(FlushToFile());
1393   return outFileStream->Write(data, size, processedSize);
1394 }
1395 
1396 HRESULT CBufSeqOutStream_WithFile::FlushToFile()
1397 {
1398   if (!outFileStream)
1399   {
1400     outFileStreamSpec = new COutFileStream;
1401     outFileStream = outFileStreamSpec;
1402     if (!outFileStreamSpec->Create(FilePath, false))
1403     {
1404       outFileStream.Release();
1405       return E_FAIL;
1406       // MessageBoxMyError(UString("Can't create file ") + fs2us(tempFilePath));
1407     }
1408   }
1409   while (_fileWritePos != _pos)
1410   {
1411     size_t cur = _pos - _fileWritePos;
1412     UInt32 curSize = (cur < kBlockSize) ? (UInt32)cur : kBlockSize;
1413     UInt32 processedSizeLoc = 0;
1414     HRESULT res = outFileStream->Write(_buffer + _fileWritePos, curSize, &processedSizeLoc);
1415     _fileWritePos += processedSizeLoc;
1416     RINOK(res);
1417     if (processedSizeLoc == 0)
1418       return E_FAIL;
1419   }
1420   return S_OK;
1421 }
1422 */
1423 
1424 /*
1425 static HRESULT GetTime(IFolderFolder *folder, UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
1426 {
1427   filetimeIsDefined = false;
1428   NCOM::CPropVariant prop;
1429   RINOK(folder->GetProperty(index, propID, &prop));
1430   if (prop.vt == VT_FILETIME)
1431   {
1432     filetime = prop.filetime;
1433     filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
1434   }
1435   else if (prop.vt != VT_EMPTY)
1436     return E_FAIL;
1437   return S_OK;
1438 }
1439 */
1440 
1441 
1442 /*
1443 tryInternal tryExternal
1444   false       false      : unused
1445   false       true       : external
1446   true        false      : internal
1447   true        true       : smart based on file extension:
1448                       !alwaysStart(name) : both
1449                       alwaysStart(name)  : external
1450 */
1451 
OpenItemInArchive(unsigned index,bool tryInternal,bool tryExternal,bool editMode,bool useEditor,const wchar_t * type)1452 void CPanel::OpenItemInArchive(unsigned index, bool tryInternal, bool tryExternal, bool editMode, bool useEditor, const wchar_t *type)
1453 {
1454   // we don't want to change hash data here
1455   if (IsHashFolder())
1456     return;
1457 
1458   const UString name = GetItemName(index);
1459   const UString relPath = GetItemRelPath(index);
1460 
1461   if (tryExternal)
1462     if (IsVirus_Message(name))
1463       return;
1464 
1465   if (!_folderOperations)
1466   {
1467     MessageBox_Error_UnsupportOperation();
1468     return;
1469   }
1470 
1471   bool tryAsArchive = tryInternal && (!tryExternal || !DoItemAlwaysStart(name));
1472 
1473   const UString fullVirtPath = _currentFolderPrefix + relPath;
1474 
1475   CTempDir tempDirectory;
1476   if (!tempDirectory.Create(kTempDirPrefix))
1477   {
1478     MessageBox_LastError();
1479     return;
1480   }
1481 
1482   FString tempDir = tempDirectory.GetPath();
1483   FString tempDirNorm = tempDir;
1484   NName::NormalizeDirPathPrefix(tempDirNorm);
1485   const FString tempFilePath = tempDirNorm + us2fs(Get_Correct_FsFile_Name(name));
1486 
1487   CTempFileInfo tempFileInfo;
1488   tempFileInfo.FileIndex = index;
1489   tempFileInfo.RelPath = relPath;
1490   tempFileInfo.FolderPath = tempDir;
1491   tempFileInfo.FilePath = tempFilePath;
1492   tempFileInfo.NeedDelete = true;
1493 
1494   if (tryAsArchive)
1495   {
1496     CMyComPtr<IInArchiveGetStream> getStream;
1497     _folder.QueryInterface(IID_IInArchiveGetStream, &getStream);
1498     if (getStream)
1499     {
1500       CMyComPtr<ISequentialInStream> subSeqStream;
1501       getStream->GetStream(index, &subSeqStream);
1502       if (subSeqStream)
1503       {
1504         CMyComPtr<IInStream> subStream;
1505         subSeqStream.QueryInterface(IID_IInStream, &subStream);
1506         if (subStream)
1507         {
1508           HRESULT res = OpenAsArc_Msg(subStream, tempFileInfo, fullVirtPath, type ? type : L""
1509               // , true // showErrorMessage
1510               );
1511           if (res == S_OK)
1512           {
1513             tempDirectory.DisableDeleting();
1514             RefreshListCtrl();
1515             return;
1516           }
1517           if (res == E_ABORT || res != S_FALSE)
1518             return;
1519           if (!tryExternal)
1520             return;
1521           tryAsArchive = false;
1522         }
1523       }
1524     }
1525   }
1526 
1527 
1528   CRecordVector<UInt32> indices;
1529   indices.Add(index);
1530 
1531   UStringVector messages;
1532 
1533   bool usePassword = false;
1534   UString password;
1535   if (!_parentFolders.IsEmpty())
1536   {
1537     const CFolderLink &fl = _parentFolders.Back();
1538     usePassword = fl.UsePassword;
1539     password = fl.Password;
1540   }
1541 
1542   /*
1543   #if defined(_WIN32) && !defined(UNDER_CE)
1544   CByteBuffer zoneBuf;
1545   #ifndef _UNICODE
1546   if (g_IsNT)
1547   #endif
1548   if (!_parentFolders.IsEmpty())
1549   {
1550     const CFolderLink &fl = _parentFolders.Front();
1551     if (!fl.IsVirtual && !fl.FilePath.IsEmpty())
1552       ReadZoneFile(fl.FilePath + k_ZoneId_StreamName, zoneBuf);
1553   }
1554   #endif
1555   */
1556 
1557 
1558   CVirtFileSystem *virtFileSystemSpec = NULL;
1559   CMyComPtr<ISequentialOutStream> virtFileSystem;
1560 
1561   const bool isAltStream = IsItem_AltStream(index);
1562 
1563   CCopyToOptions options;
1564   options.includeAltStreams = true;
1565   options.replaceAltStreamChars = isAltStream;
1566   {
1567     // CContextMenuInfo ci;
1568     // ci.Load();
1569     // if (ci.WriteZone != (UInt32)(Int32)-1)
1570     // we use kAll when we unpack just one file.
1571     options.ZoneIdMode = NExtract::NZoneIdMode::kAll;
1572     options.NeedRegistryZone = false;
1573   }
1574 
1575   if (tryAsArchive)
1576   {
1577     // actually we want to get sum: size of main file plus sizes of altStreams.
1578     // but now there is no interface to get altStreams sizes.
1579     NCOM::CPropVariant prop;
1580     _folder->GetProperty(index, kpidSize, &prop);
1581     const size_t fileLimit = g_RAM_Size_Defined ?
1582         g_RAM_Size >> MyMax(_parentFolders.Size() + 1, 8u):
1583         1u << 22;
1584     UInt64 fileSize = 0;
1585     if (!ConvertPropVariantToUInt64(prop, fileSize))
1586       fileSize = fileLimit;
1587 #if 0  // 1 : for debug
1588     fileLimit = 1;
1589 #endif
1590 
1591     if (fileSize <= fileLimit)
1592     {
1593       options.streamMode = true;
1594       virtFileSystemSpec = new CVirtFileSystem;
1595       virtFileSystem = virtFileSystemSpec;
1596       virtFileSystemSpec->FileName = name;
1597       virtFileSystemSpec->IsAltStreamFile = isAltStream;
1598 
1599 #if defined(_WIN32) && !defined(UNDER_CE)
1600 #ifndef _UNICODE
1601       if (g_IsNT)
1602 #endif
1603       {
1604         Get_ZoneId_Stream_from_ParentFolders(virtFileSystemSpec->ZoneBuf);
1605         options.ZoneBuf = virtFileSystemSpec->ZoneBuf;
1606       }
1607 #endif
1608 
1609       virtFileSystemSpec->MaxTotalAllocSize = (size_t)fileSize
1610             + (1 << 16); // we allow additional total size for small alt streams.
1611       virtFileSystemSpec->DirPrefix = tempDirNorm;
1612       // options.VirtFileSystem = virtFileSystem;
1613       options.VirtFileSystemSpec = virtFileSystemSpec;
1614     }
1615   }
1616 
1617   options.folder = fs2us(tempDirNorm);
1618   options.showErrorMessages = true;
1619 
1620   const HRESULT result = CopyTo(options, indices, &messages, usePassword, password);
1621 
1622   if (!_parentFolders.IsEmpty())
1623   {
1624     CFolderLink &fl = _parentFolders.Back();
1625     fl.UsePassword = usePassword;
1626     fl.Password = password;
1627   }
1628 
1629   if (!messages.IsEmpty())
1630     return;
1631   if (result != S_OK)
1632   {
1633     if (result != E_ABORT)
1634       MessageBox_Error_HRESULT(result);
1635     return;
1636   }
1637 
1638   if (virtFileSystemSpec && !virtFileSystemSpec->WasStreamFlushedToFS())
1639   {
1640     int index_in_Files = virtFileSystemSpec->Index_of_MainExtractedFile_in_Files;
1641     if (index_in_Files < 0)
1642     {
1643       if (virtFileSystemSpec->Files.Size() != 1)
1644       {
1645         MessageBox_Error_HRESULT(E_FAIL);
1646         return;
1647       }
1648       // it's not expected case that index was not set, but we support that case
1649       index_in_Files = 0;
1650     }
1651     {
1652       const CVirtFile &file = virtFileSystemSpec->Files[index_in_Files];
1653       CMyComPtr2_Create<IInStream, CBufInStream> bufInStream;
1654       bufInStream->Init(file.Data, file.WrittenSize, virtFileSystem);
1655       const HRESULT res = OpenAsArc_Msg(bufInStream, tempFileInfo,
1656           fullVirtPath, type ? type : L""
1657           // , encrypted
1658           // , true // showErrorMessage
1659           );
1660       if (res == S_OK)
1661       {
1662         if (virtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files >= 0
1663             && !_parentFolders.IsEmpty())
1664         {
1665           const CVirtFile &fileZone = virtFileSystemSpec->Files[
1666               virtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files];
1667           _parentFolders.Back().ZoneBuf.CopyFrom(fileZone.Data, fileZone.WrittenSize);
1668         }
1669 
1670         tempDirectory.DisableDeleting();
1671         RefreshListCtrl();
1672         return;
1673       }
1674       if (res == E_ABORT || res != S_FALSE)
1675         return;
1676       if (!tryExternal)
1677         return;
1678       tryAsArchive = false;
1679       if (virtFileSystemSpec->FlushToDisk(true) != S_OK)
1680         return;
1681     }
1682   }
1683 
1684 
1685   /*
1686   #if defined(_WIN32) && !defined(UNDER_CE)
1687   if (zoneBuf.Size() != 0)
1688   {
1689     if (NFind::DoesFileExist_Raw(tempFilePath))
1690     {
1691       WriteZoneFile(tempFilePath + k_ZoneId_StreamName, zoneBuf);
1692     }
1693   }
1694   #endif
1695   */
1696 
1697 
1698   if (tryAsArchive)
1699   {
1700     const HRESULT res = OpenAsArc_Msg(NULL, tempFileInfo, fullVirtPath, type ? type : L""
1701         // , encrypted
1702         // , true // showErrorMessage
1703         );
1704     if (res == S_OK)
1705     {
1706       tempDirectory.DisableDeleting();
1707       RefreshListCtrl();
1708       return;
1709     }
1710     if (res == E_ABORT || res != S_FALSE)
1711       return;
1712   }
1713 
1714   if (!tryExternal)
1715     return;
1716 
1717   CMyUniquePtr<CTmpProcessInfo> tpi(new CTmpProcessInfo());
1718   tpi->FolderPath = tempDir;
1719   tpi->FilePath = tempFilePath;
1720   tpi->NeedDelete = true;
1721   tpi->UsePassword = usePassword;
1722   tpi->Password = password;
1723   tpi->ReadOnly = IsThereReadOnlyFolder();
1724   if (IsHashFolder())
1725     tpi->ReadOnly = true;
1726 
1727   if (!tpi->FileInfo.Find(tempFilePath))
1728     return;
1729 
1730   CTmpProcessInfoRelease tmpProcessInfoRelease(*tpi);
1731 
1732   CProcess process;
1733   HRESULT res;
1734   if (editMode)
1735     res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process);
1736   else
1737     res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process);
1738 
1739   if ((HANDLE)process == NULL)
1740   {
1741     // win7 / win10 work so for some extensions (pdf, html ..);
1742     DEBUG_PRINT("#### (HANDLE)process == 0");
1743     // return;
1744     if (res != S_OK)
1745       return;
1746   }
1747 
1748   tpi->Window = (HWND)*this;
1749   tpi->FullPathFolderPrefix = _currentFolderPrefix;
1750   tpi->FileIndex = index;
1751   tpi->RelPath = relPath;
1752 
1753   if ((HANDLE)process)
1754     tpi->Processes.SetMainProcess(process.Detach());
1755 
1756   ::CThread th;
1757   if (Thread_Create(&th, MyThreadFunction, tpi.get()) != 0)
1758     throw 271824;
1759   g_ExitEventLauncher._threads.Add(th);
1760   g_ExitEventLauncher._numActiveThreads++;
1761 
1762   tempDirectory.DisableDeleting();
1763   tpi.release();
1764   tmpProcessInfoRelease._needDelete = false;
1765 }
1766 
1767 
1768 /*
1769 static const UINT64 kTimeLimit = UINT64(10000000) * 3600 * 24;
1770 
1771 static bool CheckDeleteItem(UINT64 currentFileTime, UINT64 folderFileTime)
1772 {
1773   return (currentFileTime - folderFileTime > kTimeLimit &&
1774       folderFileTime - currentFileTime > kTimeLimit);
1775 }
1776 
1777 void DeleteOldTempFiles()
1778 {
1779   UString tempPath;
1780   if (!MyGetTempPath(tempPath))
1781     throw 1;
1782 
1783   UINT64 currentFileTime;
1784   NTime::GetCurUtcFileTime(currentFileTime);
1785   UString searchWildCard = tempPath + kTempDirPrefix + L"*.tmp";
1786   searchWildCard += WCHAR(NName::kAnyStringWildcard);
1787   NFind::CEnumeratorW enumerator(searchWildCard);
1788   NFind::CFileInfo fileInfo;
1789   while (enumerator.Next(fileInfo))
1790   {
1791     if (!fileInfo.IsDir())
1792       continue;
1793     const UINT64 &cTime = *(const UINT64 *)(&fileInfo.CTime);
1794     if (CheckDeleteItem(cTime, currentFileTime))
1795       RemoveDirectoryWithSubItems(tempPath + fileInfo.Name);
1796   }
1797 }
1798 */
1799