Making a Program Uninstaller with VB.NET

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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:

Our Layout
Figure 1Our 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:

CLSIDs representing our programs.
Figure 2CLSIDs 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.

CLSID subkeys
Figure 3CLSID 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:

Installed program properties
Figure 4Installed 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.

Your system's uninstall window
Figure 5Your 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!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read