| // Main.cpp | |
| #include "StdAfx.h" | |
| #include "../../../Common/MyInitGuid.h" | |
| #include "../../../Common/CommandLineParser.h" | |
| #include "../../../Common/StringConvert.h" | |
| #include "../../../Common/TextConfig.h" | |
| #include "../../../Windows/DLL.h" | |
| #include "../../../Windows/ErrorMsg.h" | |
| #include "../../../Windows/FileDir.h" | |
| #include "../../../Windows/FileFind.h" | |
| #include "../../../Windows/FileIO.h" | |
| #include "../../../Windows/FileName.h" | |
| #include "../../../Windows/NtCheck.h" | |
| #include "../../../Windows/ResourceString.h" | |
| #include "../../UI/Explorer/MyMessages.h" | |
| #include "ExtractEngine.h" | |
| #include "resource.h" | |
| using namespace NWindows; | |
| using namespace NFile; | |
| using namespace NDir; | |
| HINSTANCE g_hInstance; | |
| static CFSTR kTempDirPrefix = FTEXT("7zS"); | |
| #define _SHELL_EXECUTE | |
| static bool ReadDataString(CFSTR fileName, LPCSTR startID, | |
| LPCSTR endID, AString &stringResult) | |
| { | |
| stringResult.Empty(); | |
| NIO::CInFile inFile; | |
| if (!inFile.Open(fileName)) | |
| return false; | |
| const int kBufferSize = (1 << 12); | |
| Byte buffer[kBufferSize]; | |
| int signatureStartSize = MyStringLen(startID); | |
| int signatureEndSize = MyStringLen(endID); | |
| UInt32 numBytesPrev = 0; | |
| bool writeMode = false; | |
| UInt64 posTotal = 0; | |
| for (;;) | |
| { | |
| if (posTotal > (1 << 20)) | |
| return (stringResult.IsEmpty()); | |
| UInt32 numReadBytes = kBufferSize - numBytesPrev; | |
| UInt32 processedSize; | |
| if (!inFile.Read(buffer + numBytesPrev, numReadBytes, processedSize)) | |
| return false; | |
| if (processedSize == 0) | |
| return true; | |
| UInt32 numBytesInBuffer = numBytesPrev + processedSize; | |
| UInt32 pos = 0; | |
| for (;;) | |
| { | |
| if (writeMode) | |
| { | |
| if (pos > numBytesInBuffer - signatureEndSize) | |
| break; | |
| if (memcmp(buffer + pos, endID, signatureEndSize) == 0) | |
| return true; | |
| char b = buffer[pos]; | |
| if (b == 0) | |
| return false; | |
| stringResult += b; | |
| pos++; | |
| } | |
| else | |
| { | |
| if (pos > numBytesInBuffer - signatureStartSize) | |
| break; | |
| if (memcmp(buffer + pos, startID, signatureStartSize) == 0) | |
| { | |
| writeMode = true; | |
| pos += signatureStartSize; | |
| } | |
| else | |
| pos++; | |
| } | |
| } | |
| numBytesPrev = numBytesInBuffer - pos; | |
| posTotal += pos; | |
| memmove(buffer, buffer + pos, numBytesPrev); | |
| } | |
| } | |
| static char kStartID[] = ",!@Install@!UTF-8!"; | |
| static char kEndID[] = ",!@InstallEnd@!"; | |
| class CInstallIDInit | |
| { | |
| public: | |
| CInstallIDInit() | |
| { | |
| kStartID[0] = ';'; | |
| kEndID[0] = ';'; | |
| }; | |
| } g_CInstallIDInit; | |
| #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1; | |
| static void ShowErrorMessageSpec(const UString &name) | |
| { | |
| UString message = NError::MyFormatMessage(::GetLastError()); | |
| int pos = message.Find(L"%1"); | |
| if (pos >= 0) | |
| { | |
| message.Delete(pos, 2); | |
| message.Insert(pos, name); | |
| } | |
| ShowErrorMessage(NULL, message); | |
| } | |
| int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, | |
| #ifdef UNDER_CE | |
| LPWSTR | |
| #else | |
| LPSTR | |
| #endif | |
| /* lpCmdLine */,int /* nCmdShow */) | |
| { | |
| g_hInstance = (HINSTANCE)hInstance; | |
| NT_CHECK | |
| // InitCommonControls(); | |
| UString archiveName, switches; | |
| #ifdef _SHELL_EXECUTE | |
| UString executeFile, executeParameters; | |
| #endif | |
| NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches); | |
| FString fullPath; | |
| NDLL::MyGetModuleFileName(fullPath); | |
| switches.Trim(); | |
| bool assumeYes = false; | |
| if (MyStringCompareNoCase_N(switches, L"-y", 2) == 0) | |
| { | |
| assumeYes = true; | |
| switches = switches.Ptr(2); | |
| switches.Trim(); | |
| } | |
| AString config; | |
| if (!ReadDataString(fullPath, kStartID, kEndID, config)) | |
| { | |
| if (!assumeYes) | |
| ShowErrorMessage(L"Can't load config info"); | |
| return 1; | |
| } | |
| UString dirPrefix = L"." WSTRING_PATH_SEPARATOR; | |
| UString appLaunched; | |
| bool showProgress = true; | |
| if (!config.IsEmpty()) | |
| { | |
| CObjectVector<CTextConfigPair> pairs; | |
| if (!GetTextConfig(config, pairs)) | |
| { | |
| if (!assumeYes) | |
| ShowErrorMessage(L"Config failed"); | |
| return 1; | |
| } | |
| UString friendlyName = GetTextConfigValue(pairs, L"Title"); | |
| UString installPrompt = GetTextConfigValue(pairs, L"BeginPrompt"); | |
| UString progress = GetTextConfigValue(pairs, L"Progress"); | |
| if (progress.IsEqualToNoCase(L"no")) | |
| showProgress = false; | |
| int index = FindTextConfigItem(pairs, L"Directory"); | |
| if (index >= 0) | |
| dirPrefix = pairs[index].String; | |
| if (!installPrompt.IsEmpty() && !assumeYes) | |
| { | |
| if (MessageBoxW(0, installPrompt, friendlyName, MB_YESNO | | |
| MB_ICONQUESTION) != IDYES) | |
| return 0; | |
| } | |
| appLaunched = GetTextConfigValue(pairs, L"RunProgram"); | |
| #ifdef _SHELL_EXECUTE | |
| executeFile = GetTextConfigValue(pairs, L"ExecuteFile"); | |
| executeParameters = GetTextConfigValue(pairs, L"ExecuteParameters"); | |
| #endif | |
| } | |
| CTempDir tempDir; | |
| if (!tempDir.Create(kTempDirPrefix)) | |
| { | |
| if (!assumeYes) | |
| ShowErrorMessage(L"Can not create temp folder archive"); | |
| return 1; | |
| } | |
| CCodecs *codecs = new CCodecs; | |
| CMyComPtr<IUnknown> compressCodecsInfo = codecs; | |
| HRESULT result = codecs->Load(); | |
| if (result != S_OK) | |
| { | |
| ShowErrorMessage(L"Can not load codecs"); | |
| return 1; | |
| } | |
| const FString tempDirPath = tempDir.GetPath(); | |
| // tempDirPath = L"M:\\1\\"; // to test low disk space | |
| { | |
| bool isCorrupt = false; | |
| UString errorMessage; | |
| HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress, | |
| isCorrupt, errorMessage); | |
| if (result != S_OK) | |
| { | |
| if (!assumeYes) | |
| { | |
| if (result == S_FALSE || isCorrupt) | |
| { | |
| NWindows::MyLoadString(IDS_EXTRACTION_ERROR_MESSAGE, errorMessage); | |
| result = E_FAIL; | |
| } | |
| if (result != E_ABORT) | |
| { | |
| if (errorMessage.IsEmpty()) | |
| errorMessage = NError::MyFormatMessage(result); | |
| ::MessageBoxW(0, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR); | |
| } | |
| } | |
| return 1; | |
| } | |
| } | |
| #ifndef UNDER_CE | |
| CCurrentDirRestorer currentDirRestorer; | |
| if (!SetCurrentDir(tempDirPath)) | |
| return 1; | |
| #endif | |
| HANDLE hProcess = 0; | |
| #ifdef _SHELL_EXECUTE | |
| if (!executeFile.IsEmpty()) | |
| { | |
| CSysString filePath = GetSystemString(executeFile); | |
| SHELLEXECUTEINFO execInfo; | |
| execInfo.cbSize = sizeof(execInfo); | |
| execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | |
| #ifndef UNDER_CE | |
| | SEE_MASK_FLAG_DDEWAIT | |
| #endif | |
| ; | |
| execInfo.hwnd = NULL; | |
| execInfo.lpVerb = NULL; | |
| execInfo.lpFile = filePath; | |
| if (!switches.IsEmpty()) | |
| { | |
| if (!executeParameters.IsEmpty()) | |
| executeParameters += L' '; | |
| executeParameters += switches; | |
| } | |
| CSysString parametersSys = GetSystemString(executeParameters); | |
| if (parametersSys.IsEmpty()) | |
| execInfo.lpParameters = NULL; | |
| else | |
| execInfo.lpParameters = parametersSys; | |
| execInfo.lpDirectory = NULL; | |
| execInfo.nShow = SW_SHOWNORMAL; | |
| execInfo.hProcess = 0; | |
| /* BOOL success = */ ::ShellExecuteEx(&execInfo); | |
| UINT32 result = (UINT32)(UINT_PTR)execInfo.hInstApp; | |
| if(result <= 32) | |
| { | |
| if (!assumeYes) | |
| ShowErrorMessage(L"Can not open file"); | |
| return 1; | |
| } | |
| hProcess = execInfo.hProcess; | |
| } | |
| else | |
| #endif | |
| { | |
| if (appLaunched.IsEmpty()) | |
| { | |
| appLaunched = L"setup.exe"; | |
| if (!NFind::DoesFileExist(us2fs(appLaunched))) | |
| { | |
| if (!assumeYes) | |
| ShowErrorMessage(L"Can not find setup.exe"); | |
| return 1; | |
| } | |
| } | |
| { | |
| FString s2 = tempDirPath; | |
| NName::NormalizeDirPathPrefix(s2); | |
| appLaunched.Replace(L"%%T" WSTRING_PATH_SEPARATOR, fs2us(s2)); | |
| } | |
| UString appNameForError = appLaunched; // actually we need to rtemove parameters also | |
| appLaunched.Replace(L"%%T", fs2us(tempDirPath)); | |
| if (!switches.IsEmpty()) | |
| { | |
| appLaunched += L' '; | |
| appLaunched += switches; | |
| } | |
| STARTUPINFO startupInfo; | |
| startupInfo.cb = sizeof(startupInfo); | |
| startupInfo.lpReserved = 0; | |
| startupInfo.lpDesktop = 0; | |
| startupInfo.lpTitle = 0; | |
| startupInfo.dwFlags = 0; | |
| startupInfo.cbReserved2 = 0; | |
| startupInfo.lpReserved2 = 0; | |
| PROCESS_INFORMATION processInformation; | |
| CSysString appLaunchedSys = GetSystemString(dirPrefix + appLaunched); | |
| BOOL createResult = CreateProcess(NULL, (LPTSTR)(LPCTSTR)appLaunchedSys, | |
| NULL, NULL, FALSE, 0, NULL, NULL /*tempDir.GetPath() */, | |
| &startupInfo, &processInformation); | |
| if (createResult == 0) | |
| { | |
| if (!assumeYes) | |
| { | |
| // we print name of exe file, if error message is | |
| // ERROR_BAD_EXE_FORMAT: "%1 is not a valid Win32 application". | |
| ShowErrorMessageSpec(appNameForError); | |
| } | |
| return 1; | |
| } | |
| ::CloseHandle(processInformation.hThread); | |
| hProcess = processInformation.hProcess; | |
| } | |
| if (hProcess != 0) | |
| { | |
| WaitForSingleObject(hProcess, INFINITE); | |
| ::CloseHandle(hProcess); | |
| } | |
| return 0; | |
| } |