blob: 74e6f05956b4389ee91daa0e32b2b008ede26b03 [file] [log] [blame]
/*
* Q&D Uninstaller (main.c)
*
* Copyright 2000 Andreas Mohr <andi@lisas.de>
* Copyright 2004 Hannu Valtonen <Hannu.Valtonen@hut.fi>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ToDo:
* - add search box for locating entries quickly
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#include "main.h"
#include "regstr.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(uninstaller);
/* Work around a Wine bug which defines handles as UINT rather than LPVOID */
#ifdef WINE_STRICT
#define NULL_HANDLE NULL
#else
#define NULL_HANDLE 0
#endif
/* use multi-select listbox */
#undef USE_MULTIPLESEL
/* Delete uninstall registry key after execution.
* This is probably a bad idea, because it's the
* uninstall program that is supposed to do that.
*/
#undef DEL_REG_KEY
char appname[18];
static char about_string[] =
"Wine Application Uninstaller (C) 2004 by Andreas Mohr <andi@lisas.de> and Hannu Valtonen <Hannu.Valtonen@hut.fi>";
static char program_description[] =
"Please select the application you wish to uninstall:";
typedef struct {
char *key;
WCHAR *descr;
char *command;
int active;
} uninst_entry;
uninst_entry *entries = NULL;
int numentries = 0;
int list_need_update = 1;
int oldsel = -1;
struct {
DWORD style;
LPCSTR text;
HWND hwnd;
} button[] =
{
{ BS_PUSHBUTTON, "Add/Remove", 0 },
{ BS_PUSHBUTTON, "About", 0 },
{ BS_PUSHBUTTON, "Exit", 0 }
};
#define NUM (sizeof(button)/sizeof(button[0]))
int FetchUninstallInformation(void);
void UninstallProgram(void);
void ListUninstallPrograms(void)
{
int i, len;
char *descr;
if (! FetchUninstallInformation())
return;
for (i=0; i < numentries; i++)
{
len = WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, NULL, 0, NULL, NULL);
descr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
WideCharToMultiByte(CP_UNIXCP, 0, entries[i].descr, -1, descr, len, NULL, NULL);
printf("%s|||%s\n", entries[i].key, descr);
HeapFree(GetProcessHeap(), 0, descr);
}
}
void RemoveSpecificProgram(char *name)
{
int i;
if (! FetchUninstallInformation())
return;
for (i=0; i < numentries; i++)
{
if (strcmp(entries[i].key, name) == 0)
{
entries[i].active++;
break;
}
}
if (i < numentries)
UninstallProgram();
else
{
fprintf(stderr, "Error: could not match application [%s]\n", name);
}
}
int main( int argc, char *argv[])
{
MSG msg;
WNDCLASS wc;
HWND hWnd;
LPSTR token = NULL;
int i = 1;
HINSTANCE hInst = NULL;
while( i<argc )
{
token = argv[i++];
/* Handle requests just to list the applications */
if( !lstrcmpA( token, "--list" ) )
{
ListUninstallPrograms();
return 0;
}
else if( !lstrcmpA( token, "--remove" ) )
{
if( i >= argc )
{
WINE_ERR( "The remove option requires a parameter.\n");
return 1;
}
RemoveSpecificProgram( argv[i++] );
return 0;
}
else
{
WINE_ERR( "unknown option %s\n",token);
return 1;
}
}
LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
wc.style = 0;
wc.lpfnWndProc = MainProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon( hInst, appname );
wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = appname;
if (!RegisterClass(&wc)) exit(1);
hWnd = CreateWindow( appname, "Wine Application Uninstaller",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL_HANDLE, NULL_HANDLE, hInst, NULL );
if (!hWnd) exit(1);
ShowWindow( hWnd, SW_SHOW );
UpdateWindow( hWnd );
while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return msg.wParam;
}
int cmp_by_name(const void *a, const void *b)
{
return lstrcmpiW(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
}
int FetchUninstallInformation(void)
{
HKEY hkeyUninst, hkeyApp;
int i;
DWORD sizeOfSubKeyName, displen, uninstlen;
char subKeyName[256];
char key_app[1024];
char *p;
numentries = 0;
oldsel = -1;
if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
{
MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
return 0;
}
if (!entries)
entries = HeapAlloc(GetProcessHeap(), 0, sizeof(uninst_entry));
strcpy(key_app, REGSTR_PATH_UNINSTALL);
strcat(key_app, "\\");
p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
sizeOfSubKeyName = 255;
for ( i=0;
RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
++i )
{
static const WCHAR DisplayNameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
strcpy(p, subKeyName);
RegOpenKeyExA(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
if ( (RegQueryValueExW(hkeyApp, DisplayNameW,
0, 0, NULL, &displen) == ERROR_SUCCESS)
&& (RegQueryValueExA(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
{
numentries++;
entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
entries[numentries-1].key =
HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
strcpy(entries[numentries-1].key, subKeyName);
entries[numentries-1].descr =
HeapAlloc(GetProcessHeap(), 0, displen);
RegQueryValueExW(hkeyApp, DisplayNameW, 0, 0,
(LPBYTE)entries[numentries-1].descr, &displen);
entries[numentries-1].command =
HeapAlloc(GetProcessHeap(), 0, uninstlen);
entries[numentries-1].active = 0;
RegQueryValueExA(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
entries[numentries-1].command, &uninstlen);
WINE_TRACE("allocated entry #%d: %s (%s), %s\n",
numentries, entries[numentries-1].key,
wine_dbgstr_w(entries[numentries-1].descr),
entries[numentries-1].command);
}
RegCloseKey(hkeyApp);
sizeOfSubKeyName = 255;
}
qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
RegCloseKey(hkeyUninst);
return 1;
}
void UninstallProgram(void)
{
int i;
char errormsg[1024];
BOOL res;
STARTUPINFO si;
PROCESS_INFORMATION info;
DWORD exit_code;
#ifdef DEL_REG_KEY
HKEY hkey;
#endif
for (i=0; i < numentries; i++)
{
if (!(entries[i].active)) /* don't uninstall this one */
continue;
WINE_TRACE("uninstalling %s\n", wine_dbgstr_w(entries[i].descr));
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_NORMAL;
res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
if (res == TRUE)
{ /* wait for the process to exit */
WaitForSingleObject(info.hProcess, INFINITE);
res = GetExitCodeProcess(info.hProcess, &exit_code);
WINE_TRACE("%d: %08lx\n", res, exit_code);
#ifdef DEL_REG_KEY
/* delete the application's uninstall entry */
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
0, KEY_READ, &hkey) == ERROR_SUCCESS)
{
RegDeleteKey(hkey, entries[i].key);
RegCloseKey(hkey);
}
#endif
}
else
{
sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
MessageBox(0, errormsg, appname, MB_OK);
}
}
WINE_TRACE("finished uninstall phase.\n");
list_need_update = 1;
}
LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HFONT static_font, listbox_font;
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
int cxChar, cyChar, i, y, bx, maxx, maxy, wx, wy;
static HWND hwndList = 0, static_text = 0;
DWORD style;
RECT rect;
switch( msg ) {
case WM_CREATE:
{
hdc = GetDC(hWnd);
GetTextMetrics(hdc, &tm);
static_font = CreateFont(tm.tmHeight + tm.tmExternalLeading, 0, 0, 0, 600, FALSE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
listbox_font = CreateFont(tm.tmHeight + tm.tmExternalLeading, 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hWnd, hdc);
/* FIXME: implement sorting and use LBS_SORT here ! */
style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
#ifdef USE_MULTIPLESEL
style |= LBS_MULTIPLESEL;
#endif
bx = maxx = cxChar * 3;
y = maxy = cyChar * 1;
static_text = CreateWindow("static", program_description,
WS_CHILD|WS_VISIBLE|SS_LEFT,
maxx, maxy,
cxChar * sizeof(program_description), cyChar * 1,
hWnd, (HMENU)1,
((LPCREATESTRUCT)lParam)->hInstance, NULL);
SendMessage(static_text, WM_SETFONT, (WPARAM)static_font, MAKELPARAM(FALSE, 0));
maxy += cyChar * 2; /*static text + distance */
hwndList = CreateWindow("listbox", NULL,
style,
maxx, maxy,
cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 10,
hWnd, (HMENU) 1,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
SendMessage(hwndList, WM_SETFONT, (WPARAM)listbox_font, MAKELPARAM(FALSE, 0));
GetWindowRect(hwndList, &rect);
maxx += (rect.right - rect.left)*1.1;
maxy += (rect.bottom - rect.top)*1.1;
wx = 20*cxChar;
wy = 7*cyChar/4;
y = cyChar * 3;
for (i=0; i < NUM; i++)
{
button[i].hwnd = CreateWindow("button", button[i].text,
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
maxx, y,
wx, wy,
hWnd, (HMENU)i,
((LPCREATESTRUCT)lParam)->hInstance, NULL);
if (!button[i].hwnd)
PostQuitMessage(0);
y += 2*cyChar;
}
maxx += wx + cxChar * 4;
maxy += cyChar * 2; /* window border */
SetWindowPos( hWnd, 0,
0, 0, maxx, maxy,
SWP_NOMOVE);
return 0;
}
case WM_PAINT:
{
hdc = BeginPaint( hWnd, &ps );
if (list_need_update)
{
int prevsel;
prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
if (!(FetchUninstallInformation()))
{
PostQuitMessage(0);
return 0;
}
SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
for (i=0; i < numentries; i++)
{
WINE_TRACE("adding %s\n", wine_dbgstr_w(entries[i].descr));
SendMessageW(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
}
WINE_TRACE("setting prevsel %d\n", prevsel);
if (prevsel != -1)
SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
list_need_update = 0;
}
EndPaint( hWnd, &ps );
return 0;
}
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
case WM_COMMAND:
if ((HWND)lParam == hwndList)
{
if (HIWORD(wParam) == LBN_SELCHANGE)
{
int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
#ifndef USE_MULTIPLESEL
if (oldsel != -1)
{
entries[oldsel].active ^= 1; /* toggle */
WINE_TRACE("toggling %d old %s\n", entries[oldsel].active,
wine_dbgstr_w(entries[oldsel].descr));
}
#endif
entries[sel].active ^= 1; /* toggle */
WINE_TRACE("toggling %d %s\n", entries[sel].active,
wine_dbgstr_w(entries[oldsel].descr));
oldsel = sel;
}
}
else
if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
{
UninstallProgram();
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
else
if ((HWND)lParam == button[1].hwnd) /* About button */
MessageBox(0, about_string, "About Wine Application Uninstaller", MB_OK);
else
if ((HWND)lParam == button[2].hwnd) /* Exit button */
PostQuitMessage(0);
return 0;
}
return( DefWindowProc( hWnd, msg, wParam, lParam ));
}