Elusive Bugs: No known cause for missing resources, but solved regardless

Sometimes I get bug reports and, since no one else has ever reported them and they seem weird (“they” referring to the crash, not the user!), I write them off as a fluke, a one-time random occurrence, or what you might call “bit rot”. However, when you start to get many crash logs with the same error messages over and over again, it’s obviously none of those. That doesn’t mean anybody knows how to reproduce the error though!

For months, I used to receive e-mails with the following crash log:

Exception details:
System.Resources.MissingManifestResourceException: Could not find any resources appropriate for the specified culture (or the neutral culture) on disk.
baseName: PaintDotNet.Strings locationInfo: <null> fileName: PaintDotNet.Strings.resources

I could tell that the PaintDotNet.Strings.resources file was missing. I could even reproduce this crash by simply deleting the file. Fixing was usually an easy matter as well – the user just had to reinstall Paint.NET, or click “Repair” in the Add/Remove Programs control panel. However, I had no idea why or how the file was missing. Was it just being randomly deleted? Was it a race condition in the installer? Amazingly enough, I actually had this randomly happen to myself once or twice while doing regular installation and upgrading of some old builds of Paint.NET v3.0. So I knew for 100% sure that it was a real issue that had to be solved. And I wanted my inbox back! This was a top crash report for Paint.NET v2.6 through v2.72.

To this day, I still have no idea what causes this to happen. However, I did solve the problem for users! I decided that Paint.NET would auto-repair itself if it discovered any important files were missing. Thus, PdnRepair.exe was born!

All it really does it execute a “repair” operation on the Paint.NET installation, functionality that is built-in to Windows Installer via MsiReinstallProduct() and the REINSTALLMODE_FILEMISSING flag. This required a small change to the installer for Paint.NET, whereby it simply had to write Paint.NET’s product code GUID out to the registry so that PdnRepair would know which MSI it needed to repair (it ain’t psychic!).

You can see this in action pretty easily. Just go to your installation directory for Paint.NET and delete any *.EXE, *.DLL, or *.resources files except for PaintDotNet.exe and PaintDotNet.SystemLayer.dll. Then, launch Paint.NET as normal.

Once you click Repair, a console window will show up with some diagnostic text, followed by some standard Windows Installer dialogs and progress bars. When it’s finished, Paint.NET will automatically relaunch itself. Pretty easy, eh? The only problem with this dialog is that the text had to be hard coded and is English-only. This is because the file that’s always missing in the crash logs I’ve received is the file with the string resources in it!

And now I only get about 1 or 2 of these crash logs per month and it’s always from folks who are still running version 2.72. Even though I was never able to get a solid repro on the bug, it was still solved and one of my top support issues was eradicated! It also served as a generic repair utility for any number of other small issues that may crop up.

Lastly, the source code for this utility is very straightforward. If you want to see how it’s done for your project that uses MSI’s for installation, just go to the Paint.NET download page and grab the source code! It’s in the src/PdnRepair directory.