source: trunk/eraser6/Installer/Bootstrapper/Bootstrapper.cpp @ 1360

Revision 1360, 12.5 KB checked in by lowjoel, 5 years ago (diff)

Eraser's still under development, so update the copyright notice.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22#include "stdafx.h"
23#include "Bootstrapper.h"
24
25class Handle
26{
27public:
28    Handle(HANDLE handle)
29    {
30        thisHandle = handle;
31    }
32
33    ~Handle()
34    {
35        CloseHandle(thisHandle);
36    }
37
38    operator HANDLE()
39    {
40        return thisHandle;
41    }
42
43private:
44    HANDLE thisHandle;
45};
46
47const wchar_t *ResourceName = MAKEINTRESOURCE(101);
48
49int Integrate(const std::wstring& destItem, const std::wstring& package)
50{
51    //Open a handle to ourselves
52    DWORD lastOperation = 0;
53    {
54        Handle srcFile(CreateFileW(Application::Get().GetPath().c_str(), GENERIC_READ,
55            FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
56        if (srcFile == INVALID_HANDLE_VALUE)
57            throw GetErrorMessage(GetLastError());
58
59        //Copy ourselves
60        Handle destFile(CreateFileW(destItem.c_str(), GENERIC_WRITE, 0, NULL,
61            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
62        if (destFile == INVALID_HANDLE_VALUE)
63            throw GetErrorMessage(GetLastError());
64
65        char buffer[262144];
66        while (ReadFile(srcFile, buffer, sizeof(buffer), &lastOperation, NULL) && lastOperation)
67            WriteFile(destFile, buffer, lastOperation, &lastOperation, NULL);
68    }
69
70    //Start updating the resource in the destination item
71    HANDLE resHandle(BeginUpdateResource(destItem.c_str(), false));
72    if (resHandle == NULL)
73        throw GetErrorMessage(GetLastError());
74
75    //Read the package into memory
76    Handle packageFile(CreateFileW(package.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
77        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
78    if (packageFile == INVALID_HANDLE_VALUE)
79        throw GetErrorMessage(GetLastError());
80
81    unsigned long packageSize = GetFileSize(packageFile, NULL);
82    char* inputData = new char[packageSize];
83    if (!ReadFile(packageFile, inputData, packageSize, &lastOperation, NULL) || lastOperation != packageSize)
84        throw GetErrorMessage(GetLastError());
85
86    //Add the package to the application resource section
87    if (!UpdateResource(resHandle, RT_RCDATA, ResourceName, MAKELANGID(LANG_NEUTRAL,
88        SUBLANG_DEFAULT), inputData, packageSize))
89    {
90        throw GetErrorMessage(GetLastError());
91    }
92
93    //Complete the update
94    if (!EndUpdateResource(resHandle, false)) 
95        throw GetErrorMessage(GetLastError());
96    return 0;
97}
98
99/// ISzInStream interface for extracting the archives.
100struct LZMemStream
101{
102public:
103    /// Constructor.
104    ///
105    /// \param[in] buffer The buffer containing the data to present as a stream.
106    /// \param[in] bufferSize The size of the buffer passed in.
107    /// \param[in] deleteOnDestroy True if the the buffer should be freed (using delete[])
108    ///                            after the stream is destroyed.
109    LZMemStream(void* buffer, size_t bufferSize, bool deleteOnDestroy)
110    {
111        InStream.Look = LZMemStreamLook;
112        InStream.Skip = LzMemStreamSkip;
113        InStream.Read = LZMemStreamRead;
114        InStream.Seek = LzMemStreamSeek;
115
116        Buffer = static_cast<char*>(buffer);
117        BufferRead = 0;
118        BufferSize = bufferSize;
119
120        DeleteOnDestroy = deleteOnDestroy;
121        CurrentOffset = 0;
122    }
123
124    ~LZMemStream()
125    {
126        if (DeleteOnDestroy)
127            delete[] Buffer;
128    }
129
130    ILookInStream InStream;
131
132private:
133    bool DeleteOnDestroy;
134    char* Buffer;
135    size_t BufferRead;
136    size_t BufferSize;
137    size_t CurrentOffset;
138
139    static SRes LZMemStreamLook(void* object, void** buf, size_t* size)
140    {
141        if (*size == 0)
142            return SZ_OK;
143
144        LZMemStream* s = static_cast<LZMemStream*>(object);
145
146        //Copy the memory to the provided buffer.
147        *size = std::min(std::min(*size, s->BufferSize - s->CurrentOffset),
148            static_cast<size_t>(32768));
149        char* dstBuffer = reinterpret_cast<char*>(SzAlloc(object, *size));
150        memcpy(dstBuffer, s->Buffer + s->CurrentOffset, *size);
151
152        *buf = dstBuffer;
153        s->BufferRead += *size;
154
155        MainWindow& mainWin = Application::Get().GetTopWindow();
156        mainWin.SetProgress((float)((double)s->BufferRead / s->BufferSize));
157        return SZ_OK;
158    }
159
160    static SRes LZMemStreamRead(void* object, void* buf, size_t* size)
161    {
162        LZMemStream* s = static_cast<LZMemStream*>(object);
163
164        //Copy the memory to the provided buffer.
165        *size = std::min(std::min(*size, s->BufferSize - s->CurrentOffset),
166            static_cast<size_t>(32768));
167        memcpy(buf, s->Buffer + s->CurrentOffset, *size);
168
169        s->CurrentOffset += *size;
170        s->BufferRead += *size;
171
172        MainWindow& mainWin = Application::Get().GetTopWindow();
173        mainWin.SetProgress((float)((double)s->BufferRead / s->BufferSize));
174        return SZ_OK;
175    }
176
177    static SRes LzMemStreamSkip(void* object, size_t offset)
178    {
179        LZMemStream* s = static_cast<LZMemStream*>(object);
180
181        if (offset + s->CurrentOffset > s->BufferSize)
182            return SZ_ERROR_INPUT_EOF;
183        s->CurrentOffset += offset;
184        return SZ_OK;
185    }
186
187    static SRes LzMemStreamSeek(void* object, long long* position, ESzSeek origin)
188    {
189        LZMemStream* s = static_cast<LZMemStream*>(object);
190        long long newPos = *position;
191        switch (origin)
192        {
193        case SZ_SEEK_CUR:
194            newPos += s->CurrentOffset;
195            break;
196        case SZ_SEEK_END:
197            newPos = s->BufferSize - *position;
198            break;
199        }
200
201        if (newPos > s->BufferSize || newPos < 0)
202            return SZ_ERROR_INPUT_EOF;
203        s->CurrentOffset = static_cast<size_t>(newPos);
204        *position = newPos;
205        return SZ_OK;
206    }
207};
208
209void ExtractTempFiles(std::wstring pathToExtract)
210{
211    if (std::wstring(L"\\/").find(pathToExtract[pathToExtract.length() - 1]) == std::wstring::npos)
212        pathToExtract += L"\\";
213
214    //Open the file
215    HMODULE currProcess = static_cast<HMODULE>(Application::Get().GetInstance());
216    HANDLE hResource(FindResource(currProcess, ResourceName, RT_RCDATA));
217    if (!hResource)
218        throw GetErrorMessage(GetLastError());
219
220    HANDLE hResLoad(LoadResource(currProcess, static_cast<HRSRC>(static_cast<HANDLE>(hResource))));
221    if (!hResLoad)
222        throw GetErrorMessage(GetLastError());
223
224    //Lock the data into global memory.
225    unsigned long resourceSize = SizeofResource(currProcess, static_cast<HRSRC>(
226        static_cast<HANDLE>(hResource)));
227    void* resourceBuffer = LockResource(hResLoad);
228    if (!resourceBuffer)
229        throw GetErrorMessage(GetLastError());
230
231    //7z archive database structure
232    CSzArEx db;
233
234    //memory functions
235    ISzAlloc allocImp;
236    ISzAlloc allocTempImp;
237    allocTempImp.Alloc = allocImp.Alloc = SzAlloc;
238    allocTempImp.Free = allocImp.Free = SzFree;
239
240    //Initialize the CRC and database structures
241    LZMemStream stream(resourceBuffer, resourceSize, false);
242    CrcGenerateTable();
243    SzArEx_Init(&db);
244    if (SzArEx_Open(&db, &stream.InStream, &allocImp, &allocTempImp) != SZ_OK)
245        throw std::wstring(L"Could not open archive for reading.");
246
247    //Read the database for files
248    unsigned blockIndex = 0;
249    Byte* outBuffer = NULL;
250    size_t outBufferSize = 0;
251    for (unsigned i = 0; i < db.db.NumFiles; ++i)
252    {
253        size_t offset = 0;
254        size_t processedSize = 0;
255        CSzFileItem* file = db.db.Files + i;
256        SRes result = SZ_OK;
257
258        //Create the output file
259        size_t convertedChars = 0;
260        wchar_t fileName[MAX_PATH];
261        mbstowcs_s(&convertedChars, fileName, file->Name, sizeof(fileName) / sizeof(fileName[0]));
262       
263        //Split the path to get the file name only.
264        wchar_t baseFileName[MAX_PATH];
265        wchar_t fileExt[MAX_PATH];
266        _wsplitpath_s(fileName, NULL, NULL, NULL, NULL, baseFileName,
267            sizeof(baseFileName) / sizeof(baseFileName[0]), fileExt,
268            sizeof(fileExt) / sizeof(fileExt[0]));
269        wcscpy_s(fileName, baseFileName);
270        wcscpy_s(fileName + wcslen(baseFileName),
271            sizeof(fileName) / sizeof(fileName[0]) - wcslen(baseFileName), fileExt);
272
273        Handle destFile(CreateFileW((pathToExtract + fileName).c_str(), GENERIC_WRITE, 0,
274            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
275        if (destFile == INVALID_HANDLE_VALUE)
276            throw GetErrorMessage(GetLastError());
277        unsigned long long destFileSize = file->Size;
278
279        //Extract the file
280        while (result == SZ_OK && destFileSize)
281        {
282            result = SzAr_Extract(&db, &stream.InStream, i, &blockIndex,
283                &outBuffer, &outBufferSize, &offset, &processedSize, &allocImp,
284                &allocTempImp);
285            if (result != SZ_OK)
286                throw std::wstring(L"Could not decompress data as it is corrupt.");
287
288            DWORD bytesWritten = 0;
289            if (!WriteFile(destFile, outBuffer + offset, processedSize, &bytesWritten, NULL) ||
290                bytesWritten != processedSize)
291                throw GetErrorMessage(GetLastError());
292            destFileSize -= bytesWritten;
293            Application::Get().Yield();
294        }
295    }
296
297    SzArEx_Free(&db, &allocImp);
298}
299
300bool HasNetFramework()
301{
302    HKEY key;
303    std::wstring highestVer;
304    std::wstring keys[] = { L"v3.5" };
305
306    for (int i = 0, j = sizeof(keys) / sizeof(keys[0]); i != j; ++i)
307    {
308        //Open the key for reading
309        DWORD result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
310            (L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\" + keys[i]).c_str(),
311            0, KEY_READ, &key);
312
313        //Retry for 64-bit WoW
314        if (result == ERROR_FILE_NOT_FOUND)
315        {
316            result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
317                (L"SOFTWARE\\Wow6432Node\\Microsoft\\NET Framework Setup\\NDP\\" + keys[i]).c_str(),
318                0, KEY_READ, &key);
319
320            if (result == ERROR_FILE_NOT_FOUND)
321                continue;
322        }
323
324        if (result != ERROR_SUCCESS)
325            throw GetErrorMessage(result);
326
327        //Query the Installed string
328        DWORD installedVal = 0;
329        DWORD bufferSize = sizeof(installedVal);
330        result = RegQueryValueExW(key, L"Install", NULL, NULL, (BYTE*)&installedVal,
331            &bufferSize);
332        if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA)
333            throw GetErrorMessage(result);
334        if (installedVal == 1)
335            highestVer = keys[i].substr(1);
336        RegCloseKey(key);
337    }
338
339    return !highestVer.empty();
340}
341
342int CreateProcessAndWait(const std::wstring& commandLine, const std::wstring& appName)
343{
344    //Get a mutable version of the command line
345    wchar_t* cmdLine = new wchar_t[commandLine.length() + 1];
346    wcscpy_s(cmdLine, commandLine.length() + 1, commandLine.c_str());
347
348    //Launch the process
349    STARTUPINFOW startupInfo;
350    PROCESS_INFORMATION pInfo;
351    ::ZeroMemory(&startupInfo, sizeof(startupInfo));
352    ::ZeroMemory(&pInfo, sizeof(pInfo));
353    if (!CreateProcessW(NULL, cmdLine, NULL, NULL, false, 0, NULL,  NULL, &startupInfo,
354        &pInfo))
355    {
356        delete[] cmdLine;
357        throw L"Error while executing " + appName + L": " + GetErrorMessage(GetLastError());
358    }
359    delete[] cmdLine;
360
361    //Ok the process was created, wait for it to terminate.
362    DWORD lastWait = 0;
363    while ((lastWait = WaitForSingleObject(pInfo.hProcess, 50)) == WAIT_TIMEOUT)
364        Application::Get().Yield();
365    if (lastWait == WAIT_ABANDONED)
366        throw std::wstring(L"The condition waiting on the termination of the .NET installer was abandoned.");
367
368    //Get the exit code
369    DWORD exitCode = 0;
370    if (!GetExitCodeProcess(pInfo.hProcess, &exitCode))
371        throw GetErrorMessage(GetLastError());
372
373    //Clean up
374    CloseHandle(pInfo.hProcess);
375    CloseHandle(pInfo.hThread);
376
377    //Return the exit code.
378    return exitCode;
379}
380
381bool InstallNetFramework(std::wstring tempDir, bool quiet)
382{
383    //Update the UI
384    MainWindow& mainWin = Application::Get().GetTopWindow();
385    mainWin.SetProgressIndeterminate();
386    mainWin.SetMessage(L"Installing .NET Framework...");
387
388    //Get the path to the installer
389    if (std::wstring(L"\\/").find(tempDir[tempDir.length() - 1]) == std::wstring::npos)
390        tempDir += L"\\";
391    std::wstring commandLine(L'"' + tempDir);
392    commandLine += L"dotnetfx35.exe\" /norestart";
393
394    //If the user wants it quiet then pass the /q:a switch
395    if (quiet)
396        commandLine += L" /q";
397
398    //And the return code is true if the process exited with 0.
399    return CreateProcessAndWait(commandLine, L".NET Framework Installer") == 0;
400}
401
402bool InstallEraser(std::wstring tempDir, bool quiet)
403{
404    MainWindow& mainWin = Application::Get().GetTopWindow();
405    mainWin.SetProgressIndeterminate();
406    mainWin.SetMessage(L"Installing Eraser...");
407
408    //Determine the system architecture.
409    SYSTEM_INFO sysInfo;
410    ZeroMemory(&sysInfo, sizeof(sysInfo));
411    GetNativeSystemInfo(&sysInfo);
412
413    if (std::wstring(L"\\/").find(tempDir[tempDir.length() - 1]) == std::wstring::npos)
414        tempDir += L"\\";
415    switch (sysInfo.wProcessorArchitecture)
416    {
417    case PROCESSOR_ARCHITECTURE_AMD64:
418        tempDir += L"Eraser (x64).msi";
419        break;
420
421    default:
422        tempDir += L"Eraser (x86).msi";
423        break;
424    }
425
426    std::wstring commandLine(L"msiexec.exe /i ");
427    commandLine += L'"' + tempDir + L'"';
428
429    //Add the quiet command line parameter if a quiet command line parameter was
430    //specified
431    if (quiet)
432        commandLine += L" /quiet /norestart";
433   
434    //And the return code is true if the process exited with 0.
435    return CreateProcessAndWait(commandLine, L"Eraser") == 0;
436}
Note: See TracBrowser for help on using the repository browser.