// PanelItemOpen.cpp #include "StdAfx.h" #include "../../../Common/MyWindows.h" #include #include "../../../Common/IntToString.h" #include "../../../Common/AutoPtr.h" #include "../../../Windows/ProcessUtils.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #include "../../Common/FileStreams.h" #include "../../Common/StreamObjects.h" #include "../Common/ExtractingFilePath.h" #include "App.h" #include "FileFolderPluginOpen.h" #include "FormatUtils.h" #include "LangUtils.h" #include "PropertyNameRes.h" #include "RegistryUtils.h" #include "UpdateCallback100.h" #include "../GUI/ExtractRes.h" #include "resource.h" using namespace NWindows; using namespace NSynchronization; using namespace NFile; using namespace NDir; extern bool g_RAM_Size_Defined; extern size_t g_RAM_Size; #ifndef _UNICODE extern bool g_IsNT; #endif #define kTempDirPrefix FTEXT("7zO") // #define SHOW_DEBUG_INFO #ifdef SHOW_DEBUG_INFO #define DEBUG_PRINT(s) OutputDebugStringA(s); #define DEBUG_PRINT_W(s) OutputDebugStringW(s); #define DEBUG_PRINT_NUM(s, num) { char ttt[32]; ConvertUInt32ToString(num, ttt); OutputDebugStringA(s); OutputDebugStringA(ttt); } #else #define DEBUG_PRINT(s) #define DEBUG_PRINT_W(s) #define DEBUG_PRINT_NUM(s, num) #endif #ifndef UNDER_CE class CProcessSnapshot { HANDLE _handle; public: CProcessSnapshot(): _handle(INVALID_HANDLE_VALUE) {} ~CProcessSnapshot() { Close(); } bool Close() { if (_handle == INVALID_HANDLE_VALUE) return true; if (!::CloseHandle(_handle)) return false; _handle = INVALID_HANDLE_VALUE; return true; } bool Create() { _handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); return (_handle != INVALID_HANDLE_VALUE); } bool GetFirstProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32First(_handle, pe)); } bool GetNextProcess(PROCESSENTRY32 *pe) { return BOOLToBool(Process32Next(_handle, pe)); } }; #endif /* struct COpenExtProg { const char *Ext; const char *Prog; }; static const COpenExtProg g_Progs[] = { { "jpeg jpg png bmp gif", "Microsoft.Photos.exe" }, { "html htm pdf", "MicrosoftEdge.exe" }, // , { "rrr", "notepad.exe" } }; static bool FindExtProg(const char *exts, const char *ext) { unsigned len = (unsigned)strlen(ext); for (;;) { const char *p = exts; for (;; p++) { const char c = *p; if (c == 0 || c == ' ') break; } if (len == (unsigned)(p - exts) && IsString1PrefixedByString2(exts, ext)) return true; if (*p == 0) return false; exts = p + 1; } } class CPossibleProgs { public: AStringVector ProgNames; void SetFromExtension(const char *ext) // ext must be low case { ProgNames.Clear(); for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Progs); i++) if (FindExtProg(g_Progs[i].Ext, ext)) { ProgNames.Add(g_Progs[i].Prog); } } bool IsFromList(const UString &progName) const { FOR_VECTOR (i, ProgNames) if (progName.IsEqualTo_Ascii_NoCase(ProgNames[i])) return true; return false; } }; */ #ifndef UNDER_CE EXTERN_C_BEGIN /* GetProcessImageFileName returns the path in device form, rather than drive letters: \Device\HarddiskVolume1\WINDOWS\SysWOW64\notepad.exe GetModuleFileNameEx works only after Sleep(something). Why? returns the path C:\WINDOWS\system32\NOTEPAD.EXE */ /* Kernel32.dll: Win7, Win2008R2; Psapi.dll: (if PSAPI_VERSION=1) on Win7 and Win2008R2; Psapi.dll: XP, Win2003, Vista, 2008; */ typedef DWORD (WINAPI *Func_GetProcessImageFileNameW)( HANDLE hProcess, LPWSTR lpFilename, DWORD nSize); typedef DWORD (WINAPI *Func_GetModuleFileNameExW)( HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize); typedef DWORD (WINAPI *Func_GetProcessId)(HANDLE process); EXTERN_C_END static HMODULE g_Psapi_dll_module; /* static void My_GetProcessFileName_2(HANDLE hProcess, UString &path) { path.Empty(); const unsigned maxPath = 1024; WCHAR temp[maxPath + 1]; const char *func_name = "GetModuleFileNameExW"; Func_GetModuleFileNameExW my_func = (Func_GetModuleFileNameExW) ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), func_name); if (!my_func) { if (!g_Psapi_dll_module) g_Psapi_dll_module = LoadLibraryW(L"Psapi.dll"); if (g_Psapi_dll_module) my_func = (Func_GetModuleFileNameExW)::GetProcAddress(g_Psapi_dll_module, func_name); } if (my_func) { // DWORD num = GetModuleFileNameEx(hProcess, NULL, temp, maxPath); DWORD num = my_func(hProcess, NULL, temp, maxPath); if (num != 0) path = temp; } // FreeLibrary(lib); } */ Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION static void My_GetProcessFileName(HANDLE hProcess, UString &path) { path.Empty(); const unsigned maxPath = 1024; WCHAR temp[maxPath + 1]; const char *func_name = "GetProcessImageFileNameW"; Func_GetProcessImageFileNameW my_func = Z7_GET_PROC_ADDRESS( Func_GetProcessImageFileNameW, ::GetModuleHandleA("kernel32.dll"), func_name); if (!my_func) { if (!g_Psapi_dll_module) g_Psapi_dll_module = LoadLibraryW(L"Psapi.dll"); if (g_Psapi_dll_module) my_func = Z7_GET_PROC_ADDRESS( Func_GetProcessImageFileNameW, g_Psapi_dll_module, func_name); } if (my_func) { const DWORD num = // GetProcessImageFileNameW(hProcess, temp, maxPath); my_func(hProcess, temp, maxPath); if (num != 0) path = temp; } // FreeLibrary(lib); } struct CSnapshotProcess { DWORD Id; DWORD ParentId; UString Name; }; static void GetSnapshot(CObjectVector &items) { items.Clear(); CProcessSnapshot snapshot; if (!snapshot.Create()) return; DEBUG_PRINT("snapshot.Create() OK"); PROCESSENTRY32 pe; CSnapshotProcess item; memset(&pe, 0, sizeof(pe)); pe.dwSize = sizeof(pe); BOOL res = snapshot.GetFirstProcess(&pe); while (res) { item.Id = pe.th32ProcessID; item.ParentId = pe.th32ParentProcessID; item.Name = GetUnicodeString(pe.szExeFile); items.Add(item); res = snapshot.GetNextProcess(&pe); } } #endif class CChildProcesses { #ifndef UNDER_CE CRecordVector _ids; #endif public: // bool ProgsWereUsed; CRecordVector Handles; CRecordVector NeedWait; // UStringVector Names; #ifndef UNDER_CE UString Path; #endif // CChildProcesses(): ProgsWereUsed(false) {} ~CChildProcesses() { CloseAll(); } void DisableWait(unsigned index) { NeedWait[index] = false; } void CloseAll() { FOR_VECTOR (i, Handles) { HANDLE h = Handles[i]; if (h != NULL) CloseHandle(h); } Handles.Clear(); NeedWait.Clear(); // Names.Clear(); #ifndef UNDER_CE // Path.Empty(); _ids.Clear(); #endif } void SetMainProcess(HANDLE h) { #ifndef UNDER_CE const Func_GetProcessId func = Z7_GET_PROC_ADDRESS( Func_GetProcessId, ::GetModuleHandleA("kernel32.dll"), "GetProcessId"); if (func) { const DWORD id = func(h); if (id != 0) _ids.AddToUniqueSorted(id); } My_GetProcessFileName(h, Path); DEBUG_PRINT_W(Path); #endif Handles.Add(h); NeedWait.Add(true); } #ifndef UNDER_CE void Update(bool needFindProcessByPath /* , const CPossibleProgs &progs */) { /* if (_ids.IsEmpty()) return; */ CObjectVector sps; GetSnapshot(sps); const int separ = Path.ReverseFind_PathSepar(); const UString mainName = Path.Ptr((unsigned)(separ + 1)); if (mainName.IsEmpty()) needFindProcessByPath = false; const DWORD currentProcessId = GetCurrentProcessId(); for (;;) { bool wasAdded = false; FOR_VECTOR (i, sps) { const CSnapshotProcess &sp = sps[i]; const DWORD id = sp.Id; if (id == currentProcessId) continue; if (_ids.FindInSorted(id) >= 0) continue; bool isSameName = false; const UString &name = sp.Name; if (needFindProcessByPath) isSameName = mainName.IsEqualTo_NoCase(name); bool needAdd = false; // bool isFromProgs = false; if (isSameName || _ids.FindInSorted(sp.ParentId) >= 0) needAdd = true; /* else if (progs.IsFromList(name)) { needAdd = true; isFromProgs = true; } */ if (needAdd) { DEBUG_PRINT("----- OpenProcess -----"); DEBUG_PRINT_W(name); HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, id); if (hProcess) { DEBUG_PRINT("----- OpenProcess OK -----"); // if (!isFromProgs) _ids.AddToUniqueSorted(id); Handles.Add(hProcess); NeedWait.Add(true); // Names.Add(name); wasAdded = true; // ProgsWereUsed = isFromProgs; } } } if (!wasAdded) break; } } #endif }; struct CTmpProcessInfo: public CTempFileInfo { CChildProcesses Processes; HWND Window; UString FullPathFolderPrefix; bool UsePassword; UString Password; bool ReadOnly; CTmpProcessInfo(): UsePassword(false), ReadOnly(false) {} }; class CTmpProcessInfoRelease { CTmpProcessInfo *_tmpProcessInfo; public: bool _needDelete; CTmpProcessInfoRelease(CTmpProcessInfo &tpi): _tmpProcessInfo(&tpi), _needDelete(true) {} ~CTmpProcessInfoRelease() { if (_needDelete) _tmpProcessInfo->DeleteDirAndFile(); } }; void GetFolderError(CMyComPtr &folder, UString &s); HRESULT CPanel::OpenAsArc(IInStream *inStream, const CTempFileInfo &tempFileInfo, const UString &virtualFilePath, const UString &arcFormat, COpenResult &openRes) { openRes.Encrypted = false; CFolderLink folderLink; (CTempFileInfo &)folderLink = tempFileInfo; if (inStream) folderLink.IsVirtual = true; else { if (!folderLink.FileInfo.Find(folderLink.FilePath)) return GetLastError_noZero_HRESULT(); if (folderLink.FileInfo.IsDir()) return S_FALSE; folderLink.IsVirtual = false; } folderLink.VirtualPath = virtualFilePath; CFfpOpen ffp; const HRESULT res = ffp.OpenFileFolderPlugin(inStream, folderLink.FilePath.IsEmpty() ? us2fs(virtualFilePath) : folderLink.FilePath, arcFormat, GetParent()); openRes.Encrypted = ffp.Encrypted; openRes.ErrorMessage = ffp.ErrorMessage; RINOK(res) folderLink.Password = ffp.Password; folderLink.UsePassword = ffp.Encrypted; if (_folder) folderLink.ParentFolderPath = GetFolderPath(_folder); else folderLink.ParentFolderPath = _currentFolderPrefix; if (!_parentFolders.IsEmpty()) folderLink.ParentFolder = _folder; _parentFolders.Add(folderLink); _parentFolders.Back().Library.Attach(_library.Detach()); ReleaseFolder(); _library.Free(); SetNewFolder(ffp.Folder); _library.Attach(ffp.Library.Detach()); _flatMode = _flatModeForArc; _thereAreDeletedItems = false; if (!openRes.ErrorMessage.IsEmpty()) MessageBox_Error(openRes.ErrorMessage); /* UString s; GetFolderError(_folder, s); if (!s.IsEmpty()) MessageBox_Error(s); */ // we don't show error here by some reasons: // after MessageBox_Warning it throws exception in nested archives in Debug Mode. why ?. // MessageBox_Warning(L"test error"); return S_OK; } HRESULT CPanel::OpenAsArc_Msg(IInStream *inStream, const CTempFileInfo &tempFileInfo, const UString &virtualFilePath, const UString &arcFormat // , bool &encrypted // , bool showErrorMessage ) { COpenResult opRes; HRESULT res = OpenAsArc(inStream, tempFileInfo, virtualFilePath, arcFormat, opRes); if (res == S_OK) return res; if (res == E_ABORT) return res; // if (showErrorMessage) if (opRes.Encrypted || res != S_FALSE) // 17.01 : we show message also for (res != S_FALSE) { UString message; if (res == S_FALSE) { message = MyFormatNew( opRes.Encrypted ? IDS_CANT_OPEN_ENCRYPTED_ARCHIVE : IDS_CANT_OPEN_ARCHIVE, virtualFilePath); } else message = HResultToMessage(res); MessageBox_Error(message); } return res; } HRESULT CPanel::OpenAsArc_Name(const UString &relPath, const UString &arcFormat // , bool &encrypted, // , bool showErrorMessage ) { CTempFileInfo tfi; tfi.RelPath = relPath; tfi.FolderPath = us2fs(GetFsPath()); const UString fullPath = GetFsPath() + relPath; tfi.FilePath = us2fs(fullPath); return OpenAsArc_Msg(NULL, tfi, fullPath, arcFormat /* , encrypted, showErrorMessage */); } HRESULT CPanel::OpenAsArc_Index(unsigned index, const wchar_t *type // , bool showErrorMessage ) { CDisableTimerProcessing disableTimerProcessing1(*this); CDisableNotify disableNotify(*this); HRESULT res = OpenAsArc_Name(GetItemRelPath2(index), type ? type : L"" /* , encrypted, showErrorMessage */); if (res != S_OK) { RefreshTitle(true); // in case of error we must refresh changed title of 7zFM return res; } RefreshListCtrl(); return S_OK; } HRESULT CPanel::OpenParentArchiveFolder() { CDisableTimerProcessing disableTimerProcessing(*this); CDisableNotify disableNotify(*this); if (_parentFolders.Size() < 2) return S_OK; const CFolderLink &folderLinkPrev = _parentFolders[_parentFolders.Size() - 2]; const CFolderLink &folderLink = _parentFolders.Back(); NFind::CFileInfo newFileInfo; if (newFileInfo.Find(folderLink.FilePath)) { if (folderLink.WasChanged_from_FolderLink(newFileInfo)) { const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, folderLink.RelPath); if (::MessageBoxW((HWND)*this, message, L"7-Zip", MB_YESNOCANCEL | MB_ICONQUESTION) == IDYES) { if (OnOpenItemChanged(folderLink.FileIndex, fs2us(folderLink.FilePath), folderLinkPrev.UsePassword, folderLinkPrev.Password) != S_OK) { ::MessageBoxW((HWND)*this, MyFormatNew(IDS_CANNOT_UPDATE_FILE, fs2us(folderLink.FilePath)), L"7-Zip", MB_OK | MB_ICONSTOP); return S_OK; } } } } folderLink.DeleteDirAndFile(); return S_OK; } static const char * const kExeExtensions = " exe bat ps1 com lnk" " "; static const char * const kStartExtensions = #ifdef UNDER_CE " cab" #endif " exe bat ps1 com lnk" " chm" " msi doc dot xls ppt pps wps wpt wks xlr wdb vsd pub" " docx docm dotx dotm xlsx xlsm xltx xltm xlsb xps" " xlam pptx pptm potx potm ppam ppsx ppsm vsdx xsn" " mpp" " msg" " dwf" " flv swf" " epub" " odt ods" " wb3" " pdf" " ps" " txt" " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml" " h hpp hxx c cpp cxx m mm go swift" " awk sed hta js json php php3 php4 php5 phptml pl pm py pyo rb tcl ts vbs" " asm" " mak clw csproj vcproj sln dsp dsw" " "; // bool FindExt(const char *p, const UString &name, AString &s); bool FindExt(const char *p, const UString &name, CStringFinder &finder); static bool DoItemAlwaysStart(const UString &name) { CStringFinder finder; return FindExt(kStartExtensions, name, finder); } UString GetQuotedString(const UString &s); void SplitCmdLineSmart(const UString &cmd, UString &prg, UString ¶ms); void SplitCmdLineSmart(const UString &cmd, UString &prg, UString ¶ms) { params.Empty(); prg = cmd; prg.Trim(); if (prg.Len() >= 2 && prg[0] == L'"') { int pos = prg.Find(L'"', 1); if (pos >= 0) { if ((unsigned)(pos + 1) == prg.Len() || prg[pos + 1] == ' ') { params = prg.Ptr((unsigned)(pos + 1)); params.Trim(); prg.DeleteFrom((unsigned)pos); prg.DeleteFrontal(1); } } } } static WRes StartAppWithParams(const UString &cmd, const UStringVector ¶mVector, CProcess &process) { UString param; UString prg; SplitCmdLineSmart(cmd, prg, param); param.Trim(); // int pos = params.Find(L"%1"); FOR_VECTOR (i, paramVector) { if (!param.IsEmpty() && param.Back() != ' ') param.Add_Space(); param += GetQuotedString(paramVector[i]); } return process.Create(prg, param, NULL); } static HRESULT StartEditApplication(const UString &path, bool useEditor, HWND window, CProcess &process) { UString command; ReadRegEditor(useEditor, command); if (command.IsEmpty()) { #ifdef UNDER_CE command = "\\Windows\\"; #else FString winDir; if (!GetWindowsDir(winDir)) return 0; NName::NormalizeDirPathPrefix(winDir); command = fs2us(winDir); #endif command += "notepad.exe"; } UStringVector params; params.Add(path); const WRes res = StartAppWithParams(command, params, process); if (res != 0) ::MessageBoxW(window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP); return HRESULT_FROM_WIN32(res); } void CApp::DiffFiles() { const CPanel &panel = GetFocusedPanel(); if (!panel.Is_IO_FS_Folder()) { panel.MessageBox_Error_UnsupportOperation(); return; } CRecordVector indices; panel.Get_ItemIndices_Selected(indices); UString path1, path2; if (indices.Size() == 2) { path1 = panel.GetItemFullPath(indices[0]); path2 = panel.GetItemFullPath(indices[1]); } else if (indices.Size() == 1 && NumPanels >= 2) { const CPanel &destPanel = Panels[1 - LastFocusedPanel]; if (!destPanel.Is_IO_FS_Folder()) { panel.MessageBox_Error_UnsupportOperation(); return; } path1 = panel.GetItemFullPath(indices[0]); CRecordVector indices2; destPanel.Get_ItemIndices_Selected(indices2); if (indices2.Size() == 1) path2 = destPanel.GetItemFullPath(indices2[0]); else { UString relPath = panel.GetItemRelPath2(indices[0]); if (panel._flatMode && !destPanel._flatMode) relPath = panel.GetItemName(indices[0]); path2 = destPanel._currentFolderPrefix + relPath; } } else return; DiffFiles(path1, path2); } void CApp::DiffFiles(const UString &path1, const UString &path2) { UString command; ReadRegDiff(command); if (command.IsEmpty()) return; UStringVector params; params.Add(path1); params.Add(path2); WRes res; { CProcess process; res = StartAppWithParams(command, params, process); } if (res == 0) return; ::MessageBoxW(_window, LangString(IDS_CANNOT_START_EDITOR), L"7-Zip", MB_OK | MB_ICONSTOP); } HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process); void StartApplicationDontWait(const UString &dir, const UString &path, HWND window); void CPanel::EditItem(unsigned index, bool useEditor) { if (!_parentFolders.IsEmpty()) { OpenItemInArchive(index, false, true, true, useEditor); return; } CProcess process; StartEditApplication(GetItemFullPath(index), useEditor, (HWND)*this, process); } void CPanel::OpenFolderExternal(unsigned index) { UString prefix = GetFsPath(); UString path = prefix; if (index == kParentIndex) { if (prefix.IsEmpty()) return; const wchar_t c = prefix.Back(); if (!IS_PATH_SEPAR(c) && c != ':') return; prefix.DeleteBack(); int pos = prefix.ReverseFind_PathSepar(); if (pos < 0) return; prefix.DeleteFrom((unsigned)(pos + 1)); path = prefix; } else { path += GetItemRelPath(index); path.Add_PathSepar(); } StartApplicationDontWait(prefix, path, (HWND)*this); } bool CPanel::IsVirus_Message(const UString &name) { UString name2; const wchar_t cRLO = (wchar_t)0x202E; bool isVirus = false; bool isSpaceError = false; name2 = name; if (name2.Find(cRLO) >= 0) { const UString badString(cRLO); name2.Replace(badString, L"[RLO]"); isVirus = true; } { const wchar_t * const kVirusSpaces = L" "; // const unsigned kNumSpaces = strlen(kVirusSpaces); for (;;) { int pos = name2.Find(kVirusSpaces); if (pos < 0) break; isVirus = true; isSpaceError = true; name2.Replace(kVirusSpaces, L" "); } } #ifdef _WIN32 { unsigned i; for (i = name2.Len(); i != 0;) { wchar_t c = name2[i - 1]; if (c != '.' && c != ' ') break; i--; name2.ReplaceOneCharAtPos(i, '_'); } if (i != name2.Len()) { CStringFinder finder; UString name3 = name2; name3.DeleteFrom(i); if (FindExt(kExeExtensions, name3, finder)) isVirus = true; } } #endif if (!isVirus) return false; UString s = LangString(IDS_VIRUS); if (!isSpaceError) { const int pos1 = s.Find(L'('); if (pos1 >= 0) { const int pos2 = s.Find(L')', (unsigned)pos1 + 1); if (pos2 >= 0) { s.Delete((unsigned)pos1, (unsigned)pos2 + 1 - (unsigned)pos1); if (pos1 > 0 && s[pos1 - 1] == ' ' && s[pos1] == '.') s.Delete(pos1 - 1); } } } UString name3 = name; name3.Replace(L'\n', L'_'); name2.Replace(L'\n', L'_'); s.Add_LF(); s += name2; s.Add_LF(); s += name3; MessageBox_Error(s); return true; } void CPanel::OpenItem(unsigned index, bool tryInternal, bool tryExternal, const wchar_t *type) { CDisableTimerProcessing disableTimerProcessing(*this); const UString name = GetItemRelPath2(index); if (tryExternal) if (IsVirus_Message(name)) return; if (!_parentFolders.IsEmpty()) { OpenItemInArchive(index, tryInternal, tryExternal, false, false, type); return; } CDisableNotify disableNotify(*this); UString prefix = GetFsPath(); UString fullPath = prefix + name; if (tryInternal) if (!tryExternal || !DoItemAlwaysStart(name)) { HRESULT res = OpenAsArc_Index(index, type // , true ); disableNotify.Restore(); // we must restore to allow text notification update InvalidateList(); if (res == S_OK || res == E_ABORT) return; if (res != S_FALSE) { MessageBox_Error_HRESULT(res); return; } } if (tryExternal) { // SetCurrentDirectory opens HANDLE to folder!!! // NDirectory::MySetCurrentDirectory(prefix); StartApplicationDontWait(prefix, fullPath, (HWND)*this); } } class CThreadCopyFrom: public CProgressThreadVirt { HRESULT ProcessVirt() Z7_override; public: UString FullPath; UInt32 ItemIndex; CMyComPtr FolderOperations; CMyComPtr UpdateCallback; CUpdateCallback100Imp *UpdateCallbackSpec; }; HRESULT CThreadCopyFrom::ProcessVirt() { return FolderOperations->CopyFromFile(ItemIndex, FullPath, UpdateCallback); } HRESULT CPanel::OnOpenItemChanged(UInt32 index, const wchar_t *fullFilePath, bool usePassword, const UString &password) { if (!_folderOperations) { MessageBox_Error_UnsupportOperation(); return E_FAIL; } CThreadCopyFrom t; t.UpdateCallbackSpec = new CUpdateCallback100Imp; t.UpdateCallback = t.UpdateCallbackSpec; t.UpdateCallbackSpec->ProgressDialog = &t; t.ItemIndex = index; t.FullPath = fullFilePath; t.FolderOperations = _folderOperations; t.UpdateCallbackSpec->Init(); t.UpdateCallbackSpec->PasswordIsDefined = usePassword; t.UpdateCallbackSpec->Password = password; RINOK(t.Create(GetItemName(index), (HWND)*this)) return t.Result; } LRESULT CPanel::OnOpenItemChanged(LPARAM lParam) { // DEBUG_PRINT_NUM("OnOpenItemChanged", GetCurrentThreadId()); CTmpProcessInfo &tpi = *(CTmpProcessInfo *)lParam; if (tpi.FullPathFolderPrefix != _currentFolderPrefix) return 0; UInt32 fileIndex = tpi.FileIndex; UInt32 numItems; _folder->GetNumberOfItems(&numItems); // This code is not 100% OK for cases when there are several files with // tpi.RelPath name and there are changes in archive before update. // So tpi.FileIndex can point to another file. if (fileIndex >= numItems || GetItemRelPath(fileIndex) != tpi.RelPath) { UInt32 i; for (i = 0; i < numItems; i++) if (GetItemRelPath(i) == tpi.RelPath) break; if (i == numItems) return 0; fileIndex = i; } CSelectedState state; SaveSelectedState(state); CDisableNotify disableNotify(*this); // do we need it?? HRESULT result = OnOpenItemChanged(fileIndex, fs2us(tpi.FilePath), tpi.UsePassword, tpi.Password); RefreshListCtrl(state); if (result != S_OK) return 0; return 1; } CExitEventLauncher g_ExitEventLauncher; void CExitEventLauncher::Exit(bool hardExit) { if (_needExit) { _exitEvent.Set(); _needExit = false; } if (_numActiveThreads == 0) return; FOR_VECTOR (i, _threads) { ::CThread &th = _threads[i]; if (Thread_WasCreated(&th)) { const DWORD waitResult = WaitForSingleObject(th, hardExit ? 100 : INFINITE); // Thread_Wait(&th); // if (waitResult == WAIT_TIMEOUT) wait = 1; if (!hardExit && waitResult != WAIT_OBJECT_0) continue; Thread_Close(&th); _numActiveThreads--; } } } static THREAD_FUNC_DECL MyThreadFunction(void *param) { DEBUG_PRINT("==== MyThreadFunction ===="); CMyUniquePtr tpi((CTmpProcessInfo *)param); CChildProcesses &processes = tpi->Processes; const bool mainProcessWasSet = !processes.Handles.IsEmpty(); bool isComplexMode = true; if (!processes.Handles.IsEmpty()) { const DWORD startTime = GetTickCount(); /* CPossibleProgs progs; { const UString &name = tpi->RelPath; int slashPos = name.ReverseFind_PathSepar(); int dotPos = name.ReverseFind_Dot(); if (dotPos > slashPos) { const UString ext = name.Ptr(dotPos + 1); AString extA = UnicodeStringToMultiByte(ext); extA.MakeLower_Ascii(); progs.SetFromExtension(extA); } } */ bool firstPass = true; for (;;) { CRecordVector handles; CUIntVector indices; FOR_VECTOR (i, processes.Handles) { if (handles.Size() > 60) break; if (processes.NeedWait[i]) { handles.Add(processes.Handles[i]); indices.Add(i); } } bool needFindProcessByPath = false; if (handles.IsEmpty()) { if (!firstPass) break; } else { handles.Add(g_ExitEventLauncher._exitEvent); DWORD waitResult = WaitForMultiObj_Any_Infinite(handles.Size(), handles.ConstData()); waitResult -= WAIT_OBJECT_0; if (waitResult >= handles.Size() - 1) { processes.CloseAll(); /* if (waitResult == handles.Size() - 1) { // exit event // we want to delete temp files, if progs were used if (processes.ProgsWereUsed) break; } */ return waitResult >= (DWORD)handles.Size() ? 1 : 0; } if (firstPass && indices.Size() == 1) { const DWORD curTime = GetTickCount() - startTime; /* if (curTime > 5 * 1000) progs.ProgNames.Clear(); */ needFindProcessByPath = (curTime < 2 * 1000); if (needFindProcessByPath) { NFind::CFileInfo newFileInfo; if (newFileInfo.Find(tpi->FilePath)) if (tpi->WasChanged_from_TempFileInfo(newFileInfo)) needFindProcessByPath = false; } DEBUG_PRINT_NUM(" -- firstPass -- time = ", curTime) } processes.DisableWait(indices[(unsigned)waitResult]); } firstPass = false; // Sleep(300); #ifndef UNDER_CE processes.Update(needFindProcessByPath /* , progs */); #endif } const DWORD curTime = GetTickCount() - startTime; DEBUG_PRINT_NUM("after time = ", curTime) processes.CloseAll(); isComplexMode = (curTime < 2 * 1000); } bool needCheckTimestamp = true; for (;;) { NFind::CFileInfo newFileInfo; if (!newFileInfo.Find(tpi->FilePath)) break; if (mainProcessWasSet) { if (tpi->WasChanged_from_TempFileInfo(newFileInfo)) { UString m = MyFormatNew(IDS_CANNOT_UPDATE_FILE, fs2us(tpi->FilePath)); if (tpi->ReadOnly) { m.Add_LF(); AddLangString(m, IDS_PROP_READ_ONLY); m.Add_LF(); m += tpi->FullPathFolderPrefix; ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP); return 0; } { const UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, tpi->RelPath); if (::MessageBoxW(g_HWND, message, L"7-Zip", MB_YESNOCANCEL | MB_ICONQUESTION) == IDYES) { // DEBUG_PRINT_NUM("SendMessage", GetCurrentThreadId()); if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi.get()) != 1) { ::MessageBoxW(g_HWND, m, L"7-Zip", MB_OK | MB_ICONSTOP); return 0; } } needCheckTimestamp = false; break; } } if (!isComplexMode) break; } // DEBUG_PRINT("WaitForSingleObject"); DWORD waitResult = ::WaitForSingleObject(g_ExitEventLauncher._exitEvent, INFINITE); // DEBUG_PRINT("---"); if (waitResult == WAIT_OBJECT_0) break; return 1; } { NFind::CFileInfo newFileInfo; const bool finded = newFileInfo.Find(tpi->FilePath); if (!needCheckTimestamp || !finded || !tpi->WasChanged_from_TempFileInfo(newFileInfo)) { DEBUG_PRINT("Delete Temp file"); tpi->DeleteDirAndFile(); } } return 0; } /* #if defined(_WIN32) && !defined(UNDER_CE) static const FChar * const k_ZoneId_StreamName = FTEXT(":Zone.Identifier"); #endif #ifndef UNDER_CE static void ReadZoneFile(CFSTR fileName, CByteBuffer &buf) { buf.Free(); NIO::CInFile file; if (!file.Open(fileName)) return; UInt64 fileSize; if (!file.GetLength(fileSize)) return; if (fileSize == 0 || fileSize >= ((UInt32)1 << 20)) return; buf.Alloc((size_t)fileSize); size_t processed; if (file.ReadFull(buf, (size_t)fileSize, processed) && processed == fileSize) return; buf.Free(); } static bool WriteZoneFile(CFSTR fileName, const CByteBuffer &buf) { NIO::COutFile file; if (!file.Create(fileName, true)) return false; UInt32 processed; if (!file.Write(buf, (UInt32)buf.Size(), processed)) return false; return processed == buf.Size(); } #endif */ /* Z7_CLASS_IMP_COM_1( CBufSeqOutStream_WithFile , ISequentialOutStream ) Byte *_buffer; size_t _size; size_t _pos; size_t _fileWritePos; bool fileMode; public: bool IsStreamInMem() const { return !fileMode; } size_t GetMemStreamWrittenSize() const { return _pos; } // ISequentialOutStream *FileStream; FString FilePath; COutFileStream *outFileStreamSpec; CMyComPtr outFileStream; CBufSeqOutStream_WithFile(): outFileStreamSpec(NULL) {} void Init(Byte *buffer, size_t size) { fileMode = false; _buffer = buffer; _pos = 0; _size = size; _fileWritePos = 0; } HRESULT FlushToFile(); size_t GetPos() const { return _pos; } }; static const UInt32 kBlockSize = ((UInt32)1 << 31); STDMETHODIMP CBufSeqOutStream_WithFile::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (!fileMode) { if (_size - _pos >= size) { if (size != 0) { memcpy(_buffer + _pos, data, size); _pos += size; } if (processedSize) *processedSize = (UInt32)size; return S_OK; } fileMode = true; } RINOK(FlushToFile()); return outFileStream->Write(data, size, processedSize); } HRESULT CBufSeqOutStream_WithFile::FlushToFile() { if (!outFileStream) { outFileStreamSpec = new COutFileStream; outFileStream = outFileStreamSpec; if (!outFileStreamSpec->Create(FilePath, false)) { outFileStream.Release(); return E_FAIL; // MessageBoxMyError(UString("Can't create file ") + fs2us(tempFilePath)); } } while (_fileWritePos != _pos) { size_t cur = _pos - _fileWritePos; UInt32 curSize = (cur < kBlockSize) ? (UInt32)cur : kBlockSize; UInt32 processedSizeLoc = 0; HRESULT res = outFileStream->Write(_buffer + _fileWritePos, curSize, &processedSizeLoc); _fileWritePos += processedSizeLoc; RINOK(res); if (processedSizeLoc == 0) return E_FAIL; } return S_OK; } */ /* static HRESULT GetTime(IFolderFolder *folder, UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) { filetimeIsDefined = false; NCOM::CPropVariant prop; RINOK(folder->GetProperty(index, propID, &prop)); if (prop.vt == VT_FILETIME) { filetime = prop.filetime; filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } */ /* tryInternal tryExternal false false : unused false true : external true false : internal true true : smart based on file extension: !alwaysStart(name) : both alwaysStart(name) : external */ void CPanel::OpenItemInArchive(unsigned index, bool tryInternal, bool tryExternal, bool editMode, bool useEditor, const wchar_t *type) { // we don't want to change hash data here if (IsHashFolder()) return; const UString name = GetItemName(index); const UString relPath = GetItemRelPath(index); if (tryExternal) if (IsVirus_Message(name)) return; if (!_folderOperations) { MessageBox_Error_UnsupportOperation(); return; } bool tryAsArchive = tryInternal && (!tryExternal || !DoItemAlwaysStart(name)); const UString fullVirtPath = _currentFolderPrefix + relPath; CTempDir tempDirectory; if (!tempDirectory.Create(kTempDirPrefix)) { MessageBox_LastError(); return; } FString tempDir = tempDirectory.GetPath(); FString tempDirNorm = tempDir; NName::NormalizeDirPathPrefix(tempDirNorm); const FString tempFilePath = tempDirNorm + us2fs(Get_Correct_FsFile_Name(name)); CTempFileInfo tempFileInfo; tempFileInfo.FileIndex = index; tempFileInfo.RelPath = relPath; tempFileInfo.FolderPath = tempDir; tempFileInfo.FilePath = tempFilePath; tempFileInfo.NeedDelete = true; if (tryAsArchive) { CMyComPtr getStream; _folder.QueryInterface(IID_IInArchiveGetStream, &getStream); if (getStream) { CMyComPtr subSeqStream; getStream->GetStream(index, &subSeqStream); if (subSeqStream) { CMyComPtr subStream; subSeqStream.QueryInterface(IID_IInStream, &subStream); if (subStream) { HRESULT res = OpenAsArc_Msg(subStream, tempFileInfo, fullVirtPath, type ? type : L"" // , true // showErrorMessage ); if (res == S_OK) { tempDirectory.DisableDeleting(); RefreshListCtrl(); return; } if (res == E_ABORT || res != S_FALSE) return; if (!tryExternal) return; tryAsArchive = false; } } } } CRecordVector indices; indices.Add(index); UStringVector messages; bool usePassword = false; UString password; if (!_parentFolders.IsEmpty()) { const CFolderLink &fl = _parentFolders.Back(); usePassword = fl.UsePassword; password = fl.Password; } /* #if defined(_WIN32) && !defined(UNDER_CE) CByteBuffer zoneBuf; #ifndef _UNICODE if (g_IsNT) #endif if (!_parentFolders.IsEmpty()) { const CFolderLink &fl = _parentFolders.Front(); if (!fl.IsVirtual && !fl.FilePath.IsEmpty()) ReadZoneFile(fl.FilePath + k_ZoneId_StreamName, zoneBuf); } #endif */ CVirtFileSystem *virtFileSystemSpec = NULL; CMyComPtr virtFileSystem; const bool isAltStream = IsItem_AltStream(index); CCopyToOptions options; options.includeAltStreams = true; options.replaceAltStreamChars = isAltStream; { // CContextMenuInfo ci; // ci.Load(); // if (ci.WriteZone != (UInt32)(Int32)-1) // we use kAll when we unpack just one file. options.ZoneIdMode = NExtract::NZoneIdMode::kAll; options.NeedRegistryZone = false; } if (tryAsArchive) { // actually we want to get sum: size of main file plus sizes of altStreams. // but now there is no interface to get altStreams sizes. NCOM::CPropVariant prop; _folder->GetProperty(index, kpidSize, &prop); const size_t fileLimit = g_RAM_Size_Defined ? g_RAM_Size >> MyMax(_parentFolders.Size() + 1, 8u): 1u << 22; UInt64 fileSize = 0; if (!ConvertPropVariantToUInt64(prop, fileSize)) fileSize = fileLimit; #if 0 // 1 : for debug fileLimit = 1; #endif if (fileSize <= fileLimit) { options.streamMode = true; virtFileSystemSpec = new CVirtFileSystem; virtFileSystem = virtFileSystemSpec; virtFileSystemSpec->FileName = name; virtFileSystemSpec->IsAltStreamFile = isAltStream; #if defined(_WIN32) && !defined(UNDER_CE) #ifndef _UNICODE if (g_IsNT) #endif { Get_ZoneId_Stream_from_ParentFolders(virtFileSystemSpec->ZoneBuf); options.ZoneBuf = virtFileSystemSpec->ZoneBuf; } #endif virtFileSystemSpec->MaxTotalAllocSize = (size_t)fileSize + (1 << 16); // we allow additional total size for small alt streams. virtFileSystemSpec->DirPrefix = tempDirNorm; // options.VirtFileSystem = virtFileSystem; options.VirtFileSystemSpec = virtFileSystemSpec; } } options.folder = fs2us(tempDirNorm); options.showErrorMessages = true; const HRESULT result = CopyTo(options, indices, &messages, usePassword, password); if (!_parentFolders.IsEmpty()) { CFolderLink &fl = _parentFolders.Back(); fl.UsePassword = usePassword; fl.Password = password; } if (!messages.IsEmpty()) return; if (result != S_OK) { if (result != E_ABORT) MessageBox_Error_HRESULT(result); return; } if (virtFileSystemSpec && !virtFileSystemSpec->WasStreamFlushedToFS()) { int index_in_Files = virtFileSystemSpec->Index_of_MainExtractedFile_in_Files; if (index_in_Files < 0) { if (virtFileSystemSpec->Files.Size() != 1) { MessageBox_Error_HRESULT(E_FAIL); return; } // it's not expected case that index was not set, but we support that case index_in_Files = 0; } { const CVirtFile &file = virtFileSystemSpec->Files[index_in_Files]; CMyComPtr2_Create bufInStream; bufInStream->Init(file.Data, file.WrittenSize, virtFileSystem); const HRESULT res = OpenAsArc_Msg(bufInStream, tempFileInfo, fullVirtPath, type ? type : L"" // , encrypted // , true // showErrorMessage ); if (res == S_OK) { if (virtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files >= 0 && !_parentFolders.IsEmpty()) { const CVirtFile &fileZone = virtFileSystemSpec->Files[ virtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files]; _parentFolders.Back().ZoneBuf.CopyFrom(fileZone.Data, fileZone.WrittenSize); } tempDirectory.DisableDeleting(); RefreshListCtrl(); return; } if (res == E_ABORT || res != S_FALSE) return; if (!tryExternal) return; tryAsArchive = false; if (virtFileSystemSpec->FlushToDisk(true) != S_OK) return; } } /* #if defined(_WIN32) && !defined(UNDER_CE) if (zoneBuf.Size() != 0) { if (NFind::DoesFileExist_Raw(tempFilePath)) { WriteZoneFile(tempFilePath + k_ZoneId_StreamName, zoneBuf); } } #endif */ if (tryAsArchive) { const HRESULT res = OpenAsArc_Msg(NULL, tempFileInfo, fullVirtPath, type ? type : L"" // , encrypted // , true // showErrorMessage ); if (res == S_OK) { tempDirectory.DisableDeleting(); RefreshListCtrl(); return; } if (res == E_ABORT || res != S_FALSE) return; } if (!tryExternal) return; CMyUniquePtr tpi(new CTmpProcessInfo()); tpi->FolderPath = tempDir; tpi->FilePath = tempFilePath; tpi->NeedDelete = true; tpi->UsePassword = usePassword; tpi->Password = password; tpi->ReadOnly = IsThereReadOnlyFolder(); if (IsHashFolder()) tpi->ReadOnly = true; if (!tpi->FileInfo.Find(tempFilePath)) return; CTmpProcessInfoRelease tmpProcessInfoRelease(*tpi); CProcess process; HRESULT res; if (editMode) res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process); else res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process); if ((HANDLE)process == NULL) { // win7 / win10 work so for some extensions (pdf, html ..); DEBUG_PRINT("#### (HANDLE)process == 0"); // return; if (res != S_OK) return; } tpi->Window = (HWND)*this; tpi->FullPathFolderPrefix = _currentFolderPrefix; tpi->FileIndex = index; tpi->RelPath = relPath; if ((HANDLE)process) tpi->Processes.SetMainProcess(process.Detach()); ::CThread th; if (Thread_Create(&th, MyThreadFunction, tpi.get()) != 0) throw 271824; g_ExitEventLauncher._threads.Add(th); g_ExitEventLauncher._numActiveThreads++; tempDirectory.DisableDeleting(); tpi.release(); tmpProcessInfoRelease._needDelete = false; } /* static const UINT64 kTimeLimit = UINT64(10000000) * 3600 * 24; static bool CheckDeleteItem(UINT64 currentFileTime, UINT64 folderFileTime) { return (currentFileTime - folderFileTime > kTimeLimit && folderFileTime - currentFileTime > kTimeLimit); } void DeleteOldTempFiles() { UString tempPath; if (!MyGetTempPath(tempPath)) throw 1; UINT64 currentFileTime; NTime::GetCurUtcFileTime(currentFileTime); UString searchWildCard = tempPath + kTempDirPrefix + L"*.tmp"; searchWildCard += WCHAR(NName::kAnyStringWildcard); NFind::CEnumeratorW enumerator(searchWildCard); NFind::CFileInfo fileInfo; while (enumerator.Next(fileInfo)) { if (!fileInfo.IsDir()) continue; const UINT64 &cTime = *(const UINT64 *)(&fileInfo.CTime); if (CheckDeleteItem(cTime, currentFileTime)) RemoveDirectoryWithSubItems(tempPath + fileInfo.Name); } } */