It was really easy (avoiding Windows' boilerplate message, that is). You just have to make your application handle the UnhandledException event.
For a VB.NET app windows forms like mine, that meant going to the project properties, Application
tab and clicking the View Application events
button.
That opened the (hidden by default) ApplicationEvents.vb file. I added some code:
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_UnhandledException(ByVal sender As Object, _
ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs _
) Handles Me.UnhandledException
MessageBox.Show("Application crashed due to the following unhandled exception. " + e.Exception.ToString())
End Sub
End Class
End Namespace
Now, if I'm missing some dll, I get its name, if it's managed or mixed, in the exception message. If the missing dll is unmanaged, I get just the name of the mixed assembly which needs it. But that's ok with me; throwing the mixed assembly on Depends.exe quickly reveals the offending unmanaged assembly.