source: branches/eraser6/6.0/Installer/Bootstrapper/Bootstrapper.cpp @ 1677

Revision 1677, 13.4 KB checked in by lowjoel, 5 years ago (diff)

Updated copyright information: since Eraser is still under development we should update our copyright status.

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