Most of the time I feel quite bored at work, even on the forums. I hate routine, and I hate redundant tasks. When you’ve been around as long as I have been, you’d probably feel the same way. Somebody has to do the dirty work I suppose. Sometimes though, I get a project or an idea (mostly) or a post on the forums that piques my interest. Today’s topic is such a case and I am delighted to share it with you.
Today we will learn how to make your own Uninstaller in VB.NET. Let’s get the show on the road!
Design
I have named my project HTG_UnInstall, but you could name it anything you like. Add a Listview, three labels and a button to your form. Make sure your ListView’s View Property is set to Details.
My layout looks like the following picture:
Figure 1 – Our Layout
Logic
The logic that I used was actually quite simple. Once I figured out which registry key lists all the Install & Uninstall information for each of my programs on my PC, the rest was easy. The Registry key we need to observe here is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products
If you were to look at it, you’ll see a lot of CLSIDs listed. If you do not know what a CLSID is, have a look at this article I wrote many many years ago when my brain was still working optimally. Figure 2 shows what you might see if you were to open this key in the registry editor:
Figure 2 – CLSIDs representing our programs.
These CLSIDs represent our programs. If you were to expand any of the listed items, you’d get 4 more subkeys (Features, InstallProperties, Patches and Usage) as shown in Figure 3.
Figure 3 – CLSID subkeys
We need to delve into the InstallProperties key. This is where all the information is stored for each installed program, as you can see in Figure 4:
Figure 4 – Installed program properties
The trick now is to list each of these items into a listview, with our desired settings / properties that we need. If you were to look at your PC’s Control Panel-Uninstall a program window, you’d see that my listview (Figure 1) is basically based on that. Just a refresher on what your Uninstall a program looks like when you click: Control Panel, Programs, Uninstall a program, have a look at Figure 5.
Figure 5 – Your system’s uninstall window
After we have loaded the list of installed programs, we need to be able to uninstall the selected program. Let me not get ahead of myself here (as usual) and let’s start coding!
Code
Because we will be reading from the system Registry, we need to import the System.Win32 namespace:
VB.NET
Imports Microsoft.Win32 'Assists in Registry Reading & Writing
We need to add the arrays that will host our Uninstall commands:
VB.NET
Private strUninstallStrings() As String 'Array to hold Uninstall commands for each program Private NewUninstallStrArr 'Filtered array containing ONLY valid uninstall commands
Let’s load each installed program inside the Form_Load event:
VB.NET
Private Sub frmUninstall_Load(sender As Object, e As EventArgs) Handles Me.Load 'Location in Registry where all uninstall "settings" are stored Dim ParentKey As RegistryKey = _ Registry.LocalMachine.OpenSubKey("SOFTWARE\MICROSOFT\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products") Dim count As Integer = 0 'Loop Counter Dim ChildKey As RegistryKey 'Sub key of Parent key, to read necessary Uninstall properties 'Loop through each GUID listed For Each child As String In ParentKey.GetSubKeyNames() ChildKey = ParentKey.OpenSubKey(child).OpenSubKey("InstallProperties") 'Read InstallProperties value(s) If Not ChildKey Is Nothing Then 'If not empty, display inside ListView Dim LItem As New ListViewItem() 'Create new ListView item LItem.Text = ChildKey.GetValue("DisplayName").ToString 'First Column ( Main Item ) Text - Display Name LItem.SubItems.Add(ChildKey.GetValue("Publisher").ToString) 'Publisher LItem.SubItems.Add(ChildKey.GetValue("InstallDate").ToString) 'Install Date LItem.SubItems.Add(ChildKey.GetValue("EstimatedSize").ToString) 'Estimated Size LItem.SubItems.Add(ChildKey.GetValue("DisplayVersion").ToString) 'Display Version ReDim Preserve strUninstallStrings(count) 'Redim Array If ChildKey.GetValue("UninstallString") IsNot Nothing Then 'Determine Uninstall Command(s) strUninstallStrings(count) = ChildKey.GetValue("UninstallString") 'Store each command for each program lvApps.Items.Add(LItem) 'Add ListItem Else 'If No Uninstall Command Present, Identify it strUninstallStrings(count) = "No Uninstall String" End If End If count += 1 'Increment Counter for each item Next 'Use LINQ to filter strUninstallStrings array, to only get valid programs with valid uninstall strings NewUninstallStrArr = (From str In strUninstallStrings Where Not {"No Uninstall String"}.Contains(str)).ToArray() 'New array to be used End Sub
We first locate the uninstall location. We then obtain each program’s InstallProperties subkey. Here, we read all the values and load them into our ListView. Important here, is that we need to obtain each program’s UninstallString value and store it. Why? When this is stored inside an array, it is easy to connect our UninstallString command array with the particular program that will be selected in the ListView. There is a catch however. Not all programs have an UninstallString value set. This is usually for programs that are actually just updates or patches.
Because of this, we need to filter them out of the equation. Via the use of LINQ, we determine the invalid array entries and remove them. This gives us a new array to work with and to connect with to the ListView.
Add the ListView SelectedIndexChanged event:
VB.NET
Private Sub lvApps_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lvApps.SelectedIndexChanged btnUninstall.Visible = True 'Upon itemn selection make these controls visible lblPublisher.Visible = True lblSize.Visible = True lblVersion.Visible = True 'Display each Installed program's properties lblPublisher.Text = lvApps.FocusedItem.SubItems(1).Text lblSize.Text = "Size: " & lvApps.FocusedItem.SubItems(3).Text lblVersion.Text = "Product Version: " & lvApps.FocusedItem.SubItems(4).Text End Sub
Here, we just display the selected item’s properties, and allow the user to uninstall the selected program.
Add the Uninstall button‘s code:
VB.NET
Private Sub btnUninstall_Click(sender As Object, e As EventArgs) Handles btnUninstall.Click 'Use Shell to execute uninstall command Dim procID As Integer procID = Shell(NewUninstallStrArr(lvApps.FocusedItem.Index), AppWinStyle.NormalFocus) End Sub
By using the Shell function, we run the selected program’s uninstaller. Why Shell? Well, if you were to look at the format of each program’s UninstallString, you will notice it resembles the following:
MsiExec.exe /X{90120000-0020-0409-0000-0000000FF1CE}
Shell makes it easier to execute this command. If we have used the Process object found in the System.Diagnostics namespace, we would have had to build each argument, and we’d end up with 5-7 lines of code, whereas we only need one. Call me a stickler for the old-fashioned way of doing things, but I have always preferred Shell over the Process Object; especially when CLSIDs are involved.
I am including the source code for this article below.
Conclusion
Well, there you have it. I hope you have benefited from this article and that you have learned a thing or two from it. Obviously there is much more you could do with this project, but this is all I have time for right now. Perhaps in a future update we can play around with this idea some more! Until next time, cheers and keep coding!