source: branches/eraser6/Installer/Bootstrapper/Bootstrapper.cpp @ 675

Revision 675, 10.8 KB checked in by lowjoel, 6 years ago (diff)

Warning fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008 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.Read = LZMemStreamRead;
112        InStream.Seek = LzMemStreamSeek;
113
114        Buffer = static_cast<char*>(buffer);
115        BufferRead = 0;
116        BufferSize = bufferSize;
117
118        DeleteOnDestroy = deleteOnDestroy;
119        CurrentOffset = 0;
120    }
121
122    ~LZMemStream()
123    {
124        if (DeleteOnDestroy)
125            delete[] Buffer;
126    }
127
128    ISzInStream InStream;
129
130private:
131    bool DeleteOnDestroy;
132    char* Buffer;
133    size_t BufferRead;
134    size_t BufferSize;
135    size_t CurrentOffset;
136
137    static SZ_RESULT LZMemStreamRead(void* object, void** bufferPtr, size_t size,
138        size_t* processedSize)
139    {
140        LZMemStream* s = static_cast<LZMemStream*>(object);
141
142        //Since we can allocate as much as we want to allocate, take a decent amount
143        //of memory and stop.
144        size = std::min(1048576u * 4, size);
145        static char* dstBuffer = NULL;
146        if (dstBuffer)
147            delete[] dstBuffer;
148        dstBuffer = new char[size];
149
150        //Copy the memory to the provided buffer.
151        memcpy(dstBuffer, s->Buffer + s->CurrentOffset, size);
152        *bufferPtr = dstBuffer;
153        *processedSize = size;
154        s->BufferRead += size;
155        s->CurrentOffset += size;
156
157        MainWindow& mainWin = Application::Get().GetTopWindow();
158        mainWin.SetProgress((float)((double)s->BufferRead / s->BufferSize));
159        return SZ_OK;
160    }
161
162    static SZ_RESULT LzMemStreamSeek(void *object, CFileSize pos)
163    {
164        LZMemStream* s = static_cast<LZMemStream*>(object);
165
166        if (pos > s->BufferSize)
167            return SZE_FAIL;
168        s->CurrentOffset = static_cast<size_t>(pos);
169        return SZ_OK;
170    }
171};
172
173void ExtractTempFiles(std::wstring pathToExtract)
174{
175    if (std::wstring(L"\\/").find(pathToExtract[pathToExtract.length() - 1]) == std::wstring::npos)
176        pathToExtract += L"\\";
177
178    //Open the file
179    HMODULE currProcess = static_cast<HMODULE>(Application::Get().GetInstance());
180    HANDLE hResource(FindResource(currProcess, ResourceName, RT_RCDATA));
181    if (!hResource)
182        throw GetErrorMessage(GetLastError());
183
184    HANDLE hResLoad(LoadResource(currProcess, static_cast<HRSRC>(static_cast<HANDLE>(hResource))));
185    if (!hResLoad)
186        throw GetErrorMessage(GetLastError());
187
188    //Lock the data into global memory.
189    unsigned long resourceSize = SizeofResource(currProcess, static_cast<HRSRC>(
190        static_cast<HANDLE>(hResource)));
191    void* resourceBuffer = LockResource(hResLoad);
192    if (!resourceBuffer)
193        throw GetErrorMessage(GetLastError());
194
195    //7z archive database structure
196    CArchiveDatabaseEx db;
197
198    //memory functions
199    ISzAlloc allocImp;
200    ISzAlloc allocTempImp;
201    allocTempImp.Alloc = allocImp.Alloc = SzAlloc;
202    allocTempImp.Free = allocImp.Free = SzFree;
203
204    //Initialize the CRC and database structures
205    LZMemStream stream(resourceBuffer, resourceSize, false);
206    CrcGenerateTable();
207    SzArDbExInit(&db);
208    if (SzArchiveOpen(&stream.InStream, &db, &allocImp, &allocTempImp) != SZ_OK)
209        throw std::wstring(L"Could not open archive for reading.");
210
211    //Read the database for files
212    unsigned blockIndex = 0;
213    Byte* outBuffer = NULL;
214    size_t outBufferSize = 0;
215    for (unsigned i = 0; i < db.Database.NumFiles; ++i)
216    {
217        size_t offset = 0;
218        size_t processedSize = 0;
219        CFileItem* file = db.Database.Files + i;
220        SZ_RESULT result = SZ_OK;
221
222        //Create the output file
223        size_t convertedChars = 0;
224        wchar_t fileName[MAX_PATH];
225        mbstowcs_s(&convertedChars, fileName, file->Name, sizeof(fileName) / sizeof(fileName[0]));
226        Handle destFile(CreateFileW((pathToExtract + fileName).c_str(), GENERIC_WRITE, 0,
227            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
228        if (destFile == INVALID_HANDLE_VALUE)
229            throw GetErrorMessage(GetLastError());
230        unsigned long long destFileSize = file->Size;
231
232        //Extract the file
233        while (result == SZ_OK && destFileSize)
234        {
235            result = SzExtract(&stream.InStream, &db, i, &blockIndex,
236                &outBuffer, &outBufferSize, &offset, &processedSize, &allocImp,
237                &allocTempImp);
238            if (result != SZ_OK)
239                throw std::wstring(L"Could not decompress data as it is corrupt.");
240
241            DWORD bytesWritten = 0;
242            if (!WriteFile(destFile, outBuffer + offset, processedSize, &bytesWritten, NULL) ||
243                bytesWritten != processedSize)
244                throw GetErrorMessage(GetLastError());
245            destFileSize -= bytesWritten;
246            Application::Get().Yield();
247        }
248    }
249
250    allocImp.Free(outBuffer);
251}
252
253bool HasNetFramework()
254{
255    HKEY key;
256    std::wstring highestVer;
257    std::wstring keys[] = { L"v3.5" };
258
259    for (int i = 0, j = sizeof(keys) / sizeof(keys[0]); i != j; ++i)
260    {
261        //Open the key for reading
262        DWORD result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
263            (L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\" + keys[i]).c_str(),
264            0, KEY_READ, &key);
265
266        //Retry for 64-bit WoW
267        if (result == ERROR_FILE_NOT_FOUND)
268        {
269            result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
270                (L"SOFTWARE\\Wow6432Node\\Microsoft\\NET Framework Setup\\NDP\\" + keys[i]).c_str(),
271                0, KEY_READ, &key);
272
273            if (result == ERROR_FILE_NOT_FOUND)
274                continue;
275        }
276
277        if (result != ERROR_SUCCESS)
278            throw GetErrorMessage(result);
279
280        //Query the Installed string
281        DWORD installedVal = 0;
282        DWORD bufferSize = sizeof(installedVal);
283        result = RegQueryValueExW(key, L"Install", NULL, NULL, (BYTE*)&installedVal,
284            &bufferSize);
285        if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA)
286            throw GetErrorMessage(result);
287        if (installedVal == 1)
288            highestVer = keys[i].substr(1);
289        RegCloseKey(key);
290    }
291
292    return !highestVer.empty();
293}
294
295int CreateProcessAndWait(const std::wstring& commandLine)
296{
297    //Get a mutable version of the command line
298    wchar_t* cmdLine = new wchar_t[commandLine.length() + 1];
299    wcscpy_s(cmdLine, commandLine.length() + 1, commandLine.c_str());
300
301    //Launch the process
302    STARTUPINFOW startupInfo;
303    PROCESS_INFORMATION pInfo;
304    ::ZeroMemory(&startupInfo, sizeof(startupInfo));
305    ::ZeroMemory(&pInfo, sizeof(pInfo));
306    if (!CreateProcessW(NULL, cmdLine, NULL, NULL, false, 0, NULL,  NULL, &startupInfo,
307        &pInfo))
308    {
309        delete[] cmdLine;
310        throw GetErrorMessage(GetLastError());
311    }
312    delete[] cmdLine;
313
314    //Ok the process was created, wait for it to terminate.
315    DWORD lastWait = 0;
316    while ((lastWait = WaitForSingleObject(pInfo.hProcess, 50)) == WAIT_TIMEOUT)
317        Application::Get().Yield();
318    if (lastWait == WAIT_ABANDONED)
319        throw std::wstring(L"The condition waiting on the termination of the .NET installer was abandoned.");
320
321    //Get the exit code
322    DWORD exitCode = 0;
323    if (!GetExitCodeProcess(pInfo.hProcess, &exitCode))
324        throw GetErrorMessage(GetLastError());
325
326    //Clean up
327    CloseHandle(pInfo.hProcess);
328    CloseHandle(pInfo.hThread);
329
330    //Return the exit code.
331    return exitCode;
332}
333
334bool InstallNetFramework(std::wstring tempDir)
335{
336    //Update the UI
337    MainWindow& mainWin = Application::Get().GetTopWindow();
338    mainWin.SetProgressIndeterminate();
339    mainWin.SetMessage(L"Installing .NET Framework...");
340
341    //Get the path to the installer
342    if (std::wstring(L"\\/").find(tempDir[tempDir.length() - 1]) == std::wstring::npos)
343        tempDir += L"\\";
344    std::wstring commandLine(L'"' + tempDir);
345    commandLine += L"dotnetfx35.exe\"";
346
347    //And the return code is true if the process exited with 0.
348    return CreateProcessAndWait(commandLine) == 0;
349}
350
351bool InstallEraser(std::wstring tempDir)
352{
353    MainWindow& mainWin = Application::Get().GetTopWindow();
354    mainWin.SetProgressIndeterminate();
355    mainWin.SetMessage(L"Installing Eraser...");
356
357    //Determine the system architecture.
358    SYSTEM_INFO sysInfo;
359    ZeroMemory(&sysInfo, sizeof(sysInfo));
360    GetSystemInfo(&sysInfo);
361
362    if (std::wstring(L"\\/").find(tempDir[tempDir.length() - 1]) == std::wstring::npos)
363        tempDir += L"\\";
364    switch (sysInfo.wProcessorArchitecture)
365    {
366    case PROCESSOR_ARCHITECTURE_AMD64:
367        tempDir += L"Eraser (x64).msi";
368        break;
369
370    default:
371        tempDir += L"Eraser (x86).msi";
372        break;
373    }
374
375    std::wstring commandLine(L"msiexec.exe /i ");
376    commandLine += L'"' + tempDir + L'"';
377   
378    //And the return code is true if the process exited with 0.
379    return CreateProcessAndWait(commandLine) == 0;
380}
Note: See TracBrowser for help on using the repository browser.