| 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 | |
|---|
| 31 | namespace { |
|---|
| 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 | |
|---|
| 99 | int Install(bool quiet); |
|---|
| 100 | int 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 | |
|---|
| 159 | int 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 | |
|---|
| 189 | Application::Application() |
|---|
| 190 | { |
|---|
| 191 | MainWin.Create(); |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | Application& Application::Get() |
|---|
| 195 | { |
|---|
| 196 | static Application Instance; |
|---|
| 197 | return Instance; |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | HINSTANCE Application::GetInstance() |
|---|
| 201 | { |
|---|
| 202 | return ::hInstance; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | MainWindow& Application::GetTopWindow() |
|---|
| 206 | { |
|---|
| 207 | return MainWin; |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | void 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 | |
|---|
| 223 | std::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 | |
|---|
| 234 | std::map<HWND, MainWindow::WndProcData> MainWindow::OldWndProcs; |
|---|
| 235 | MainWindow::~MainWindow() |
|---|
| 236 | { |
|---|
| 237 | DestroyWindow(hWndStatusLbl); |
|---|
| 238 | DestroyWindow(hWndProgressBar); |
|---|
| 239 | DestroyWindow(hWndCancelBtn); |
|---|
| 240 | DestroyWindow(hWndPanel); |
|---|
| 241 | DestroyWindow(hWnd); |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | bool 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 | |
|---|
| 275 | bool 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 | |
|---|
| 306 | void 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 | |
|---|
| 334 | void MainWindow::Show(bool show) |
|---|
| 335 | { |
|---|
| 336 | ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); |
|---|
| 337 | } |
|---|
| 338 | |
|---|
| 339 | void MainWindow::EnableCancellation(bool enable) |
|---|
| 340 | { |
|---|
| 341 | EnableWindow(hWndCancelBtn, enable); |
|---|
| 342 | } |
|---|
| 343 | |
|---|
| 344 | void 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 | |
|---|
| 352 | void MainWindow::SetProgressIndeterminate() |
|---|
| 353 | { |
|---|
| 354 | SetWindowLongPtr(hWndProgressBar, GWL_STYLE, |
|---|
| 355 | GetWindowLongPtr(hWndProgressBar, GWL_STYLE) | PBS_MARQUEE); |
|---|
| 356 | SendMessage(hWndProgressBar, PBM_SETMARQUEE, true, 100); |
|---|
| 357 | } |
|---|
| 358 | |
|---|
| 359 | void MainWindow::SetMessage(std::wstring message) |
|---|
| 360 | { |
|---|
| 361 | SetWindowTextW(hWndStatusLbl, message.c_str()); |
|---|
| 362 | } |
|---|
| 363 | |
|---|
| 364 | LRESULT 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 | |
|---|
| 379 | void 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 | |
|---|
| 387 | LRESULT __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 | |
|---|
| 414 | std::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 | } |
|---|