1 // Main.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/DllSecur.h"
6
7 #include "../../../Common/MyWindows.h"
8 #include "../../../Common/MyInitGuid.h"
9
10 #include "../../../Common/CommandLineParser.h"
11 #include "../../../Common/StringConvert.h"
12 #include "../../../Common/TextConfig.h"
13
14 #include "../../../Windows/DLL.h"
15 #include "../../../Windows/ErrorMsg.h"
16 #include "../../../Windows/FileDir.h"
17 #include "../../../Windows/FileFind.h"
18 #include "../../../Windows/FileIO.h"
19 #include "../../../Windows/FileName.h"
20 #include "../../../Windows/NtCheck.h"
21 #include "../../../Windows/ResourceString.h"
22
23 #include "../../UI/Explorer/MyMessages.h"
24
25 #include "ExtractEngine.h"
26
27 #include "resource.h"
28
29 using namespace NWindows;
30 using namespace NFile;
31 using namespace NDir;
32
33 extern
34 HINSTANCE g_hInstance;
35 HINSTANCE g_hInstance;
36
37 static CFSTR const kTempDirPrefix = FTEXT("7zS");
38
39 #define MY_SHELL_EXECUTE
40
ReadDataString(CFSTR fileName, LPCSTR startID, LPCSTR endID, AString &stringResult)41 static bool ReadDataString(CFSTR fileName, LPCSTR startID,
42 LPCSTR endID, AString &stringResult)
43 {
44 stringResult.Empty();
45 NIO::CInFile inFile;
46 if (!inFile.Open(fileName))
47 return false;
48 const size_t kBufferSize = (1 << 12);
49
50 Byte buffer[kBufferSize];
51 const unsigned signatureStartSize = MyStringLen(startID);
52 const unsigned signatureEndSize = MyStringLen(endID);
53
54 size_t numBytesPrev = 0;
55 bool writeMode = false;
56 UInt64 posTotal = 0;
57 for (;;)
58 {
59 if (posTotal > (1 << 20))
60 return (stringResult.IsEmpty());
61 const size_t numReadBytes = kBufferSize - numBytesPrev;
62 size_t processedSize;
63 if (!inFile.ReadFull(buffer + numBytesPrev, numReadBytes, processedSize))
64 return false;
65 if (processedSize == 0)
66 return true;
67 const size_t numBytesInBuffer = numBytesPrev + processedSize;
68 UInt32 pos = 0;
69 for (;;)
70 {
71 if (writeMode)
72 {
73 if (pos + signatureEndSize > numBytesInBuffer)
74 break;
75 if (memcmp(buffer + pos, endID, signatureEndSize) == 0)
76 return true;
77 const Byte b = buffer[pos];
78 if (b == 0)
79 return false;
80 stringResult += (char)b;
81 pos++;
82 }
83 else
84 {
85 if (pos + signatureStartSize > numBytesInBuffer)
86 break;
87 if (memcmp(buffer + pos, startID, signatureStartSize) == 0)
88 {
89 writeMode = true;
90 pos += signatureStartSize;
91 }
92 else
93 pos++;
94 }
95 }
96 numBytesPrev = numBytesInBuffer - pos;
97 posTotal += pos;
98 memmove(buffer, buffer + pos, numBytesPrev);
99 }
100 }
101
102 static char kStartID[] = { ',','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 };
103 static char kEndID[] = { ',','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 };
104
105 static struct CInstallIDInit
106 {
CInstallIDInitCInstallIDInit107 CInstallIDInit()
108 {
109 kStartID[0] = ';';
110 kEndID[0] = ';';
111 }
112 } g_CInstallIDInit;
113
114
115 #if defined(_WIN32) && defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE)
116 #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1;
117 #endif
118
ShowErrorMessageSpec(const UString &name)119 static void ShowErrorMessageSpec(const UString &name)
120 {
121 UString message = NError::MyFormatMessage(::GetLastError());
122 const int pos = message.Find(L"%1");
123 if (pos >= 0)
124 {
125 message.Delete((unsigned)pos, 2);
126 message.Insert((unsigned)pos, name);
127 }
128 ShowErrorMessage(NULL, message);
129 }
130
WinMain(HINSTANCE hInstance, HINSTANCE , LPWSTR ,int )131 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
132 #ifdef UNDER_CE
133 LPWSTR
134 #else
135 LPSTR
136 #endif
137 /* lpCmdLine */,int /* nCmdShow */)
138 {
139 g_hInstance = (HINSTANCE)hInstance;
140
141 NT_CHECK
142
143 #ifdef _WIN32
144 LoadSecurityDlls();
145 #endif
146
147 // InitCommonControls();
148
149 UString archiveName, switches;
150 #ifdef MY_SHELL_EXECUTE
151 UString executeFile, executeParameters;
152 #endif
153 NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches);
154
155 FString fullPath;
156 NDLL::MyGetModuleFileName(fullPath);
157
158 switches.Trim();
159 bool assumeYes = false;
160 if (switches.IsPrefixedBy_Ascii_NoCase("-y"))
161 {
162 assumeYes = true;
163 switches = switches.Ptr(2);
164 switches.Trim();
165 }
166
167 AString config;
168 if (!ReadDataString(fullPath, kStartID, kEndID, config))
169 {
170 if (!assumeYes)
171 ShowErrorMessage(L"Can't load config info");
172 return 1;
173 }
174
175 UString dirPrefix ("." STRING_PATH_SEPARATOR);
176 UString appLaunched;
177 bool showProgress = true;
178 if (!config.IsEmpty())
179 {
180 CObjectVector<CTextConfigPair> pairs;
181 if (!GetTextConfig(config, pairs))
182 {
183 if (!assumeYes)
184 ShowErrorMessage(L"Config failed");
185 return 1;
186 }
187 const UString friendlyName = GetTextConfigValue(pairs, "Title");
188 const UString installPrompt = GetTextConfigValue(pairs, "BeginPrompt");
189 const UString progress = GetTextConfigValue(pairs, "Progress");
190 if (progress.IsEqualTo_Ascii_NoCase("no"))
191 showProgress = false;
192 const int index = FindTextConfigItem(pairs, "Directory");
193 if (index >= 0)
194 dirPrefix = pairs[index].String;
195 if (!installPrompt.IsEmpty() && !assumeYes)
196 {
197 if (MessageBoxW(NULL, installPrompt, friendlyName, MB_YESNO |
198 MB_ICONQUESTION) != IDYES)
199 return 0;
200 }
201 appLaunched = GetTextConfigValue(pairs, "RunProgram");
202
203 #ifdef MY_SHELL_EXECUTE
204 executeFile = GetTextConfigValue(pairs, "ExecuteFile");
205 executeParameters = GetTextConfigValue(pairs, "ExecuteParameters");
206 #endif
207 }
208
209 CTempDir tempDir;
210 if (!tempDir.Create(kTempDirPrefix))
211 {
212 if (!assumeYes)
213 ShowErrorMessage(L"Cannot create temp folder archive");
214 return 1;
215 }
216
217 CCodecs *codecs = new CCodecs;
218 CMyComPtr<IUnknown> compressCodecsInfo = codecs;
219 {
220 const HRESULT result = codecs->Load();
221 if (result != S_OK)
222 {
223 ShowErrorMessage(L"Cannot load codecs");
224 return 1;
225 }
226 }
227
228 const FString tempDirPath = tempDir.GetPath();
229 // tempDirPath = L"M:\\1\\"; // to test low disk space
230 {
231 bool isCorrupt = false;
232 UString errorMessage;
233 HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress,
234 isCorrupt, errorMessage);
235
236 if (result != S_OK)
237 {
238 if (!assumeYes)
239 {
240 if (result == S_FALSE || isCorrupt)
241 {
242 NWindows::MyLoadString(IDS_EXTRACTION_ERROR_MESSAGE, errorMessage);
243 result = E_FAIL;
244 }
245 if (result != E_ABORT)
246 {
247 if (errorMessage.IsEmpty())
248 errorMessage = NError::MyFormatMessage(result);
249 ::MessageBoxW(NULL, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR);
250 }
251 }
252 return 1;
253 }
254 }
255
256 #ifndef UNDER_CE
257 CCurrentDirRestorer currentDirRestorer;
258 if (!SetCurrentDir(tempDirPath))
259 return 1;
260 #endif
261
262 HANDLE hProcess = NULL;
263 #ifdef MY_SHELL_EXECUTE
264 if (!executeFile.IsEmpty())
265 {
266 CSysString filePath (GetSystemString(executeFile));
267 SHELLEXECUTEINFO execInfo;
268 execInfo.cbSize = sizeof(execInfo);
269 execInfo.fMask = SEE_MASK_NOCLOSEPROCESS
270 #ifndef UNDER_CE
271 | SEE_MASK_FLAG_DDEWAIT
272 #endif
273 ;
274 execInfo.hwnd = NULL;
275 execInfo.lpVerb = NULL;
276 execInfo.lpFile = filePath;
277
278 if (!switches.IsEmpty())
279 {
280 executeParameters.Add_Space_if_NotEmpty();
281 executeParameters += switches;
282 }
283
284 const CSysString parametersSys (GetSystemString(executeParameters));
285 if (parametersSys.IsEmpty())
286 execInfo.lpParameters = NULL;
287 else
288 execInfo.lpParameters = parametersSys;
289
290 execInfo.lpDirectory = NULL;
291 execInfo.nShow = SW_SHOWNORMAL;
292 execInfo.hProcess = NULL;
293 /* BOOL success = */ ::ShellExecuteEx(&execInfo);
294 UINT32 result = (UINT32)(UINT_PTR)execInfo.hInstApp;
295 if (result <= 32)
296 {
297 if (!assumeYes)
298 ShowErrorMessage(L"Cannot open file");
299 return 1;
300 }
301 hProcess = execInfo.hProcess;
302 }
303 else
304 #endif
305 {
306 if (appLaunched.IsEmpty())
307 {
308 appLaunched = L"setup.exe";
309 if (!NFind::DoesFileExist_FollowLink(us2fs(appLaunched)))
310 {
311 if (!assumeYes)
312 ShowErrorMessage(L"Cannot find setup.exe");
313 return 1;
314 }
315 }
316
317 {
318 FString s2 = tempDirPath;
319 NName::NormalizeDirPathPrefix(s2);
320 appLaunched.Replace(L"%%T" WSTRING_PATH_SEPARATOR, fs2us(s2));
321 }
322
323 const UString appNameForError = appLaunched; // actually we need to rtemove parameters also
324
325 appLaunched.Replace(L"%%T", fs2us(tempDirPath));
326
327 if (!switches.IsEmpty())
328 {
329 appLaunched.Add_Space();
330 appLaunched += switches;
331 }
332 STARTUPINFO startupInfo;
333 startupInfo.cb = sizeof(startupInfo);
334 startupInfo.lpReserved = NULL;
335 startupInfo.lpDesktop = NULL;
336 startupInfo.lpTitle = NULL;
337 startupInfo.dwFlags = 0;
338 startupInfo.cbReserved2 = 0;
339 startupInfo.lpReserved2 = NULL;
340
341 PROCESS_INFORMATION processInformation;
342
343 const CSysString appLaunchedSys (GetSystemString(dirPrefix + appLaunched));
344
345 const BOOL createResult = CreateProcess(NULL,
346 appLaunchedSys.Ptr_non_const(),
347 NULL, NULL, FALSE, 0, NULL, NULL /*tempDir.GetPath() */,
348 &startupInfo, &processInformation);
349 if (createResult == 0)
350 {
351 if (!assumeYes)
352 {
353 // we print name of exe file, if error message is
354 // ERROR_BAD_EXE_FORMAT: "%1 is not a valid Win32 application".
355 ShowErrorMessageSpec(appNameForError);
356 }
357 return 1;
358 }
359 ::CloseHandle(processInformation.hThread);
360 hProcess = processInformation.hProcess;
361 }
362 if (hProcess)
363 {
364 WaitForSingleObject(hProcess, INFINITE);
365 ::CloseHandle(hProcess);
366 }
367 return 0;
368 }
369