EXIFViewer, Redux

Last year, I wrote about a small photo EXIF data viewer I’d built. Unfortunately, I hadn’t really given the project much thought since then, especially since it was written to run on Windows and late last year, I switched back to MacOS. 

Recently, however, i’ve been toying with idea of porting it to MacOS, especially since Microsoft’s Xamarin lets you write .NET code and compile it with MacOS as the target OS. However, to do so, I needed to rewrite for the Windows platform.

The first problem is that the original was written in Visual Basic.NET, which is great for rapidly-building applications, but is not a modern language and is on its way to being deprecated by Microsoft.

The second problem, and this is somewhat-embarassing considering that I’m a software development manager and solutions architect at my day job, but the application was poorly-built (I threw it together in a couple of hours). No modularity. No proper design patterns. Logic intermingled with UI. Lots of global variables. 

So, to port to MacOS via Xamarin, I’d need to rewrite the code in C# (since VB.NET isn’t supported) and I’d need to make it more modular, so that the processing/backend was abstracted away from the user interface. This way, I could use the codebase that extracts the EXIF data in my Mac version without modification and will only need to build the UI elements for MacOS. 

At any rate, I’ve started making my first stabs at writing the Mac version, but until then, the Windows version is available on GitHub here. You can download the installer here.

I welcome feedback, contributions and pull requests!

Cleaning Windows

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. 


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.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;

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)

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)

        private void fileSystemWatcher2_Created(object sender, FileSystemEventArgs e)

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)

            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.

A Lightweight EXIF Data Viewer

If you’ve read the title of this post and are wondering “what is this EXIF thing?”, then here’s a bit of information. EXIF is an acronym for EXchangeable Image File Format. And, no, I don’t know why it’s not “EXIFF”. Basically, it’s metadata tagged onto a digital image that contains information about that image. This, along with another group of metadata, IPTC, is used by digital photographers to keep track of information about such things as camera/lens settings, geographic information and copyright of a given photo.

Some photographers post their images online with this information intact, while others will strip it out when posting, keeping their secret sauce to themselves. For myself, I keep it intact as I hope it might be helpful to other photographers to understand how a photo was capture as well as being an aid in enforcing copyright. Most, if not all, photos on my photography site have this data tagged onto them and the basic data can be viewed by clicking the “View Photo Data and Location” button under the photo:

Basic EXIF data on 75CentralPhotography.Com

However, there are a lot of times that I want to view this data locally for unpublished photos on my PC. To make this easy, I wrote a simple Windows application that will display this data for a selected photo:

Main Interface

It displays the most-commonly used EXIF data on the main interface; and, if there’s GPS information embedded in the metadata, it shows a button to view the photo’s location on Google Maps. If you want all the EXIF data, you can click “File→Show All EXIF Data…” and a dialog will appear showing everything:

Everything, Everything

This application is written in VB.NET and the source code is available on GitHub. If you want to install it, you’re welcome to download it here.

A couple of installation notes:

When downloading the installer, you may get this warning:

Because this app hasn’t been installed enough times for Windows to “trust” it, Windows Defender wants you to really think about it before installing. To continue, click the three dots and choose “Keep”.

You might then get another warning:

Go ahead and click the down arrow next to “Show more” and click “Keep Anyway”. Then, navigate to your download location and doubleclick EXIFViewer.msi to install. You might get another warning:

Click More info and you’ll get the option to run anyway. At this point, the installer will launch and you can install the application.

A lot of rigmarole to install an app, but it’s for most people’s own good, as Windows tries its best to prevent you from installing malicious software using Defender Smartscreen. In this case, you’re going to have to trust me that this isn’t malicious. You have the option, of course, to review the source code at the Github repository listed above. And you know where to find me. If enough people install, Windows will eventually allow it past Smartscreen without complaint.

If you download and use the EXIFViewer and have any feedback or find any bugs, please submit an issue here or send me an email at matt@75central.com.