I’ll admit it…I’m kind of a messy person. My desk is covered with various tools, gadgets, opened and unopened mail, receipts and whatnot. In my house, it’s okay to drape your jacket over the back of the easy chair rather than hanging it in the closet. And maybe the dishes don’t get done right after dinner. We’re not gross, unclean people like you’d see on Hoarders, just slightly messy, have-a-lot-of-things-going-on people. 

There is, however, one place that I like to keep pristine. Unfortunately, it’s in the nerdy, digital realm…my Windows desktop. I like there to be no icons, just the background and the Recycle Bin:

 

To achieve this, I periodically minimize all windows, select any detritus that’s gathered and drag it to the Recycle Bin. 

Perfection!

Since I don’t consciously ever save anything to the desktop, the only things that gather there are shortcuts “helpfully” created when an application is installed. While some installers nicely ask you if you want to create a desktop shortcut, in my experience, these are pretty rare. Most just plop one down there and make themselves at home.

Keeping up with this virtual cleaning is a manual task, and while it doesn’t take but a few seconds at most, it’s still something that I have to do. 

So, while the world has been obsessed with keeping meatspace clean due to COVID-19, I decided to come up with a way to keep my virtual space clean automatically, because I’m both nerdy and lazy.

So, introducing ShortcutCleaner—a small program that lives in your system tray and watches for shortcuts to be created on the desktop, then quietly whisks them away into nothingness.

You can install it by going to the release page on GitHub, downloading the .msi file and running.

If you need a deeper dive in installing (i.e. you run into issues with Window disallowing install because of Windows Defender SmartScreen), take a look at my post on the ExifViewer I built.

Now for the nerdy part: a breakdown of how the code works. So exciting!

First, we do some boilerplate Visual Studio C# stuff when the program starts to instantiate our main form

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new formMain());

When formMain() is instantiated, we go ahead an declare a few class properties to get started:

 [DllImport("Shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);

        public const int SHCNE_ASSOCCHANGED = 0x8000000;
        public const int SHCNF_IDLIST = 0;
        public string commonPath;
        public string specialPath;

DllImport does what it says on the box: it imports shell32.dll into the project so we can access some low-level Windows functionality. In this case, we want to be able to force Windows to refresh the desktop after we’ve cleaned it…otherwise the shortcuts will be logically deleted, but they might hang around on the desktop until Windows does some automated housecleaning. And once we’ve imported the .dll, we can go ahead and declare the SHChangeNotify function and associated constants, which will actually do the refresh. Finally, we define a couple of class-level variables to hold the two paths for the Windows desktop. (Yes, there are two paths in Windows…the desktop for all users and the desktop specific to the currently logged-in user).

 private void formMain_Load(object sender, EventArgs e)
        {
            commonPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
            specialPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            fileSystemWatcher1.Path = commonPath;
            fileSystemWatcher2.Path = specialPath;
            this.ShowInTaskbar = false;
            loadClean(commonPath);
            loadClean(specialPath);
        }

Next, we load formMain(). We set the two previsouly-mentioned desktop paths, then we instantiate two fileSystemWatcher objects. These are built-in components in .NET that will watch a specified folder for changes. Since we have two paths, we need to fileSystemWatchers. Since we want this utility to only live in the system tray, we tell the application to not be shown in the taskbar. Finally, on load, we call our function that actually does the cleaning twice, once for each of the watched paths.

private void loadClean(string cleanPath)
        {
            string checkEx = @".lnk";

            foreach (string fileName in Directory.GetFiles(cleanPath))
            {
                string extension = Path.GetExtension(fileName);
                if (extension == checkEx)
                {
                    File.Delete(fileName);
                }
            }
        }

loadClean(), called at startup, simply takes the specified path as an argument and loops through all the files at that path. Since we want to delete shortcuts, we look for files with the extension “.lnk”. If we find one, we delete it.

 private void fileSystemWatcher1_Created(object sender, FileSystemEventArgs e)
        {
            clean(e.FullPath);
        }

        private void fileSystemWatcher2_Created(object sender, FileSystemEventArgs e)
        {
            clean(e.FullPath);
        }

Our two fileSystemWatchers are the same. If a file is created in the object’s path, we call the clean() function with the file’s full path as the argument.

private void clean(string cleanPath)
        {
            string checkEx = @".lnk";

            string extension = Path.GetExtension(cleanPath);
            if (extension == checkEx)
            {
                Thread.Sleep(2000);
                File.Delete(cleanPath);
            }

            SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
        }

clean() is slightly different than loadClean() in that we don’t need to iterate through all the files in a given path since we know the relevant filename from invocation. In this function, we simple check if the extension is “.lnk” and delete it. Before we delete, however, we let the program’s execution thread sleep for two seconds to give Window’s time to complete creating the shortcut, otherwise our application might not be able to access the file as it will be locked during the write process. Finally, we call the SHChangeNotify function declared earlier to force Windows to refresh the desktop to ensure that the now-deleted shortcut icons are no longer visible to the user.

All-in-all, it’s a pretty small application and doesn’t use a lot of memory and was pretty simple to implement in only a few minutes. If you want to examine the source code closer, by all means take a look at it on GitHub. if you find any issues, log it on the GitHub repository or shoot me an email at matt@75central.com.

What are your thoughts?