• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 UInt64 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(newFileInfo))
610     {
611       UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, folderLink.RelPath);
612       if (::MessageBoxW((HWND)*this, message, L"7-Zip", MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
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     DWORD wait = (hardExit ? 100 : INFINITE);
1087     if (Thread_WasCreated(&th))
1088     {
1089       DWORD waitResult = WaitForSingleObject(th, wait);
1090       // Thread_Wait(&th);
1091       if (waitResult == WAIT_TIMEOUT)
1092         wait = 1;
1093       if (!hardExit && waitResult != WAIT_OBJECT_0)
1094         continue;
1095       Thread_Close(&th);
1096       _numActiveThreads--;
1097     }
1098   }
1099 }
1100 
1101 
1102 
MyThreadFunction(void * param)1103 static THREAD_FUNC_DECL MyThreadFunction(void *param)
1104 {
1105   DEBUG_PRINT("==== MyThreadFunction ====");
1106 
1107   CMyUniquePtr<CTmpProcessInfo> tpi((CTmpProcessInfo *)param);
1108   CChildProcesses &processes = tpi->Processes;
1109 
1110   bool mainProcessWasSet = !processes.Handles.IsEmpty();
1111 
1112   bool isComplexMode = true;
1113 
1114   if (!processes.Handles.IsEmpty())
1115   {
1116 
1117   const DWORD startTime = GetTickCount();
1118 
1119   /*
1120   CPossibleProgs progs;
1121   {
1122     const UString &name = tpi->RelPath;
1123     int slashPos = name.ReverseFind_PathSepar();
1124     int dotPos = name.ReverseFind_Dot();
1125     if (dotPos > slashPos)
1126     {
1127       const UString ext = name.Ptr(dotPos + 1);
1128       AString extA = UnicodeStringToMultiByte(ext);
1129       extA.MakeLower_Ascii();
1130       progs.SetFromExtension(extA);
1131     }
1132   }
1133   */
1134 
1135   bool firstPass = true;
1136 
1137   for (;;)
1138   {
1139     CRecordVector<HANDLE> handles;
1140     CUIntVector indices;
1141 
1142     FOR_VECTOR (i, processes.Handles)
1143     {
1144       if (handles.Size() > 60)
1145         break;
1146       if (processes.NeedWait[i])
1147       {
1148         handles.Add(processes.Handles[i]);
1149         indices.Add(i);
1150       }
1151     }
1152 
1153     bool needFindProcessByPath = false;
1154 
1155     if (handles.IsEmpty())
1156     {
1157       if (!firstPass)
1158         break;
1159     }
1160     else
1161     {
1162       handles.Add(g_ExitEventLauncher._exitEvent);
1163 
1164       DWORD waitResult = WaitForMultiObj_Any_Infinite(handles.Size(), handles.ConstData());
1165 
1166       waitResult -= WAIT_OBJECT_0;
1167 
1168       if (waitResult >= handles.Size() - 1)
1169       {
1170         processes.CloseAll();
1171         /*
1172         if (waitResult == handles.Size() - 1)
1173         {
1174           // exit event
1175           // we want to delete temp files, if progs were used
1176           if (processes.ProgsWereUsed)
1177             break;
1178         }
1179         */
1180         return waitResult >= (DWORD)handles.Size() ? 1 : 0;
1181       }
1182 
1183       if (firstPass && indices.Size() == 1)
1184       {
1185         const DWORD curTime = GetTickCount() - startTime;
1186 
1187         /*
1188         if (curTime > 5 * 1000)
1189           progs.ProgNames.Clear();
1190         */
1191 
1192         needFindProcessByPath = (curTime < 2 * 1000);
1193 
1194         if (needFindProcessByPath)
1195         {
1196           NFind::CFileInfo newFileInfo;
1197           if (newFileInfo.Find(tpi->FilePath))
1198             if (tpi->WasChanged(newFileInfo))
1199               needFindProcessByPath = false;
1200         }
1201 
1202         DEBUG_PRINT_NUM(" -- firstPass -- time = ", curTime)
1203       }
1204 
1205       processes.DisableWait(indices[(unsigned)waitResult]);
1206     }
1207 
1208     firstPass = false;
1209 
1210     // Sleep(300);
1211     #ifndef UNDER_CE
1212     processes.Update(needFindProcessByPath /* , progs */);
1213     #endif
1214   }
1215 
1216 
1217   const DWORD curTime = GetTickCount() - startTime;
1218 
1219   DEBUG_PRINT_NUM("after time = ", curTime)
1220 
1221   processes.CloseAll();
1222 
1223   isComplexMode = (curTime < 2 * 1000);
1224 
1225   }
1226 
1227   bool needCheckTimestamp = true;
1228 
1229   for (;;)
1230   {
1231     NFind::CFileInfo newFileInfo;
1232 
1233     if (!newFileInfo.Find(tpi->FilePath))
1234       break;
1235 
1236     if (mainProcessWasSet)
1237     {
1238       if (tpi->WasChanged(newFileInfo))
1239       {
1240         UString m = MyFormatNew(IDS_CANNOT_UPDATE_FILE, fs2us(tpi->FilePath));
1241         if (tpi->ReadOnly)
1242         {
1243           m.Add_LF();
1244           AddLangString(m, IDS_PROP_READ_ONLY);
1245           m.Add_LF();
1246           m += tpi->FullPathFolderPrefix;
1247           ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP);
1248           return 0;
1249         }
1250         {
1251           const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, tpi->RelPath);
1252           if (::MessageBoxW(g_HWND, message, L"7-Zip", MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
1253           {
1254             // DEBUG_PRINT_NUM("SendMessage", GetCurrentThreadId());
1255             if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi.get()) != 1)
1256             {
1257               ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP);
1258               return 0;
1259             }
1260           }
1261           needCheckTimestamp = false;
1262           break;
1263         }
1264       }
1265 
1266       if (!isComplexMode)
1267         break;
1268     }
1269 
1270     // DEBUG_PRINT("WaitForSingleObject");
1271     DWORD waitResult = ::WaitForSingleObject(g_ExitEventLauncher._exitEvent, INFINITE);
1272     // DEBUG_PRINT("---");
1273 
1274     if (waitResult == WAIT_OBJECT_0)
1275       break;
1276 
1277     return 1;
1278   }
1279 
1280   {
1281     NFind::CFileInfo newFileInfo;
1282 
1283     bool finded = newFileInfo.Find(tpi->FilePath);
1284 
1285     if (!needCheckTimestamp || !finded || !tpi->WasChanged(newFileInfo))
1286     {
1287       DEBUG_PRINT("Delete Temp file");
1288       tpi->DeleteDirAndFile();
1289     }
1290   }
1291 
1292   return 0;
1293 }
1294 
1295 
1296 /*
1297 #if defined(_WIN32) && !defined(UNDER_CE)
1298 static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier");
1299 #endif
1300 
1301 
1302 #ifndef UNDER_CE
1303 
1304 static void ReadZoneFile(CFSTR fileName, CByteBuffer &buf)
1305 {
1306   buf.Free();
1307   NIO::CInFile file;
1308   if (!file.Open(fileName))
1309     return;
1310   UInt64 fileSize;
1311   if (!file.GetLength(fileSize))
1312     return;
1313   if (fileSize == 0 || fileSize >= ((UInt32)1 << 20))
1314     return;
1315   buf.Alloc((size_t)fileSize);
1316   size_t processed;
1317   if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize)
1318     return;
1319   buf.Free();
1320 }
1321 
1322 static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf)
1323 {
1324   NIO::COutFile file;
1325   if (!file.Create(fileName, true))
1326     return false;
1327   UInt32 processed;
1328   if (!file.Write(buf, (UInt32)buf.Size(), processed))
1329     return false;
1330   return processed == buf.Size();
1331 }
1332 
1333 #endif
1334 */
1335 
1336 /*
1337 Z7_CLASS_IMP_COM_1(
1338   CBufSeqOutStream_WithFile
1339   , ISequentialOutStream
1340 )
1341   Byte *_buffer;
1342   size_t _size;
1343   size_t _pos;
1344 
1345   size_t _fileWritePos;
1346   bool fileMode;
1347 public:
1348 
1349   bool IsStreamInMem() const { return !fileMode; }
1350   size_t GetMemStreamWrittenSize() const { return _pos; }
1351 
1352   // ISequentialOutStream *FileStream;
1353   FString FilePath;
1354   COutFileStream *outFileStreamSpec;
1355   CMyComPtr<ISequentialOutStream> outFileStream;
1356 
1357   CBufSeqOutStream_WithFile(): outFileStreamSpec(NULL) {}
1358 
1359   void Init(Byte *buffer, size_t size)
1360   {
1361     fileMode = false;
1362     _buffer = buffer;
1363     _pos = 0;
1364     _size = size;
1365     _fileWritePos = 0;
1366   }
1367 
1368   HRESULT FlushToFile();
1369   size_t GetPos() const { return _pos; }
1370 };
1371 
1372 static const UInt32 kBlockSize = ((UInt32)1 << 31);
1373 
1374 STDMETHODIMP CBufSeqOutStream_WithFile::Write(const void *data, UInt32 size, UInt32 *processedSize)
1375 {
1376   if (processedSize)
1377     *processedSize = 0;
1378   if (!fileMode)
1379   {
1380     if (_size - _pos >= size)
1381     {
1382       if (size != 0)
1383       {
1384         memcpy(_buffer + _pos, data, size);
1385         _pos += size;
1386       }
1387       if (processedSize)
1388         *processedSize = (UInt32)size;
1389       return S_OK;
1390     }
1391 
1392     fileMode = true;
1393   }
1394   RINOK(FlushToFile());
1395   return outFileStream->Write(data, size, processedSize);
1396 }
1397 
1398 HRESULT CBufSeqOutStream_WithFile::FlushToFile()
1399 {
1400   if (!outFileStream)
1401   {
1402     outFileStreamSpec = new COutFileStream;
1403     outFileStream = outFileStreamSpec;
1404     if (!outFileStreamSpec->Create(FilePath, false))
1405     {
1406       outFileStream.Release();
1407       return E_FAIL;
1408       // MessageBoxMyError(UString("Can't create file ") + fs2us(tempFilePath));
1409     }
1410   }
1411   while (_fileWritePos != _pos)
1412   {
1413     size_t cur = _pos - _fileWritePos;
1414     UInt32 curSize = (cur < kBlockSize) ? (UInt32)cur : kBlockSize;
1415     UInt32 processedSizeLoc = 0;
1416     HRESULT res = outFileStream->Write(_buffer + _fileWritePos, curSize, &processedSizeLoc);
1417     _fileWritePos += processedSizeLoc;
1418     RINOK(res);
1419     if (processedSizeLoc == 0)
1420       return E_FAIL;
1421   }
1422   return S_OK;
1423 }
1424 */
1425 
1426 /*
1427 static HRESULT GetTime(IFolderFolder *folder, UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
1428 {
1429   filetimeIsDefined = false;
1430   NCOM::CPropVariant prop;
1431   RINOK(folder->GetProperty(index, propID, &prop));
1432   if (prop.vt == VT_FILETIME)
1433   {
1434     filetime = prop.filetime;
1435     filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
1436   }
1437   else if (prop.vt != VT_EMPTY)
1438     return E_FAIL;
1439   return S_OK;
1440 }
1441 */
1442 
1443 
1444 /*
1445 tryInternal tryExternal
1446   false       false      : unused
1447   false       true       : external
1448   true        false      : internal
1449   true        true       : smart based on file extension:
1450                       !alwaysStart(name) : both
1451                       alwaysStart(name)  : external
1452 */
1453 
OpenItemInArchive(unsigned index,bool tryInternal,bool tryExternal,bool editMode,bool useEditor,const wchar_t * type)1454 void CPanel::OpenItemInArchive(unsigned index, bool tryInternal, bool tryExternal, bool editMode, bool useEditor, const wchar_t *type)
1455 {
1456   // we don't want to change hash data here
1457   if (IsHashFolder())
1458     return;
1459 
1460   const UString name = GetItemName(index);
1461   const UString relPath = GetItemRelPath(index);
1462 
1463   if (tryExternal)
1464     if (IsVirus_Message(name))
1465       return;
1466 
1467   if (!_folderOperations)
1468   {
1469     MessageBox_Error_UnsupportOperation();
1470     return;
1471   }
1472 
1473   bool tryAsArchive = tryInternal && (!tryExternal || !DoItemAlwaysStart(name));
1474 
1475   const UString fullVirtPath = _currentFolderPrefix + relPath;
1476 
1477   CTempDir tempDirectory;
1478   if (!tempDirectory.Create(kTempDirPrefix))
1479   {
1480     MessageBox_LastError();
1481     return;
1482   }
1483 
1484   FString tempDir = tempDirectory.GetPath();
1485   FString tempDirNorm = tempDir;
1486   NName::NormalizeDirPathPrefix(tempDirNorm);
1487   const FString tempFilePath = tempDirNorm + us2fs(Get_Correct_FsFile_Name(name));
1488 
1489   CTempFileInfo tempFileInfo;
1490   tempFileInfo.FileIndex = index;
1491   tempFileInfo.RelPath = relPath;
1492   tempFileInfo.FolderPath = tempDir;
1493   tempFileInfo.FilePath = tempFilePath;
1494   tempFileInfo.NeedDelete = true;
1495 
1496   if (tryAsArchive)
1497   {
1498     CMyComPtr<IInArchiveGetStream> getStream;
1499     _folder.QueryInterface(IID_IInArchiveGetStream, &getStream);
1500     if (getStream)
1501     {
1502       CMyComPtr<ISequentialInStream> subSeqStream;
1503       getStream->GetStream(index, &subSeqStream);
1504       if (subSeqStream)
1505       {
1506         CMyComPtr<IInStream> subStream;
1507         subSeqStream.QueryInterface(IID_IInStream, &subStream);
1508         if (subStream)
1509         {
1510           HRESULT res = OpenAsArc_Msg(subStream, tempFileInfo, fullVirtPath, type ? type : L""
1511               // , true // showErrorMessage
1512               );
1513           if (res == S_OK)
1514           {
1515             tempDirectory.DisableDeleting();
1516             RefreshListCtrl();
1517             return;
1518           }
1519           if (res == E_ABORT || res != S_FALSE)
1520             return;
1521           if (!tryExternal)
1522             return;
1523           tryAsArchive = false;
1524         }
1525       }
1526     }
1527   }
1528 
1529 
1530   CRecordVector<UInt32> indices;
1531   indices.Add(index);
1532 
1533   UStringVector messages;
1534 
1535   bool usePassword = false;
1536   UString password;
1537   if (_parentFolders.Size() > 0)
1538   {
1539     const CFolderLink &fl = _parentFolders.Back();
1540     usePassword = fl.UsePassword;
1541     password = fl.Password;
1542   }
1543 
1544   /*
1545   #if defined(_WIN32) && !defined(UNDER_CE)
1546   CByteBuffer zoneBuf;
1547   #ifndef _UNICODE
1548   if (g_IsNT)
1549   #endif
1550   if (_parentFolders.Size() > 0)
1551   {
1552     const CFolderLink &fl = _parentFolders.Front();
1553     if (!fl.IsVirtual && !fl.FilePath.IsEmpty())
1554       ReadZoneFile(fl.FilePath + k_ZoneId_StreamName, zoneBuf);
1555   }
1556   #endif
1557   */
1558 
1559 
1560   CVirtFileSystem *virtFileSystemSpec = NULL;
1561   CMyComPtr<ISequentialOutStream> virtFileSystem;
1562 
1563   const bool isAltStream = IsItem_AltStream(index);
1564 
1565   CCopyToOptions options;
1566   options.includeAltStreams = true;
1567   options.replaceAltStreamChars = isAltStream;
1568   {
1569     // CContextMenuInfo ci;
1570     // ci.Load();
1571     // if (ci.WriteZone != (UInt32)(Int32)-1)
1572     // we use kAll when we unpack just one file.
1573     options.ZoneIdMode = NExtract::NZoneIdMode::kAll;
1574     options.NeedRegistryZone = false;
1575   }
1576 
1577   if (tryAsArchive)
1578   {
1579     NCOM::CPropVariant prop;
1580     _folder->GetProperty(index, kpidSize, &prop);
1581     UInt64 fileLimit = 1 << 22;
1582     if (g_RAM_Size_Defined)
1583       fileLimit = g_RAM_Size / 4;
1584 
1585     UInt64 fileSize = 0;
1586     if (!ConvertPropVariantToUInt64(prop, fileSize))
1587       fileSize = fileLimit;
1588     if (fileSize <= fileLimit && fileSize > 0)
1589     {
1590       options.streamMode = true;
1591       virtFileSystemSpec = new CVirtFileSystem;
1592       virtFileSystem = virtFileSystemSpec;
1593 
1594 #if defined(_WIN32) && !defined(UNDER_CE)
1595 #ifndef _UNICODE
1596       if (g_IsNT)
1597 #endif
1598       if (_parentFolders.Size() > 0)
1599       {
1600         const CFolderLink &fl = _parentFolders.Front();
1601         if (!fl.IsVirtual && !fl.FilePath.IsEmpty())
1602           ReadZoneFile_Of_BaseFile(fl.FilePath, virtFileSystemSpec->ZoneBuf);
1603       }
1604 #endif
1605 
1606       // we allow additional total size for small alt streams;
1607       virtFileSystemSpec->MaxTotalAllocSize = fileSize + (1 << 10);
1608 
1609       virtFileSystemSpec->DirPrefix = tempDirNorm;
1610       virtFileSystemSpec->Init();
1611       options.VirtFileSystem = virtFileSystem;
1612       options.VirtFileSystemSpec = virtFileSystemSpec;
1613     }
1614   }
1615 
1616   options.folder = fs2us(tempDirNorm);
1617   options.showErrorMessages = true;
1618 
1619   const HRESULT result = CopyTo(options, indices, &messages, usePassword, password);
1620 
1621   if (_parentFolders.Size() > 0)
1622   {
1623     CFolderLink &fl = _parentFolders.Back();
1624     fl.UsePassword = usePassword;
1625     fl.Password = password;
1626   }
1627 
1628   if (!messages.IsEmpty())
1629     return;
1630   if (result != S_OK)
1631   {
1632     if (result != E_ABORT)
1633       MessageBox_Error_HRESULT(result);
1634     return;
1635   }
1636 
1637   if (options.VirtFileSystem)
1638   {
1639     if (virtFileSystemSpec->IsStreamInMem())
1640     {
1641       const CVirtFile &file = virtFileSystemSpec->Files[0];
1642 
1643       size_t streamSize = (size_t)file.Size;
1644       CBufInStream *bufInStreamSpec = new CBufInStream;
1645       CMyComPtr<IInStream> bufInStream = bufInStreamSpec;
1646       bufInStreamSpec->Init(file.Data, streamSize, virtFileSystem);
1647 
1648       HRESULT res = OpenAsArc_Msg(bufInStream, tempFileInfo, fullVirtPath, type ? type : L""
1649           // , encrypted
1650           // , true // showErrorMessage
1651           );
1652 
1653       if (res == S_OK)
1654       {
1655         tempDirectory.DisableDeleting();
1656         RefreshListCtrl();
1657         return;
1658       }
1659 
1660       if (res == E_ABORT || res != S_FALSE)
1661         return;
1662       if (!tryExternal)
1663         return;
1664 
1665       tryAsArchive = false;
1666       if (virtFileSystemSpec->FlushToDisk(true) != S_OK)
1667         return;
1668     }
1669   }
1670 
1671 
1672   /*
1673   #if defined(_WIN32) && !defined(UNDER_CE)
1674   if (zoneBuf.Size() != 0)
1675   {
1676     if (NFind::DoesFileExist_Raw(tempFilePath))
1677     {
1678       WriteZoneFile(tempFilePath + k_ZoneId_StreamName, zoneBuf);
1679     }
1680   }
1681   #endif
1682   */
1683 
1684 
1685   if (tryAsArchive)
1686   {
1687     HRESULT res = OpenAsArc_Msg(NULL, tempFileInfo, fullVirtPath, type ? type : L""
1688         // , encrypted
1689         // , true // showErrorMessage
1690         );
1691     if (res == S_OK)
1692     {
1693       tempDirectory.DisableDeleting();
1694       RefreshListCtrl();
1695       return;
1696     }
1697     if (res == E_ABORT || res != S_FALSE)
1698       return;
1699   }
1700 
1701   if (!tryExternal)
1702     return;
1703 
1704   CMyUniquePtr<CTmpProcessInfo> tpi(new CTmpProcessInfo());
1705   tpi->FolderPath = tempDir;
1706   tpi->FilePath = tempFilePath;
1707   tpi->NeedDelete = true;
1708   tpi->UsePassword = usePassword;
1709   tpi->Password = password;
1710   tpi->ReadOnly = IsThereReadOnlyFolder();
1711   if (IsHashFolder())
1712     tpi->ReadOnly = true;
1713 
1714   if (!tpi->FileInfo.Find(tempFilePath))
1715     return;
1716 
1717   CTmpProcessInfoRelease tmpProcessInfoRelease(*tpi);
1718 
1719   CProcess process;
1720   HRESULT res;
1721   if (editMode)
1722     res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process);
1723   else
1724     res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process);
1725 
1726   if ((HANDLE)process == NULL)
1727   {
1728     // win7 / win10 work so for some extensions (pdf, html ..);
1729     DEBUG_PRINT("#### (HANDLE)process == 0");
1730     // return;
1731     if (res != S_OK)
1732       return;
1733   }
1734 
1735   tpi->Window = (HWND)(*this);
1736   tpi->FullPathFolderPrefix = _currentFolderPrefix;
1737   tpi->FileIndex = index;
1738   tpi->RelPath = relPath;
1739 
1740   if ((HANDLE)process)
1741     tpi->Processes.SetMainProcess(process.Detach());
1742 
1743   ::CThread th;
1744   if (Thread_Create(&th, MyThreadFunction, tpi.get()) != 0)
1745     throw 271824;
1746   g_ExitEventLauncher._threads.Add(th);
1747   g_ExitEventLauncher._numActiveThreads++;
1748 
1749   tempDirectory.DisableDeleting();
1750   tpi.release();
1751   tmpProcessInfoRelease._needDelete = false;
1752 }
1753 
1754 
1755 /*
1756 static const UINT64 kTimeLimit = UINT64(10000000) * 3600 * 24;
1757 
1758 static bool CheckDeleteItem(UINT64 currentFileTime, UINT64 folderFileTime)
1759 {
1760   return (currentFileTime - folderFileTime > kTimeLimit &&
1761       folderFileTime - currentFileTime > kTimeLimit);
1762 }
1763 
1764 void DeleteOldTempFiles()
1765 {
1766   UString tempPath;
1767   if (!MyGetTempPath(tempPath))
1768     throw 1;
1769 
1770   UINT64 currentFileTime;
1771   NTime::GetCurUtcFileTime(currentFileTime);
1772   UString searchWildCard = tempPath + kTempDirPrefix + L"*.tmp";
1773   searchWildCard += WCHAR(NName::kAnyStringWildcard);
1774   NFind::CEnumeratorW enumerator(searchWildCard);
1775   NFind::CFileInfo fileInfo;
1776   while (enumerator.Next(fileInfo))
1777   {
1778     if (!fileInfo.IsDir())
1779       continue;
1780     const UINT64 &cTime = *(const UINT64 *)(&fileInfo.CTime);
1781     if (CheckDeleteItem(cTime, currentFileTime))
1782       RemoveDirectoryWithSubItems(tempPath + fileInfo.Name);
1783   }
1784 }
1785 */
1786