-
What HockeyApp told me about my Visual Studio extension
Last time I wrote about how I managed to incorporate HockeyApp into a new Visual Studio extension I’ve created. Well I published the updated extension a couple of days ago and HockeyApp is already paying dividends.
Here’s the info from HockeyApp at the time of writing:
Build Number
Downloads
Crashes
Last Updated
1.1.0.0
0
119
29 Jul 2016, 10:15
1.0.0.0
0
8
26 Jul 2016, 21:54
HockeyApp can provide a way to deploy applications, but that isn’t relevant to Visual Studio extensions, which is why the ‘Downloads’ column is zero. So you can see there’s a bunch of crashes (aka exceptions) that my extension is experiencing. Now I’m pretty sure that these wouldn’t be really crashing Visual Studio, but they would be affecting how well the extension works.
There’s now a number of issues created in the Github repo for me to review:
- Fix crash in Microsoft.VisualStudio.Diagnostics.Common.Check.Throw[XT]
- Fix OverflowException in System.Convert.ToUInt32
- Fix AggregateException in Microsoft.CodeAnalysis.Workspace.CheckDocumentIsInCurrentSolution
- Fix AggregateException in Microsoft.CodeAnalysis.Workspace.CheckProjectIsInCurrentSolution
- Fix AggregateException in Microsoft.VisualStudio.Alm.Roslyn.Server.RemoteInterop.RemoteProjectInfo.ToProjectInfo
- Fix AggregateException in Microsoft.VisualStudio.Alm.Roslyn.Server.OutOfProcessWorkspaceServer.FindReference
- Fix AggregateException in System.IO.__Error.WinIOError
- Fix AggregateException in System.Threading.CancellationTokenSource.ThrowObjectDisposedException
- Fix crash in
Microsoft.PowerToolsEx.BlockTagger.Implementation.CsharpVBBlockParser.<GetandParseSyntaxNodeAsync>d\_\_1.MoveNext
- Fix XmlException in System.Xml.XmlTextReaderImpl.Throw
- Fix crash in System.Threading.Tasks.Dataflow.Internal.Common.InitializeStackTrace
- Fix AggregateException in JetBrains.Application.InterruptableActivityCookie.CheckAndThrow
A bit of work for me to do now 😀 Hopefully not too much - I suspect a few of can all be handled in a similar way. Interesting too to see all the different ways your own code can interact with other parts of both Visual Studio and other extensions!
Actually after a bit of investigation, I have a theory that most of these exceptions are nothing to do with me. When I configured HockeyApp, I used the RegisterDefaultUnobservedTaskExceptionHandler(). I suspect this was causing HockeyApp to capture any unobserved Task exception that happened in Visual Studio – not just relating to my extension. I guess if there was a way to get HockeyApp to include only those exceptions based on the extension’s namespace that would be more useful.
-
Using HockeyApp to instrument a Visual Studio extension
I’m working on a new Visual Studio extension and I wanted to include some automatic analytics/crash reporting. Previously I would have looked at Visual Studio Application Insights, but over the last few months Microsoft have been transitioning developers of mobile and desktop apps to use their HockeyApp platform.
Assuming you’ve signed up for HockeyApp (or as in my case, had an existing Insights app on Azure that was migrated over to HockeyApp), you are ready to start using HockeyApp.
There is a choice of NuGet packages to choose from, depending on the kind of application you’re building. Nothing specifically for a Visual Studio extension, so the next best fit seemed to be HockeySDK.WPF. After all, Visual Studio has WPF in it, so that should work, right?
Not quite. The WPF package really assumes that you’ve got your own complete WPF application, not just an extension assembly being hosted in another application.
I encountered a couple of issues.
TrackException vs HandleException
The
HockeyClient.Current
property exposes anIHockeyClient
interface. Browsing through the methods exposed on this interface I noticedTrackException
. Perfect, I’ll use that in a few strategic try/catch blocks in the application. Just to test things, I added athrow new InvalidOperationException()
.Running the extension under the debugger gave a surprise – I was getting a
KeyNotFoundException
being thrown from inside that TrackException method call!A request for help from the HockeyApp forums pointed me in the right direction. There’s a WPF sample on GitHub. Reviewing the source code there revealed that they’re using a
HandleException
method which exists onHockeyClient
, but not onIHockeyClient
. Ok, let’s use that then, and we’re all good right?NullReferenceException in HockeyPlatformHelperWPF.get_AppVersion
Not quite, again!
This time, I was getting another exception being thrown inside the
HockeyPlatformHelperWPF.AppVersion
property getter. The SDK itself is also on GitHub, I had a look at the property body. Curiously most of it was already in a try/catch, so I suspected the part not working was in the actual catch block:catch (Exception) { } // Executing Assembly _appVersion = Assembly.GetCallingAssembly().GetName().Version.ToString(); }
Again, because we’re being hosted in another app, the code isn’t finding things as it was expecting. Fortunately it turns out that this code is only run if a field is null, otherwise it just uses the existing cached field value. Unfortunately there isn’t a public setter for that field, so reflection is the only option. Yes, it’s a bit dirty, but it gets the job done.
I added this code directly after I called HockeyClient.Current.Configure():
var hockeyClient = (HockeyClient)HockeyClient.Current; var helper = new HockeyPlatformHelperWPF(); var crashLogInfo = new CrashLogInformation() { PackageName = helper.AppPackageName, OperatingSystem = helper.OSPlatform, Windows = hockeyClient.OsVersion, Manufacturer = helper.Manufacturer, Model = helper.Model, ProductID = helper.ProductID, Version = Assembly.GetExecutingAssembly().GetName().Version.ToString() }; var field = typeof(HockeyClient).GetField("_crashLogInfo", BindingFlags.NonPublic | BindingFlags.Instance); field.SetValue(hockeyClient, crashLogInfo);
This code almost completely replicates the functionality found in the
HockeyClient.PrefilledCrashLogInfo
property, with the exception (ha ha) of the value assigned toVersion
. Because this code is now part of our extension assembly, I just useGetExecutingAssembly()
instead ofGetCallingAssembly()
.With these changes made, I’m pleased to report that my InvalidOperationException was properly reported and now appears in HockeyApp. Even nicer, because I configured HockeyApp to talk to GitHub, it automatically created an issue in my extension’s GitHub repository.
Very nice!
-
The changing state of installers
I’ve begun to notice that there’s somewhat of a gradual move away from MSI-style installers. This is an interesting change considering for a number of years now I’ve been a keen proponent of creating an MSI to package up your software and drive the installation process during deployment.
MSI (Windows Installer – previously Microsoft Installer) started life as the Office 2000 installer. Office was (and still is) quite a large and complex piece of software and I guess the development team realised they needed a slightly more reliable way to install all the bits in the right places.
This then evolved into a more general-purpose installer technology and was finally built right in to Windows itself as the preferred way to install applications and services.
So what’s changed?
MSI is great if you just need to install a single instance of an application. It is possible to handle multiple instances – for example SQL Server. If you’ve ever installed SQL you’ll know that you can use the setup program to install multiple instance of SQL Server. I recently learned through a question posed to a Reddit AMA that they use ‘Transforms’ for this. It is possible, but not super easy.
Then there’s Nano – Nano Server to be precise. This is the new super cut-down, minimalist edition of Windows Server, and one of the things that has been left out to keep it as lean as possible is MSI support. To build software for Nano, you’ll be packaging it up in an ‘appx’ file (essentially the same package used for Windows 8/10 UWP).
So who’s changed?
I noticed that JetBrains went to a non-MSI installer with their ReSharper 9.x release. They were already using NuGet packages for managing extensions. I think they’ve kind of gone the whole way with their installer now.
It also appears that Visual Studio “15” (not to be confused with the current 2015 release which is version 14) is moving to a non-MSI installer as part of reducing the install process.
ASP.NET for a while now has had the ‘msdeploy’ packaging. It’s what is used when you choose ‘Publish’ from the context menu in Visual Studio.
So what now?
MSI still has a role, especially for traditional server deployments, but if you want to support deployments to Nano then you’ll need to look at appx. If you have a desktop app that you’d like to feature in the Windows Store, then appx is also a requirement.
There’s currently a couple of options if you want to move towards appx deployments:
Project Centennial – aka the Desktop Converter for Win32 or .NET 4.6.1 apps. This is currently in preview (and requires that you run on a preview release of Windows 10).
WiX AppX Extension – a commercial product created by some of the main WiX developers that lets you leverage your existing WiX projects to produce an Appx package.