1
/* -*- Mode: C++; c-basic-offset: 2; tab-width: 8; indent-tabs-mode: nil; -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Fennec Installer for WinCE.
17
* The Initial Developer of the Original Code is The Mozilla Foundation.
19
* Portions created by the Initial Developer are Copyright (C) 2009
20
* the Initial Developer. All Rights Reserved.
23
* Alex Pakhotin <alexp@mozilla.com> (original author)
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
41
* Uninstall.cpp : Mozilla Fennec Uninstaller
47
#include <cfgmgrapi.h>
49
#include "nsSetupStrings.h"
50
#include "Uninstall.rh"
52
#define WM_DIALOGCREATED (WM_USER + 1)
54
const WCHAR c_sStringsFile[] = L"setup.ini";
55
nsSetupStrings Strings;
57
BOOL g_bRunFromSetupDll = FALSE;
59
WCHAR g_sInstallPath[MAX_PATH];
60
WCHAR g_sUninstallPath[MAX_PATH];
63
const WCHAR c_sAppRegKeyTemplate[] = L"Software\\Mozilla\\%s";
65
const DWORD c_nTempBufSize = MAX_PATH * 2;
66
const WCHAR c_sRemoveParam[] = L"[remove]";
67
const WCHAR c_sSetupParam[] = L"[setup]"; // means executed by Setup.dll
69
// Retry counter for the file/directory deletion
70
int nTotalRetries = 0;
71
const int c_nMaxTotalRetries = 10;
72
const int c_nMaxOneFileRetries = 6;
73
const int c_nRetryDelay = 500; //milliseconds
75
int g_nDirsDeleted = 0;
76
const int c_nMaxDirs = 25; // approximate number of dirs just to show some progress
82
ErrInstallationNotFound = -2,
83
ErrShutdownFailed = -3,
86
int g_nResult = ErrOK;
88
// Forward declarations
89
BOOL GetModulePath(WCHAR *sPath);
91
BOOL GetInstallPath(WCHAR *sPath);
92
BOOL DeleteShortcut(HWND hwndParent);
93
BOOL DeleteDirectory(const WCHAR* sPathToDelete);
94
BOOL DeleteRegistryKey();
96
BOOL ShutdownFastStartService(const WCHAR *sInstallPath);
97
BOOL RunSystemUninstall();
99
BOOL CALLBACK DlgUninstall(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
100
BOOL OnDialogInit(HWND hDlg);
101
BOOL OnDialogCreated(HWND hDlg);
102
int OnUninstall(HWND hDlg);
103
void UpdateProgress();
105
///////////////////////////////////////////////////////////////////////////////
108
int WINAPI WinMain(HINSTANCE hInstance,
109
HINSTANCE hPrevInstance,
113
HWND hWnd = GetForegroundWindow();
114
*g_sUninstallPath = 0;
116
WCHAR *sCommandLine = GetCommandLine();
117
WCHAR *sRemoveParam = wcsstr(sCommandLine, c_sRemoveParam);
118
WCHAR g_bRunFromSetupDll = (wcsstr(sCommandLine, c_sSetupParam) != NULL);
119
if (sRemoveParam != NULL)
121
// Launched from a temp directory with parameters "[remove] \Program Files\Fennec\"
122
wcscpy(g_sUninstallPath, sRemoveParam + wcslen(c_sRemoveParam) + 1);
126
// Just copy this EXE and launch it from a temp location with a special parameter
127
// to delete itself in the installation directory
131
// Perform uninstallation when executed with a special parameter
132
// (or in case when CopyAndLaunch failed - just execute in place)
136
MessageBoxW(hWnd, L"Cannot find the strings file.", L"Uninstall", MB_OK|MB_ICONWARNING);
140
if (GetInstallPath(g_sInstallPath))
142
int nDlgResult = DialogBox(hInstance, (LPCTSTR)IDD_MAIN, NULL, (DLGPROC)DlgUninstall);
143
if (nDlgResult != ErrOK)
144
g_nResult = nDlgResult;
148
MessageBoxW(hWnd, Strings.GetString(StrID_InstallationNotFound),
149
Strings.GetString(StrID_UninstallCaption), MB_OK|MB_ICONINFORMATION);
150
return ErrInstallationNotFound;
156
int Uninstall(HWND hWnd)
158
// Remove all installed files
159
DeleteDirectory(g_sInstallPath);
160
DeleteShortcut(hWnd);
163
if (!g_bRunFromSetupDll)
164
RunSystemUninstall();
166
// TODO: May probably handle errors during deletion (such as locked directories)
167
// and notify user. Right now just return OK.
171
///////////////////////////////////////////////////////////////////////////////
174
BOOL CALLBACK DlgUninstall(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
179
return OnDialogInit(hDlg);
181
case WM_DIALOGCREATED:
182
return OnDialogCreated(hDlg);
185
switch (LOWORD(wParam))
188
EndDialog(hDlg, ErrOK);
192
EndDialog(hDlg, ErrCancel);
195
case IDC_BTN_UNINSTALL:
196
g_nResult = OnUninstall(hDlg);
202
EndDialog(hDlg, ErrCancel);
208
BOOL OnDialogCreated(HWND)
210
ShutdownFastStartService(g_sInstallPath);
211
// Just continue regardless of the result
215
BOOL OnDialogInit(HWND hDlg)
218
PostMessage(hDlg, WM_DIALOGCREATED, 0, 0);
220
SetWindowText(hDlg, Strings.GetString(StrID_UninstallCaption));
223
shidi.dwMask = SHIDIM_FLAGS;
224
shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_EMPTYMENU;
226
SHInitDialog(&shidi);
228
// Hide the Done button
229
SHDoneButton(hDlg, SHDB_HIDE);
232
WCHAR sMsg[c_nTempBufSize];
233
_snwprintf(sMsg, c_nTempBufSize, L"%s %s", Strings.GetString(StrID_FilesWillBeRemoved), g_sInstallPath);
234
SetWindowText(GetDlgItem(hDlg, IDC_STATIC_TEXT), sMsg);
235
SetWindowText(GetDlgItem(hDlg, IDC_QUESTION_TEXT), Strings.GetString(StrID_AreYouSure));
236
SetWindowText(GetDlgItem(hDlg, IDC_BTN_UNINSTALL), L"OK"); // TODO: Use localized text "Uninstall"
237
SetWindowText(GetDlgItem(hDlg, IDCANCEL), Strings.GetString(StrID_Cancel));
238
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS), SW_HIDE);
239
ShowWindow(GetDlgItem(hDlg, IDOK), SW_HIDE);
244
int OnUninstall(HWND hDlg)
246
SetWindowText(GetDlgItem(hDlg, IDC_QUESTION_TEXT), L"");
247
ShowWindow(GetDlgItem(hDlg, IDC_BTN_UNINSTALL), SW_HIDE);
248
ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_HIDE);
249
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS), SW_SHOW);
250
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS), PBM_SETRANGE, 0, (LPARAM) MAKELPARAM (0, c_nMaxDirs));
251
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS), PBM_SETPOS, 0, 0);
253
UpdateWindow(hDlg); // make sure the text is drawn
255
int ret = Uninstall(hDlg);
259
SetWindowText(GetDlgItem(hDlg, IDC_STATIC_TEXT), Strings.GetString(StrID_UninstalledSuccessfully));
263
//TODO: Localize this message. It might be shown later when the errors will be handled
264
SetWindowText(GetDlgItem(hDlg, IDC_STATIC_TEXT), L"There were errors during uninstallation.");
267
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS), SW_HIDE);
268
ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
273
void UpdateProgress()
275
SendMessage(GetDlgItem(g_hDlg, IDC_PROGRESS), PBM_SETPOS, (WPARAM)g_nDirsDeleted, 0);
278
///////////////////////////////////////////////////////////////////////////////
284
// Locate and load the strings file used by installer
285
if (*g_sUninstallPath == 0)
286
GetModulePath(g_sUninstallPath);
288
WCHAR sStringsFile[MAX_PATH];
289
_snwprintf(sStringsFile, MAX_PATH, L"%s\\%s", g_sUninstallPath, c_sStringsFile);
291
return Strings.LoadStrings(sStringsFile);
294
BOOL GetModulePath(WCHAR *sPath)
296
if (GetModuleFileName(NULL, sPath, MAX_PATH) == 0)
299
WCHAR *sSlash = wcsrchr(sPath, L'\\') + 1;
300
*sSlash = L'\0'; // cut the file name
305
BOOL GetInstallPath(WCHAR *sPath)
308
WCHAR sRegFennecKey[MAX_PATH];
309
_snwprintf(sRegFennecKey, MAX_PATH, c_sAppRegKeyTemplate, Strings.GetString(StrID_AppShortName));
311
LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sRegFennecKey, 0, KEY_ALL_ACCESS, &hKey);
312
if (result == ERROR_SUCCESS)
315
DWORD dwCount = MAX_PATH * sizeof(WCHAR);
316
result = RegQueryValueEx(hKey, L"Path", NULL, &dwType, (LPBYTE)sPath, &dwCount);
321
return (result == ERROR_SUCCESS);
324
BOOL DeleteDirectory(const WCHAR* sPathToDelete)
328
WCHAR sFilePath[MAX_PATH];
329
WCHAR sPattern[MAX_PATH];
330
WIN32_FIND_DATA findData;
332
wcscpy(sPattern, sPathToDelete);
333
wcscat(sPattern, L"\\*.*");
334
hFile = FindFirstFile(sPattern, &findData);
335
if (hFile != INVALID_HANDLE_VALUE)
339
if (findData.cFileName[0] != L'.')
341
_snwprintf(sFilePath, MAX_PATH, L"%s\\%s", sPathToDelete, findData.cFileName);
343
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
345
// Delete subdirectory
346
if (!DeleteDirectory(sFilePath))
351
// Set file attributes
352
if (SetFileAttributes(sFilePath, FILE_ATTRIBUTE_NORMAL))
355
if (!DeleteFile(sFilePath) && nTotalRetries < c_nMaxTotalRetries)
357
BOOL bDeleted = FALSE;
359
for (int nRetry = 0; nRetry < c_nMaxOneFileRetries; nRetry++)
361
Sleep(c_nRetryDelay);
362
if (DeleteFile(sFilePath))
374
} while (FindNextFile(hFile, &findData));
380
// Set directory attributes
381
if (SetFileAttributes(sPathToDelete, FILE_ATTRIBUTE_NORMAL))
386
if (!RemoveDirectory(sPathToDelete) && nTotalRetries < c_nMaxTotalRetries)
388
BOOL bDeleted = FALSE;
390
for (int nRetry = 0; nRetry < c_nMaxOneFileRetries; nRetry++)
392
Sleep(c_nRetryDelay);
393
if (RemoveDirectory(sPathToDelete))
407
// Deletes shortcuts for Fennec and FastStart service created by the installer.
408
// Note: The shortcut names have to be in sync with CreateShortcut in nsInstallerDlg.cpp
409
BOOL DeleteShortcut(HWND hwndParent)
413
WCHAR sProgramsPath[MAX_PATH];
414
if (SHGetSpecialFolderPath(hwndParent, sProgramsPath, CSIDL_PROGRAMS, FALSE))
416
WCHAR sShortcutPath[MAX_PATH];
417
_snwprintf(sShortcutPath, MAX_PATH, L"%s\\%s.lnk", sProgramsPath, Strings.GetString(StrID_AppShortName));
420
if(SetFileAttributes(sShortcutPath, FILE_ATTRIBUTE_NORMAL))
421
result = DeleteFile(sShortcutPath);
424
// Handle faststart shortcut
425
WCHAR sStartupPath[MAX_PATH];
426
if (SHGetSpecialFolderPath(hwndParent, sStartupPath, CSIDL_STARTUP, FALSE))
428
WCHAR sStartupShortcutPath[MAX_PATH];
429
_snwprintf(sStartupShortcutPath, MAX_PATH, L"%s\\%sFastStart.lnk", sStartupPath, Strings.GetString(StrID_AppShortName));
431
// It may not exist - just ignore that
432
if(SetFileAttributes(sStartupShortcutPath, FILE_ATTRIBUTE_NORMAL))
433
DeleteFile(sStartupShortcutPath);
439
BOOL DeleteRegistryKey()
441
WCHAR sRegFennecKey[MAX_PATH];
442
_snwprintf(sRegFennecKey, MAX_PATH, c_sAppRegKeyTemplate, Strings.GetString(StrID_AppShortName));
444
LONG result = RegDeleteKey(HKEY_LOCAL_MACHINE, sRegFennecKey);
445
return (result == ERROR_SUCCESS);
448
// Copy to a temp directory and launch from there
451
WCHAR sNewName[] = L"\\Temp\\uninstall.exe";
452
WCHAR sModule[MAX_PATH];
453
if (GetModuleFileName(NULL, sModule, MAX_PATH) == 0 || !GetModulePath(g_sUninstallPath))
455
if (CopyFile(sModule, sNewName, FALSE))
457
PROCESS_INFORMATION pi;
458
WCHAR sParam[c_nTempBufSize];
459
if (g_bRunFromSetupDll)
460
_snwprintf(sParam, c_nTempBufSize, L"%s %s %s", c_sSetupParam, c_sRemoveParam, g_sUninstallPath);
462
_snwprintf(sParam, c_nTempBufSize, L"%s %s", c_sRemoveParam, g_sUninstallPath);
464
// Launch "\Temp\uninstall.exe remove \Program Files\Fennec\"
465
return CreateProcess(sNewName, sParam, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi);
471
BOOL ConvertToChar(const WCHAR *wstr, char *str, DWORD bufSize)
473
return 0 != WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, bufSize, NULL, NULL);
476
BOOL ShutdownFastStartService(const WCHAR *wsInstallPath)
480
// Class name: appName + "MessageWindow"
481
WCHAR sClassName[c_nTempBufSize];
482
_snwprintf(sClassName, c_nTempBufSize, L"%s%s", Strings.GetString(StrID_AppShortName), L"MessageWindow");
484
HWND handle = ::FindWindowW(sClassName, NULL);
487
char sPath[MAX_PATH];
488
ConvertToChar(wsInstallPath, sPath, MAX_PATH);
489
size_t pathLen = strlen(sPath);
490
if (pathLen > 0 && sPath[pathLen-1] != '\\')
496
char sCopyData[MAX_PATH * 3];
497
_snprintf(sCopyData, MAX_PATH * 2, "\"%s%S.exe\" -shutdown-faststart", sPath, Strings.GetString(StrID_AppShortName));
498
size_t cmdLineLen = strlen(sCopyData);
500
char *sRest = sCopyData + cmdLineLen + 1;
501
strcpy(sRest, sPath);
503
COPYDATASTRUCT cds = {
505
pathLen + 1 + cmdLineLen,
508
::SendMessage(handle, WM_COPYDATA, 0, (LPARAM)&cds);
510
// Wait until it's shut down or for some timeout
511
for (int i = 0; i < 20 && handle; i++)
514
handle = ::FindWindowW(sClassName, NULL);
517
// The window must not exist if the service shut down properly
518
result = (handle == NULL);
523
// Remove the part installed from the CAB
524
BOOL RunSystemUninstall()
526
WCHAR sXML[c_nTempBufSize];
527
LPWSTR sXMLOut = NULL;
529
_snwprintf(sXML, c_nTempBufSize,
530
L"<wap-provisioningdoc>"
531
L" <characteristic type=\"UnInstall\">"
532
L" <characteristic type=\"%s\">"
533
L" <parm name=\"uninstall\" value=\"1\" />"
534
L" </characteristic>"
535
L" </characteristic>"
536
L"</wap-provisioningdoc>",
537
Strings.GetString(StrID_AppLongName));
539
HRESULT hr = DMProcessConfigXML(sXML, CFGFLAG_PROCESS, &sXMLOut);