1 Option Explicit ' Variables must be declared explicitly
5 '******************************************************************************
6 ' Global constants and variables
7 '******************************************************************************
8 dim Tab, NewLine ' String constants
9 dim Shell, FSO ' Global objects
10 dim ProgArgs ' Program arguments
11 dim Dbg ' Output debugging stuff
12 dim Language ' Program language
13 dim AppName ' Application name
14 dim Title ' Application title
15 dim UninstallCtrlFileName ' Name of the uninstall control file
16 dim SystemDrive ' The system drive
17 dim SystemRoot ' The windows directory
18 dim UserName ' Name if the current user
19 dim UserProfile ' User profile directory
20 dim ProgramFiles ' Program files directory
21 dim Failed ' Global flag for removal failed
22 dim RegList ' List of registry entries to remove
23 dim FileList ' List of files to remove
24 dim DirList ' List of directories to remove
28 '******************************************************************************
29 ' Display an error message window with an OK button
30 '******************************************************************************
32 call MsgBox (Msg, vbOkOnly + vbExclamation, Title)
37 '******************************************************************************
38 ' Display an error message window and abort the installer
39 '******************************************************************************
47 '******************************************************************************
48 ' Convert a number to a string
49 '******************************************************************************
50 function ToString (Num)
51 ToString = FormatNumber (Num, vbFalse, vbTrue, vbFalse, vbFalse)
56 '******************************************************************************
57 ' Return a message in the current language
58 '******************************************************************************
62 ' Handle other languages here
66 ' No assignment, use english
69 Msg = "Do you want to abort the installation?"
71 Msg = "You must be Administrator to remove %1."
72 Msg = Msg & " Are you sure you want to continue?"
73 case "MSG_CTRLFILEERR"
74 Msg = "The file %1 is invalid." & NewLine
75 Msg = Msg & "Line %2: %3"
77 Msg = "Some folders could not be removed:"
78 Msg = Msg & NewLine & "%1"
80 Msg = "Duplicate value"
82 Msg = "Could not remove %1." & NewLine
83 Msg = "%2 needs to be run by an Administrator!"
85 Msg = "Some files could not be deleted:"
86 Msg = Msg & NewLine & "%1"
88 Msg = "Error opening %1"
90 Msg = "Some registry entries could not be deleted:"
91 Msg = Msg & NewLine & "%1"
95 Msg = "%1 has been successfully removed."
97 Msg = "Usage:" & NewLine & "uninstall appname ctrl-file"
107 '******************************************************************************
108 ' Format a string replacing %n specifiers in the format string F
109 '******************************************************************************
110 function Fmt (F, Values)
111 dim I, Count, Key, Val, Start, Pos
112 Count = UBound (Values) ' How many values?
113 for I = Count to 0 step -1
114 Key = "%" & ToString (I)
115 select case VarType (Values (I))
119 Val = ToString (Values (I))
121 Val = ToString (Values (I))
125 Val = ToString (Values (I))
127 Val = ToString (Values (I))
131 Abort ("Internal error: Invalid conversion in Format()")
133 F = Replace (F, Key, Val)
140 '******************************************************************************
141 ' Format a message replacing %n specifiers in the format string F
142 '******************************************************************************
143 function FmtMsg (Msg, Values)
144 FmtMsg = Fmt (GetMsg (Msg), Values)
149 '******************************************************************************
150 ' Return an environment string. Fix up Microsoft "innovative" ideas.
151 '******************************************************************************
152 function GetEnv (Key)
154 Value = Shell.ExpandEnvironmentStrings (Key)
156 GetEnv = vbNullString
164 '******************************************************************************
165 ' Build a path from two components
166 '******************************************************************************
167 function BuildPath (Path, Name)
168 BuildPath = FSO.BuildPath (Path, Name)
173 '******************************************************************************
174 ' Delete a folder and return an error string
175 '******************************************************************************
176 function DeleteFolder (Path)
178 call FSO.DeleteFolder (Path, true)
179 DeleteFolder = Err.Description
184 '******************************************************************************
185 ' Delete a file and return an error string
186 '******************************************************************************
187 function DeleteFile (Path)
189 call FSO.DeleteFile (Path, true)
190 DeleteFile = Err.Description
195 '******************************************************************************
196 ' Delete a registry entry
197 '******************************************************************************
198 function RegDelete (Key)
200 call Shell.RegDelete (Key)
201 RegDelete = Err.Description
206 '******************************************************************************
207 ' Sort an array of strings
208 '******************************************************************************
209 sub QS (byref A, Lo, Hi)
219 if StrComp (A(Lo), A(I), vbTextCompare) < 0 then
225 if StrComp (A(Lo), A(J), vbTextCompare) >= 0 then
240 ' Swap A(J) and A(Lo)
245 if (2 * J) > (Hi + Lo) then
246 call QS (A, J + 1, Hi)
249 call QS (A, Lo, J - 1)
255 sub Quicksort (byref A)
256 if UBound (A) > 1 then
257 call QS (A, LBound (A), UBound (A))
263 '******************************************************************************
264 ' Initialize global variables
265 '******************************************************************************
266 sub InitializeGlobals ()
269 ' String stuff used for formatting
274 set Shell = WScript.CreateObject ("WScript.Shell")
275 set FSO = CreateObject ("Scripting.FileSystemObject")
278 set ProgArgs = WScript.Arguments
280 ' Handle program arguments
282 Title = "Uninstaller"
283 UninstallCtrlFileName = ""
286 for I = 0 to ProgArgs.Count-1
287 select case ProgArgs(I)
296 AppName = ProgArgs(I)
297 elseif UninstallCtrlFileName = "" then
298 UninstallCtrlFileName = ProgArgs(I)
300 call ErrorMsg (GetMsg ("MSG_USAGE"))
306 ' We need the application name and uninstall control file
307 if AppName = "" or UninstallCtrlFileName = "" then
308 call Abort (GetMsg ("MSG_USAGE"))
311 ' Set the title early, because it's used in error messages
312 Title = AppName & " Uninstaller"
314 ' Paths and locations
315 SystemDrive = GetEnv ("%SystemDrive%")
316 if SystemDrive = vbNullString then
319 SystemRoot = GetEnv ("%SystemRoot%")
320 if SystemRoot = vbNullString then
321 SystemRoot = BuildPath (SystemDrive, "winnt")
323 UserName = GetEnv ("%USERNAME%")
324 if UserName = vbNullString then
325 UserName = "Administrator"
327 UserProfile = GetEnv ("%USERPROFILE%")
328 if UserProfile = vbNullString then
329 UserProfile = BuildPath (SystemDrive, "Dokumente und Einstellungen\" & UserName)
331 ProgramFiles = GetEnv ("%ProgramFiles%")
332 if ProgramFiles = vbNullString then
333 ProgramFiles = BuildPath (SystemDrive, "Programme")
336 ' Assume we could remove the software
343 '******************************************************************************
344 ' Ask a yes/no question and return the result. "Yes" is default.
345 '******************************************************************************
346 function AskYesNo (Question)
347 AskYesNo = MsgBox (Question, vbYesNo + vbQuestion + vbDefaultButton1, Title)
352 '******************************************************************************
353 ' Ask a yes/no question and return the result. "No" is default.
354 '******************************************************************************
355 function AskNoYes (Question)
356 AskNoYes = MsgBox (Question, vbYesNo + vbQuestion + vbDefaultButton2, Title)
361 '******************************************************************************
362 ' Ask if the user wants to abort install, and terminate if the answer is yes
363 '******************************************************************************
365 if AskNoYes (GetMsg ("MSG_ABORT")) = vbYes then
372 '******************************************************************************
373 ' Function that displays the paths and locations found
374 '******************************************************************************
375 function OneLoc (Key, Value)
378 if Len (Result) <= 8 then
379 Result = Result & Tab
381 OneLoc = Result & Tab & "=" & Tab & Value & NewLine
384 sub ShowPathsAndLocations ()
386 Msg = Msg & OneLoc ("SystemDrive", SystemDrive)
387 Msg = Msg & OneLoc ("SystemRoot", SystemRoot)
388 Msg = Msg & OneLoc ("UserName", UserName)
389 Msg = Msg & OneLoc ("UserProfile", UserProfile)
390 Msg = Msg & OneLoc ("ProgramFiles", ProgramFiles)
392 MsgBox Msg, vbOkOnly, "Paths and Locations"
397 '******************************************************************************
398 ' Check that were running this script as admin
399 '******************************************************************************
400 sub CheckAdminRights ()
402 ' FIXME: This check is not perfect
403 if UserName <> "Administrator" then
407 if AskNoYes (FmtMsg ("MSG_ADMIN", Args)) <> vbYes then
416 '******************************************************************************
417 ' Read the uninstall control file and create the data collections
418 '******************************************************************************
419 sub InvalidCtrlFile (Line, Val)
421 Args(1) = UninstallCtrlFileName
424 call Abort (FmtMsg ("MSG_CTRLFILEERR", Args))
427 sub ReadUninstallCtrlFile ()
430 dim File, Line, Tag, Args(3)
431 dim MyRegList, MyFileList, myDirList
433 ' Create some dictionaries. These are not really used as dictionaries, but
434 ' have the nice property of expanding dynamically, and we need that.
435 set MyRegList = CreateObject ("Scripting.Dictionary")
436 set MyFileList = CreateObject ("Scripting.Dictionary")
437 set MyDirList = CreateObject ("Scripting.Dictionary")
439 ' Open the file. Checking Err doesn't work here, don't know why.
442 set File = FSO.OpenTextFile (UninstallCtrlFileName, ForReading)
444 if File is nothing then
445 Args(1) = UninstallCtrlFileName
446 call Abort (FmtMsg ("MSG_OPENERR", Args))
449 ' Read all lines and insert them in their list
450 do while File.AtEndOfStream <> true
457 ' Get the type from the line and remove it, so the line contains just
462 ' Determine the type of the entry
466 ' A directory. Convert to lowercase to unify names.
468 if MyDirList.Exists (Line) then
469 call InvalidCtrlFile (File.Line - 1, GetMsg ("MSG_DUPLICATE"))
471 call MyDirList.Add (Line, "")
475 ' A file. Convert to lowercase to unify names
477 if MyFileList.Exists (Line) then
478 call InvalidCtrlFile (File.Line - 1, GetMsg ("MSG_DUPLICATE"))
480 call MyFileList.Add (Line, "")
485 if MyRegList.Exists (Line) then
486 call InvalidCtrlFile (File.Line - 1, GetMsg ("MSG_DUPLICATE"))
488 call MyRegList.Add (Line, "")
492 call InvalidCtrlFile (File.Line - 1, Tag & " " & Line)
503 ' Make the global arrays big enough for the data
504 RegList = Array (MyRegList.Count)
505 FileList = Array (MyFileList.Count)
506 DirList = Array (MyDirList.Count)
508 ' Copy the data into the global arrays
509 RegList = MyRegList.Keys
510 FileList = MyFileList.Keys
511 DirList = MyDirList.Keys
513 ' Sort all the lists. This makes sure nodes are in the array before the
514 ' leaves that depend on it. Or in other words: Top level directories and
515 ' registry entries come first. So if we delete the items starting at the
516 ' other side of the array, we will never delete a subdirectory before its
518 call QuickSort (RegList)
519 call QuickSort (FileList)
520 call QuickSort (DirList)
526 '******************************************************************************
527 ' Delete the registry entries
528 '******************************************************************************
529 sub DeleteRegistryEntries ()
531 dim I, Result, NoDel, Args(1)
534 for I = UBound (RegList) to LBound (RegList) step -1
535 Result = RegDelete (RegList (I))
537 ' Remember the entries we could not delete
538 NoDel = NoDel & RegList (I) & NewLine
544 call ErrorMsg (FmtMsg ("MSG_REGDEL", Args))
550 '******************************************************************************
552 '******************************************************************************
555 dim I, Result, NoDel, Args(1)
558 for I = UBound (FileList) to LBound (FileList) step -1
559 Result = DeleteFile (FileList (I))
561 ' Remember the files we could not delete
562 NoDel = NoDel & FileList (I) & NewLine
568 call ErrorMsg (FmtMsg ("MSG_FILEDEL", Args))
574 '******************************************************************************
575 ' Delete the directories
576 '******************************************************************************
577 sub DeleteDirectories ()
579 dim I, Result, NoDel, Args(1)
582 for I = UBound (DirList) to LBound (DirList) step -1
583 Result = DeleteFolder (DirList (I))
585 ' Remember the directories we could not delete
586 NoDel = NoDel & DirList (I) & NewLine
592 call ErrorMsg (FmtMsg ("MSG_DIRDEL", Args))
598 '******************************************************************************
599 ' Function that tells the user that the install was successful
600 '******************************************************************************
606 call MsgBox (FmtMsg ("MSG_SUCCESS", Args), vbOkOnly + vbInformation, Title)
612 '******************************************************************************
613 ' Function that tells the user that the uninstall failed
614 '******************************************************************************
621 ErrorMsg (FmtMsg ("MSG_FAILURE", Args))
628 '******************************************************************************
630 '******************************************************************************
635 ' Initialize global variables. This includes the paths used
638 ShowPathsAndLocations ()
641 ' Check that we're running this script as admin
644 ' Let the user make up his mind
646 if AskYesNo (FmtMsg ("MSG_REMOVE", Args)) <> vbYes then
650 ' Read the uninstall control file
651 call ReadUninstallCtrlFile ()
653 ' Delete the registry entries
654 call DeleteRegistryEntries ()
659 ' Delete the directories
660 call DeleteDirectories ()
672 '******************************************************************************
673 ' The script body just calls Main...
674 '******************************************************************************