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 ¶ms);
SplitCmdLineSmart(const UString & cmd,UString & prg,UString & params)675 void SplitCmdLineSmart(const UString &cmd, UString &prg, UString ¶ms)
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 ¶mVector, 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