diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2015-12-31 03:00:00 +0300 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:16:58 +0300 |
commit | 9608215ad8deb58355bae27692669fda067c4f43 (patch) | |
tree | 1227131a3f19bc36e5da4ba11791154d05cc08af /CPP/7zip/UI/FileManager/PanelItemOpen.cpp | |
parent | 5de23c1deb52b8be4c43ad9f694c64bbddd0c38a (diff) |
15.1315.13
Diffstat (limited to 'CPP/7zip/UI/FileManager/PanelItemOpen.cpp')
-rw-r--r-- | CPP/7zip/UI/FileManager/PanelItemOpen.cpp | 575 |
1 files changed, 507 insertions, 68 deletions
diff --git a/CPP/7zip/UI/FileManager/PanelItemOpen.cpp b/CPP/7zip/UI/FileManager/PanelItemOpen.cpp index c37ac64f..cfbc5770 100644 --- a/CPP/7zip/UI/FileManager/PanelItemOpen.cpp +++ b/CPP/7zip/UI/FileManager/PanelItemOpen.cpp @@ -6,6 +6,8 @@ #include <tlhelp32.h> +#include "../../../Common/IntToString.h" + #include "../../../Common/AutoPtr.h" #include "../../../Common/StringConvert.h" @@ -24,6 +26,7 @@ #include "FileFolderPluginOpen.h" #include "FormatUtils.h" #include "LangUtils.h" +#include "PropertyNameRes.h" #include "RegistryUtils.h" #include "UpdateCallback100.h" @@ -45,6 +48,20 @@ extern bool g_IsNT; static CFSTR 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 @@ -76,90 +93,329 @@ public: #endif -typedef DWORD (WINAPI *GetProcessIdFunc)(HANDLE process); + +/* +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 < 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); +} +*/ + +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 = (Func_GetProcessImageFileNameW) + ::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_GetProcessImageFileNameW)::GetProcAddress(g_Psapi_dll_module, func_name); + } + + if (my_func) + { + // DWORD num = GetProcessImageFileNameW(hProcess, temp, maxPath); + DWORD num = my_func(hProcess, temp, maxPath); + if (num != 0) + path = temp; + } + // FreeLibrary(lib); +} + +struct CSnapshotProcess +{ + DWORD Id; + DWORD ParentId; + UString Name; +}; + +static void GetSnapshot(CObjectVector<CSnapshotProcess> &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<DWORD> _ids; #endif + public: + // bool ProgsWereUsed; CRecordVector<HANDLE> Handles; CRecordVector<bool> 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) + 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 AddProcess(HANDLE h) + void SetMainProcess(HANDLE h) { #ifndef UNDER_CE - GetProcessIdFunc func = (GetProcessIdFunc)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "GetProcessId"); + + Func_GetProcessId func = (Func_GetProcessId)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "GetProcessId"); if (func) - _ids.AddToUniqueSorted(func(h)); + { + 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); } - void Update() + #ifndef UNDER_CE + + void Update(bool needFindProcessByPath /* , const CPossibleProgs &progs */) { - #ifndef UNDER_CE - CRecordVector<DWORD> ids, parents; + /* + if (_ids.IsEmpty()) + return; + */ + + CObjectVector<CSnapshotProcess> sps; + GetSnapshot(sps); + + const int separ = Path.ReverseFind_PathSepar(); + const UString mainName = Path.Ptr(separ + 1); + if (mainName.IsEmpty()) + needFindProcessByPath = false; + + const DWORD currentProcessId = GetCurrentProcessId(); + + for (;;) { - CProcessSnapshot snapshot; - if (snapshot.Create()) + bool wasAdded = false; + + FOR_VECTOR (i, sps) { - PROCESSENTRY32 pe; - memset(&pe, 0, sizeof(pe)); - pe.dwSize = sizeof(pe); - BOOL res = snapshot.GetFirstProcess(&pe); - while (res) + 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)) { - ids.Add(pe.th32ProcessID); - parents.Add(pe.th32ParentProcessID); - res = snapshot.GetNextProcess(&pe); + needAdd = true; + isFromProgs = true; } - } - } + */ - for (;;) - { - unsigned i; - for (i = 0; i < ids.Size(); i++) - { - DWORD id = ids[i]; - if (_ids.FindInSorted(parents[i]) >= 0 && - _ids.FindInSorted(id) < 0) + if (needAdd) { + DEBUG_PRINT("----- OpenProcess -----"); + DEBUG_PRINT_W(name); HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, id); if (hProcess) { - _ids.AddToUniqueSorted(id); + DEBUG_PRINT("----- OpenProcess OK -----"); + // if (!isFromProgs) + _ids.AddToUniqueSorted(id); Handles.Add(hProcess); NeedWait.Add(true); - break; + // Names.Add(name); + wasAdded = true; + // ProgsWereUsed = isFromProgs; } } } - if (i == ids.Size()) + + if (!wasAdded) break; } - #endif } + + #endif }; + struct CTmpProcessInfo: public CTempFileInfo { CChildProcesses Processes; @@ -168,9 +424,12 @@ struct CTmpProcessInfo: public CTempFileInfo bool UsePassword; UString Password; - CTmpProcessInfo(): UsePassword(false) {} + bool ReadOnly; + + CTmpProcessInfo(): UsePassword(false), ReadOnly(false) {} }; + class CTmpProcessInfoRelease { CTmpProcessInfo *_tmpProcessInfo; @@ -600,6 +859,9 @@ static HRESULT StartApplication(const UString &dir, const UString &path, HWND wi process.Attach(execInfo.hProcess); } + + DEBUG_PRINT_NUM("-- ShellExecuteEx -- execInfo.hInstApp = ", result) + if (result <= 32) { switch (result) @@ -610,6 +872,8 @@ static HRESULT StartApplication(const UString &dir, const UString &path, HWND wi // L"There is no application associated with the given file name extension", L"7-Zip", MB_OK | MB_ICONSTOP); } + + return E_FAIL; // fixed in 15.13. Can we use it for any Windows version? } return S_OK; @@ -800,6 +1064,8 @@ HRESULT CPanel::OnOpenItemChanged(UInt32 index, const wchar_t *fullFilePath, LRESULT CPanel::OnOpenItemChanged(LPARAM lParam) { + // DEBUG_PRINT_NUM("OnOpenItemChanged", GetCurrentThreadId()); + CTmpProcessInfo &tpi = *(CTmpProcessInfo *)lParam; if (tpi.FullPathFolderPrefix != _currentFolderPrefix) return 0; @@ -834,24 +1100,75 @@ LRESULT CPanel::OnOpenItemChanged(LPARAM lParam) return 1; } -class CExitEventLauncher + +CExitEventLauncher g_ExitEventLauncher; + +void CExitEventLauncher::Exit(bool hardExit) { -public: - NWindows::NSynchronization::CManualResetEvent _exitEvent; - CExitEventLauncher() + if (_needExit) { - if (_exitEvent.Create(false) != S_OK) - throw 9387173; - }; - ~CExitEventLauncher() { _exitEvent.Set(); } -} g_ExitEventLauncher; + _exitEvent.Set(); + _needExit = false; + } + + if (_numActiveThreads == 0) + return; + + FOR_VECTOR (i, _threads) + { + ::CThread &th = _threads[i]; + DWORD wait = (hardExit ? 100 : INFINITE); + if (Thread_WasCreated(&th)) + { + DWORD waitResult = WaitForSingleObject(th, wait); + // 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 ===="); + CMyAutoPtr<CTmpProcessInfo> tmpProcessInfoPtr((CTmpProcessInfo *)param); CTmpProcessInfo *tpi = tmpProcessInfoPtr.get(); CChildProcesses &processes = tpi->Processes; + 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<HANDLE> handles; @@ -859,6 +1176,8 @@ static THREAD_FUNC_DECL MyThreadFunction(void *param) FOR_VECTOR (i, processes.Handles) { + if (handles.Size() > 60) + break; if (processes.NeedWait[i]) { handles.Add(processes.Handles[i]); @@ -866,43 +1185,150 @@ static THREAD_FUNC_DECL MyThreadFunction(void *param) } } + bool needFindProcessByPath = false; + if (handles.IsEmpty()) - break; + { + if (!firstPass) + break; + } + else + { + handles.Add(g_ExitEventLauncher._exitEvent); + + DWORD waitResult = ::WaitForMultipleObjects(handles.Size(), &handles.Front(), FALSE, INFINITE); + + 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; + } - handles.Add(g_ExitEventLauncher._exitEvent); + if (firstPass && indices.Size() == 1) + { + DWORD curTime = GetTickCount() - startTime; - DWORD waitResult = ::WaitForMultipleObjects(handles.Size(), &handles.Front(), FALSE, INFINITE); + /* + if (curTime > 5 * 1000) + progs.ProgNames.Clear(); + */ - if (waitResult >= (DWORD)handles.Size() - 1) - { - processes.CloseAll(); - return waitResult >= (DWORD)handles.Size() ? 1 : 0; + needFindProcessByPath = (curTime < 2 * 1000); + + if (needFindProcessByPath) + { + NFind::CFileInfo newFileInfo; + if (newFileInfo.Find(tpi->FilePath)) + if (tpi->WasChanged(newFileInfo)) + needFindProcessByPath = false; + } + + DEBUG_PRINT_NUM(" -- firstPass -- time = ", curTime) + } + + processes.DisableWait(indices[waitResult]); } - processes.Update(); - processes.DisableWait(indices[waitResult]); + + firstPass = false; + + // Sleep(300); + #ifndef UNDER_CE + processes.Update(needFindProcessByPath /* , progs */); + #endif } - NFind::CFileInfo newFileInfo; - if (newFileInfo.Find(tpi->FilePath)) + + DWORD curTime = GetTickCount() - startTime; + + DEBUG_PRINT_NUM("after time = ", curTime) + + processes.CloseAll(); + + isComplexMode = (curTime < 2 * 1000); + + } + + bool needCheckTimestamp = true; + + for (;;) { - if (tpi->WasChanged(newFileInfo)) + NFind::CFileInfo newFileInfo; + + if (!newFileInfo.Find(tpi->FilePath)) + break; + + if (mainProcessWasSet) { - UString message = MyFormatNew(IDS_WANT_UPDATE_MODIFIED_FILE, tpi->RelPath); - if (::MessageBoxW(g_HWND, message, L"7-Zip", MB_OKCANCEL | MB_ICONQUESTION) == IDOK) + if (tpi->WasChanged(newFileInfo)) { - if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi) != 1) + UString m = MyFormatNew(IDS_CANNOT_UPDATE_FILE, fs2us(tpi->FilePath)); + if (tpi->ReadOnly) { - ::MessageBoxW(g_HWND, MyFormatNew(IDS_CANNOT_UPDATE_FILE, - fs2us(tpi->FilePath)), L"7-Zip", MB_OK | MB_ICONSTOP); + 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_OKCANCEL | MB_ICONQUESTION) == IDOK) + { + // DEBUG_PRINT_NUM("SendMessage", GetCurrentThreadId()); + if (SendMessage(tpi->Window, kOpenItemChanged, 0, (LONG_PTR)tpi) != 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; } - tpi->DeleteDirAndFile(); + + { + NFind::CFileInfo newFileInfo; + + bool finded = newFileInfo.Find(tpi->FilePath); + + if (!needCheckTimestamp || !finded || !tpi->WasChanged(newFileInfo)) + { + DEBUG_PRINT("Delete Temp file"); + tpi->DeleteDirAndFile(); + } + } + return 0; } + + #if defined(_WIN32) && !defined(UNDER_CE) static const FChar *k_ZoneId_StreamName = FTEXT(":Zone.Identifier"); #endif @@ -1284,6 +1710,7 @@ void CPanel::OpenItemInArchive(int index, bool tryInternal, bool tryExternal, bo tpi->NeedDelete = true; tpi->UsePassword = usePassword; tpi->Password = password; + tpi->ReadOnly = IsThereReadOnlyFolder(); if (!tpi->FileInfo.Find(tempFilePath)) return; @@ -1291,29 +1718,41 @@ void CPanel::OpenItemInArchive(int index, bool tryInternal, bool tryExternal, bo CTmpProcessInfoRelease tmpProcessInfoRelease(*tpi); CProcess process; - // HRESULT res; + HRESULT res; if (editMode) - /* res = */ StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process); + res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process); else - /* res = */ StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process); + res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process); - if ((HANDLE)process == 0) - return; + if ((HANDLE)process == NULL) + { + // win7 / win10 work so for some extensions (pdf, html ..); + DEBUG_PRINT("#### (HANDLE)process == 0"); + // return; + if (res != SZ_OK) + return; + } tpi->Window = (HWND)(*this); tpi->FullPathFolderPrefix = _currentFolderPrefix; tpi->FileIndex = index; tpi->RelPath = relPath; - tpi->Processes.AddProcess(process.Detach()); + + if ((HANDLE)process != 0) + tpi->Processes.SetMainProcess(process.Detach()); - NWindows::CThread thread; - if (thread.Create(MyThreadFunction, tpi) != S_OK) + ::CThread th; + if (Thread_Create(&th, MyThreadFunction, tpi) != 0) throw 271824; + g_ExitEventLauncher._threads.Add(th); + g_ExitEventLauncher._numActiveThreads++; + tempDirectory.DisableDeleting(); tmpProcessInfoPtr.release(); tmpProcessInfoRelease._needDelete = false; } + /* static const UINT64 kTimeLimit = UINT64(10000000) * 3600 * 24; |