source: branches/eraser6/Installer/Bootstrapper/Main.cpp @ 792

Revision 792, 10.6 KB checked in by lowjoel, 6 years ago (diff)

Don't reset the progress bar style to remove the marquee if it wasn't set to begin with: this fixes the problem of the progress bar always displaying zero.

  • 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
25//Common Controls Version 6
26#pragma comment(linker, "\"/manifestdependency:type='Win32' " \
27    "name='Microsoft.Windows.Common-Controls' version='6.0.0.0' "\
28    "processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' "\
29    "language='*'\"")
30
31namespace {
32    //Constants
33    const wchar_t* STATIC_CLASS = L"STATIC";
34    const wchar_t* BUTTON_CLASS = L"BUTTON";
35    const wchar_t* szWindowClass = L"EraserBootstrapper";
36
37    //Static variables
38    HINSTANCE hInstance = NULL;
39    LRESULT __stdcall WndProc(HWND, UINT, WPARAM, LPARAM);
40
41    /// Creates a temporary directory with the given name. The directory and files in it
42    /// are deleted when this object is destroyed.
43    class TempDir
44    {
45    public:
46        /// Constructor.
47        ///
48        /// \param[in] dirName The path to the directory. This directory will be created.
49        TempDir(std::wstring dirName)
50            : DirName(dirName)
51        {
52            //Ensure there is a trailing slash
53            if (std::wstring(L"\\/").find(dirName[dirName.length() - 1]) == std::wstring::npos)
54                dirName += L"\\";
55
56            if (!CreateDirectoryW(dirName.c_str(), NULL))
57                switch (GetLastError())
58                {
59                case ERROR_ALREADY_EXISTS:
60                    DeleteContents();
61                    break;
62
63                default:
64                    throw GetErrorMessage(GetLastError());
65                }
66        }
67
68        ~TempDir()
69        {
70            DeleteContents();
71            RemoveDirectoryW(DirName.c_str());
72        }
73
74    private:
75        /// The path to the directory.
76        std::wstring DirName;
77
78    private:
79        void DeleteContents()
80        {
81            //Clean up the files in the directory.
82            WIN32_FIND_DATAW findData;
83            ZeroMemory(&findData, sizeof(findData));
84            HANDLE findHandle = FindFirstFileW((DirName + L"*").c_str(), &findData);
85            if (findHandle == INVALID_HANDLE_VALUE)
86                throw GetErrorMessage(GetLastError());
87
88            //Delete!
89            do
90                DeleteFileW((DirName + findData.cFileName).c_str());
91            while (FindNextFileW(findHandle, &findData));
92
93            //Clean up.
94            FindClose(findHandle);
95        }
96    };
97}
98
99int Install(bool quiet);
100int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/,
101                       LPTSTR lpCmdLine, int /*nCmdShow*/)
102{
103    try
104    {
105        //Parse the command line.
106        int argc = 0;
107        LPWSTR* argv = CommandLineToArgvW(lpCmdLine, & argc);
108        if (argv == NULL)
109            throw GetErrorMessage(GetLastError());
110
111        bool quiet = false;
112        for (int i = 0; i != argc; ++i)
113        {
114            std::wstring arg(argv[i]);
115            if (arg == L"--integrate")
116            {
117                //OK, integrate ourselves.
118                std::wstring destItem, package;
119                if (++i != argc)
120                    package = argv[i];
121
122                for (++i; i < argc; ++i)
123                {
124                    arg = argv[i];
125                    if (arg.substr(0, 9) == L"--out")
126                    {
127                        if (++i != argc)
128                            destItem = argv[i];
129                    }
130                    else if (arg.substr(0, 2) == L"-q" || arg.substr(0, 7) == L"--quiet")
131                    {
132                        quiet = true;
133                    }
134                }
135
136                if (!destItem.empty() && !package.empty())
137                    return Integrate(destItem, package);
138            }
139            else if (arg.substr(0, 2) == L"-q" || arg.substr(0, 7) == L"--quiet")
140            {
141                quiet = true;
142            }
143        }
144
145        //Create the parent window and the child controls
146        ::hInstance = hInstance;
147        return Install(quiet);
148
149    }
150    catch (const std::wstring& e)
151    {
152        MessageBoxW(Application::Get().GetTopWindow().GetHandle(), e.c_str(),
153            L"Eraser Setup", MB_OK | MB_ICONERROR);
154    }
155
156    return 0;
157}
158
159int Install(bool quiet)
160{
161    Application& app = Application::Get();
162    MainWindow& mainWin = app.GetTopWindow();
163    mainWin.Show(!quiet);
164
165    //OK, now we do the hard work. Create a folder to place our payload into
166    wchar_t tempPath[MAX_PATH];
167    DWORD result = GetTempPathW(sizeof(tempPath) / sizeof(tempPath[0]), tempPath);
168    if (!result)
169        throw GetErrorMessage(GetLastError());
170
171    std::wstring tempDir(tempPath, result);
172    if (std::wstring(L"\\/").find(tempDir[tempDir.length() - 1]) == std::wstring::npos)
173        tempDir += L"\\";
174    tempDir += L"eraserInstallBootstrapper\\";
175    TempDir dir(tempDir);
176    ExtractTempFiles(tempDir);
177    mainWin.EnableCancellation(false);
178
179    //Install the .NET framework
180    if (!HasNetFramework())
181        InstallNetFramework(tempDir, quiet);
182
183    //Then install Eraser!
184    mainWin.Show(false);
185    InstallEraser(tempDir, quiet);
186    return 0;
187}
188
189Application::Application()
190{
191    MainWin.Create();
192}
193
194Application& Application::Get()
195{
196    static Application Instance;
197    return Instance;
198}
199
200HINSTANCE Application::GetInstance()
201{
202    return ::hInstance;
203}
204
205MainWindow& Application::GetTopWindow()
206{
207    return MainWin;
208}
209
210void Application::Yield()
211{
212    MSG msg;
213    while (PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) && msg.message != WM_QUIT)
214    {
215        if (GetMessageW(&msg, NULL, 0, 0) == 0)
216            return;
217
218        TranslateMessage(&msg);
219        DispatchMessage(&msg);
220    }
221}
222
223std::wstring Application::GetPath()
224{
225    wchar_t filePath[MAX_PATH];
226    DWORD result = GetModuleFileNameW(hInstance, filePath,
227        sizeof(filePath) / sizeof(filePath[0]));
228
229    if (result == 0)
230        throw GetErrorMessage(GetLastError());
231    return std::wstring(filePath, result);
232}
233
234std::map<HWND, MainWindow::WndProcData> MainWindow::OldWndProcs;
235MainWindow::~MainWindow()
236{
237    DestroyWindow(hWndStatusLbl);
238    DestroyWindow(hWndProgressBar);
239    DestroyWindow(hWndCancelBtn);
240    DestroyWindow(hWndPanel);
241    DestroyWindow(hWnd);
242}
243
244bool MainWindow::Create()
245{
246    if (!InitInstance())
247        return false;
248
249    hWndPanel = CreateWindowExW(0, STATIC_CLASS, NULL, WS_CHILD | WS_VISIBLE,
250        0, 0, 294, 104, hWnd, NULL, hInstance, NULL);
251    hWndStatusLbl = CreateWindowExW(0, STATIC_CLASS, L"Extracting setup files...",
252        WS_CHILD | WS_VISIBLE, 13, 38, 270, 19, hWndPanel, NULL, hInstance, NULL);
253    hWndProgressBar = CreateWindowExW(0, PROGRESS_CLASS, NULL,
254        WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 13, 13, 270, 24, hWndPanel, NULL,
255        hInstance, NULL);
256    hWndCancelBtn = CreateWindowExW(0, BUTTON_CLASS, L"Cancel", WS_TABSTOP |
257        WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 193, 65, 90, 23, hWndPanel, NULL,
258        hInstance, NULL);
259    if (!hWndPanel || !hWndStatusLbl || !hWndProgressBar || !hWndCancelBtn)
260        return false;
261
262    SetWindowFont(hWndPanel);
263    SetWindowFont(hWndStatusLbl);
264    SetWindowFont(hWndProgressBar);
265    SetWindowFont(hWndCancelBtn);
266
267    SendMessage(hWndProgressBar, PBM_SETRANGE32, 0, 1000);
268    SubclassWindow(*this, hWndCancelBtn, WndProc);
269    SubclassWindow(*this, hWndPanel, WndProc);
270
271    UpdateWindow(hWnd);
272    return true;
273}
274
275bool MainWindow::InitInstance()
276{
277    WNDCLASSEX wcex;
278    ::ZeroMemory(&wcex, sizeof(wcex));
279
280    wcex.cbSize         = sizeof(WNDCLASSEX);
281    wcex.style          = CS_HREDRAW | CS_VREDRAW;
282    wcex.lpfnWndProc    = WndProc;
283    wcex.cbClsExtra     = 0;
284    wcex.cbWndExtra     = 0;
285    wcex.hInstance      = hInstance;
286    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(BOOTSTRAPPER_ICON));
287    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
288    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
289    wcex.lpszClassName  = szWindowClass;
290    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BOOTSTRAPPER_ICON));
291    RegisterClassExW(&wcex);
292    InitCommonControls();
293
294    //Create the window
295    hWnd = CreateWindowW(szWindowClass, L"Eraser Setup", WS_CAPTION,
296        CW_USEDEFAULT, 0, 300, 130, NULL, NULL, hInstance, NULL);
297
298    if (!hWnd)
299        return false;
300
301    //Set default settings (font)
302    SetWindowFont(hWnd);
303    return true;
304}
305
306void MainWindow::SetWindowFont(HWND hWnd)
307{
308    HFONT hWndFont = NULL;
309    if (!hWndFont)
310    {
311        NONCLIENTMETRICS ncm;
312        ::ZeroMemory(&ncm, sizeof(ncm));
313        ncm.cbSize = sizeof(ncm);
314
315        if ( !::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0) )
316        {
317#if WINVER >= 0x0600
318            // a new field has been added to NONCLIENTMETRICS under Vista, so
319            // the call to SystemParametersInfo() fails if we use the struct
320            // size incorporating this new value on an older system -- retry
321            // without it
322            ncm.cbSize -= sizeof(int);
323            if ( !::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0) )
324#endif
325                return;
326        }
327
328        hWndFont = CreateFontIndirectW(&ncm.lfMessageFont);
329    }
330
331    SendMessage(hWnd, WM_SETFONT, (WPARAM)hWndFont, MAKELPARAM(TRUE, 0));
332}
333
334void MainWindow::Show(bool show)
335{
336    ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
337}
338
339void MainWindow::EnableCancellation(bool enable)
340{
341    EnableWindow(hWndCancelBtn, enable);
342}
343
344void MainWindow::SetProgress(float progress)
345{
346    LONG_PTR pbStyle = GetWindowLongPtr(hWndProgressBar, GWL_STYLE);
347    if (pbStyle & PBS_MARQUEE)
348        SetWindowLongPtr(hWndProgressBar, GWL_STYLE, pbStyle & (~PBS_MARQUEE));
349    SendMessage(hWndProgressBar, PBM_SETPOS, (int)(progress * 1000), 0);
350}
351
352void MainWindow::SetProgressIndeterminate()
353{
354    SetWindowLongPtr(hWndProgressBar, GWL_STYLE,
355        GetWindowLongPtr(hWndProgressBar, GWL_STYLE) | PBS_MARQUEE);
356    SendMessage(hWndProgressBar, PBM_SETMARQUEE, true, 100);
357}
358
359void MainWindow::SetMessage(std::wstring message)
360{
361    SetWindowTextW(hWndStatusLbl, message.c_str());
362}
363
364LRESULT MainWindow::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM /*lParam*/,
365    bool& handled)
366{
367    handled = false;
368    switch (message)
369    {
370    case WM_COMMAND:
371        if (hWnd == hWndCancelBtn && HIWORD(wParam) == BN_CLICKED)
372            PostQuitMessage(1);
373        break;
374    }
375
376    return 0;
377}
378
379void MainWindow::SubclassWindow(MainWindow& owner, HWND hWnd, WNDPROC wndProc)
380{
381    OldWndProcs.insert(std::make_pair(
382        hWnd, WndProcData(reinterpret_cast<WNDPROC>(SetWindowLongPtr(hWnd,
383            GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(wndProc))), owner)
384    ));
385}
386
387LRESULT __stdcall MainWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
388{
389    std::map<HWND, WndProcData>::const_iterator iter = OldWndProcs.find(hWnd);
390    if (iter != OldWndProcs.end())
391    {
392        bool handled = false;
393        LRESULT result = iter->second.Owner->WindowProc(hWnd, message, wParam,
394            lParam, handled);
395        if (handled)
396            return result;
397
398        return CallWindowProc(iter->second.OldWndProc, hWnd, message, wParam, lParam);
399    }
400
401    switch (message)
402    {
403    case WM_DESTROY:
404        PostQuitMessage(0);
405        break;
406
407    default:
408        return DefWindowProc(hWnd, message, wParam, lParam);
409    }
410
411    return 0;
412}
413
414std::wstring GetErrorMessage(DWORD lastError)
415{
416    unsigned lastBufferSize = 128;
417    wchar_t* buffer = NULL;
418    DWORD result = 0;
419
420    while (result == 0 || result == lastBufferSize - 1)
421    {
422        delete[] buffer;
423        buffer = new wchar_t[lastBufferSize *= 2];
424        result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, lastError, 0, buffer,
425            lastBufferSize, NULL);
426    }
427
428    std::wstring message(buffer);
429    delete[] buffer;
430    return message;
431}
Note: See TracBrowser for help on using the repository browser.