Category Archives: Version 7.2

.NET Reflector 7.2 has been released, and a .NET Reflector 7.3 EA build, including BAML decompilation, is now available

I’m very pleased to announce that .NET Reflector 7.2 has been released. You can get it via the main download link on the homepage at www.reflector.net.

Highlights include:

I’m also pleased to be able to announce the release of the first .NET Reflector 7.3 early access build, which includes BAML to XAML decompilation. This isn’t yet perfect but as you can see from this screenshot it works pretty well:

Reflector7_3EA1_BAMLtoXAML

(We’ll obviously sort out the formatting in due course.)

You can get the 7.3 EA build from the EA download link at the bottom of the www.reflector.net homepage.

Some changes to the object model for 7.2

We try to avoid making changes to the object model that Reflector exposes. Doing so requires our users to make changes to their applications forcing them to recompile. However, for 7.2 we decided that a change was the right thing to do in one particular small area of the object model.

The problem of the existing model was that it didn’t handle the difference between the two LINQ queries in the following code.

            var testData = new[]
                               {
                                   new Tuple<string,string>(“b”, “1”),
                                   new Tuple<string,string>(“a”, “3”),
                                   new Tuple<string,string>(“a”, “2”),
                               };

 

            var result =
                from datum in testData
                orderby datum.Item1
                orderby datum.Item2
                select datum;

 

            foreach(var item in result)
            {
                Console.WriteLine(item);
            }

 

            var result2 =
                from datum in testData
                orderby datum.Item1, datum.Item2
                select datum;

 

            foreach (var item in result2)
            {
                Console.WriteLine(item);
            }

 

Running the above code prints out the following results, which highlight the difference between multiple orderby clauses and a single orderby clause with multiple items.

(b, 1)
(a, 2)
(a, 3)
(a, 2)
(a, 3)
(b, 1)

The old Reflector object model couldn’t express the latter type of LINQ clauses, which were instead incorrectly represented as multiple orderby clauses. The IObjectClause had the following form, making it possible only to have a group of one item.

       public interface IOrderClause : IQueryClause
       {
              IExpression Expression { get; set; }
              OrderDirection Direction { get; set; }
       }

 

For 7.2 we experimented in one of the EAPs with adding a IThenOrderClause to express subsequent expressions that happen as part of the main sort. That would reflect the way 
           orderby A,B
is translated by the compiler into
            Enumerable.ThenBy(Enumerable.OrderBy(source, filter on A), filter on B).

 

However in the end we felt that since a ThenBy needs to follow an OrderBy, it was better to have just a single node type to express the entire part of the LINQ expression. With that in mind, we have now changed the IOrderClause interface to

 

       public interface IOrderClause : IQueryClause
       {
        IExpressionAndDirectionCollection ExpressionAndDirections { get; }
       }

This exposes a collection in the standard Reflector pattern leading to the individual items.

    public interface IExpressionAndDirectionCollection : ICollection
    {
        void Clear();
        void Add(IExpressionAndDirection value);
        void AddRange(ICollection value);
        void Remove(IExpressionAndDirection value);
        void Insert(int index, IExpressionAndDirection value);
        void RemoveAt(int index);
        bool Contains(IExpressionAndDirection value);
        int IndexOf(IExpressionAndDirection value);
        IExpressionAndDirection this[int index] { get; set; }
    }

 

    public interface IExpressionAndDirection
    {
        IExpression Expression { get; set; }
        OrderDirection Direction { get; set; }
    }
 

As usual, we welcome any feedback on the change we have made. 

Supporting add-ins across multiple versions of Reflector

We’ve recently been working on an add-ins page to go on this site, which has been dangerously close to nearly ready to publish – forgive me if I sometimes sound a little jaded – for a few weeks now.

We’ve also been receiving a fair few questions about v7 add-in compatibility. I’ve finally had five minutes to think about this properly today and quickly came to the conclusion that every solution we’d considered thus far sucks balls in one way or another.

So here are some facts:

  • Users don’t care what version of Reflector they’re running, they just want their add-ins to work… and they want them to carry on working when they upgrade.
  • Add-in authors don’t want, and for the most part don’t need, to maintain different versions of their add-ins for different versions of Reflector.
  • We have, as far as possible, tried to change the add-in API only by adding new interfaces, not by changing existing ones (there are a couple of cases in the engine where that hasn’t been possible, but I’ll leave Clive to explain that).

The upshot is that most of the actively maintained add-ins should work with Reflector 7 unmodified with no problems, or at least with no more problems than they would under Reflector 6 – and remember, for those of you running .NET Reflector 6.8, the add-ins are still available, and it’s still possible to create new add-ins for 6.8.

The fix, which I implemented today, is actually pretty simple – albeit a bit grubby. Check this out:

using System;
using System.Reflection;
 
namespace Reflector
{
 
	/// <summary>
	/// Helper class for supporting Reflector 5 add-ins without requiring them to be recompiled.
	/// This is really simple: all it does is examine assembly resolution requests and, if a request
	/// is made to resolve Reflector 5.0.0.0, just returns a reference to the main Reflector 7
	/// assembly.
	/// </summary>
	internal class Reflector5AddinSupportAssemblyResolver
	{
		private static readonly byte []	Reflector5PublicKeyToken	= new byte[] { 0x18, 0xca, 0x6b, 0xb8, 0xdd, 0x6a, 0x03, 0xc3 };
 
		private static bool PublicKeyTokenMatchesReflector5s( byte [] possibleMatchingToken )
		{
			if ( possibleMatchingToken.Length != Reflector5PublicKeyToken.Length )
			{
				return false;
			}
 
			for ( int index = 0, size = Reflector5PublicKeyToken.Length; index < size; ++index )
			{
				if ( possibleMatchingToken [ index ] != Reflector5PublicKeyToken [ index ] )
				{
					return false;
				}
			}
 
			return true;
		}
 
		internal static Assembly ResolveAssembly( object sender, ResolveEventArgs args )
		{
			string	fullName	= args.Name;
			if ( null == fullName )
			{
				return null;
			}
 
			AssemblyName	name	= new AssemblyName( fullName );
			if ( name.Name == "Reflector"
				&& name.Version.Major == 5
				&& name.Version.Minor == 0
				&& name.Version.Build == 0
				&& name.Version.Revision == 0
				&& PublicKeyTokenMatchesReflector5s( name.GetPublicKeyToken() ) )
			{
				return typeof( IConfigurationManager ).Assembly;
			}
 
			return null;
		}
	}
}

 

And then I just insert this into the Reflector start-up sequence and jobs a good ‘un:

AppDomain.CurrentDomain.AssemblyResolve	+= Reflector5AddinSupportAssemblyResolver.ResolveAssembly;

Yep, that’s right, all this code does is return a reference to the main Reflector assembly when something (anything) asks for a reference to Reflector 5.0.0.0. I told you it was grubby, didn’t I?

The important thing is that it works, and it does so without the need for fragile config files, or multiple builds, or any other half baked solution that requires either the add-in authors, or you in turn to do something half-arsed to get add-ins working.

What it means is that, in theory, any add-in written for .NET Reflector 5.x, or 6.x will work in .NET Reflector 7.2.0.130 or later. It’s still possible to create add-ins that only work in .NET Reflector 7 or later, if you want to take advantage of some of the new API additions, which I’ll talk about in another post.

Let me emphasize once again that this isn’t absolutely foolproof. As I’ve said, we’ve tried to avoid changing interfaces, and have instead mostly derived new ones. In one or two cases, that shouldn’t affect most add-ins, this isn’t so. We’ve also tried to avoid changing behaviour so, for example, even though the decompilation view supports tabbing, if you’re using the add-in API, it’ll still look and act like the old decompilation view that could only hold one thing at a time. Again, it maybe that in all cases we haven’t succeeded, although I’m not aware of any specific problems at the moment.

So, if you want to be able to use all your add-ins against .NET Reflector 7, download the latest early access build from the bottom of our homepage at http://www.reflector.net/.

Enjoy!

You want speed? We got speed.

OK, so after the rather miserable conclusion to my last blog post on the topic I’m pleased to say that we finally got to the bottom of the start-up performance problems. Annoyingly it largely had naff all to do with the start-up code itself, but hey, that’s all nicely optimised now so, apart from a bit of WPF overhead that we just can’t shift, it’s all good.

No, what it turned out to be was an errant protection setting on the assembly that was causing hundreds and thousands of strong name signature verifications. A single verification is fast, but thousands… well, that’s noticeable on even the most capable of desktop machines. Anyway, we turned the setting off, which instantly fixed the problem – I gently cursed the injustice of the world, and we put out a new fixed build, which has now been out for some while. I’m slightly paranoid so wanted to wait a bit and see if everything really was working properly before crowing about it on here.

Anyway, if you need the fix, you can get it as ever in the latest early access build from the bottom of our homepage at http://www.reflector.net.

We’ll put this out as part of the 7.2 final release hopefully next week, although it may slip into the following week.

.NET Reflector 7 Start-up Time: Running Out of Gas, or Pedal to the Metal?

There’s real satisfaction to be had in debugging and fixing performance problems. They’re such a potent source of irritation to users that there’s often a certain amount of kudos on offer if you manage to fix one. And fortunately nowadays there are many tools that make this easier – although I’ve always felt it’s important to keep users in the dark about exactly how much easier (you never know when that kudos is going to come in handy). Often you can see where the problem lies as soon as you open the results. My favourite performance tool is (obviously, usual disclaimers apply, blah, blah, etc.) ANTS Performance Profiler.

It’s easy to find yourself expecting that all performance problems will be similarly easy, so it comes as rather a disappointment when they’re not, and you actually have to do some real work to see any improvement. Sometimes you find yourself having to make changes in several places to shave execution time from different activities, which can become quite involved.

A case in point is the start-up time of .NET Reflector 7, which has been the crab infestation in the groin of my development schedule for at least two weeks now and which, frustratingly, I still don’t feel I’ve truly understood.

Of course, one starts off with the cheap solutions and the usual suspects.

The cheap solution relates to an issue caused by assemblies on network shares that, for whatever reason, aren’t available when you open up .NET Reflector: there’s this appalling delay before you can use Reflector whilst it waits for a network timeout. And the solution? For now, just don’t do that. Unfortunately this isn’t the problem people have been experiencing.

Moving on to the usual suspects: our check for updates and licensing components.

Check for updates does exactly what it says on the tin – phones home to find out if a new version of the software is available. (Don’t worry: it doesn’t tell us anything about you, other than which version you’re licensed for so we can tell you which updates are available for free.) This happens on start-up and, because it means involving t’internet (which might or might not be accessible), it really should happen on a background thread so it doesn’t get in the way. This was the case, but it turns out the call was being marshalled onto the thread and executed synchronously, for reasons lost in antiquity, which explains why it was the biggest performance bottleneck I could see when I ran ANTS Performance Profiler over our start-up code. Nice easy fix though!

I also discovered a bug in licensing, fixed in our other products, that can really slow down start-up on some machines due to a faulty network configuration. Again, not difficult to fix.

So, did this work? Not so far. The check for updates issue was fixed for 7.1, but licensing wasn’t. Nevertheless, as you’ll see if you read the discussion at http://forums.reflector.net/questions/589/startup-time it doesn’t look like either of these was the problem.

Time to fire up the profiler again…

I managed to identify several problem areas from the results over several runs:

  1. WPF application initialisation
  2. Creation and population of the ElementHost control, used to host WPF elements
  3. Unnecessary update and redraw of the assembly browser tree
  4. Unnecessary decompilation with multiple tabs open

I managed to improve (1) by moving WPF App creation onto a background thread. This is quite an expensive constructor call, as you can see from Figure 1, and is necessary so that the WPF resources (BAML, etc.) are available when adding WPF elements to a WinForms ElementHost control. I posted about this on StackOverflow a while back at http://stackoverflow.com/questions/4326621/wpf-control-not-displaying-in-elementhost-in-winforms-app. Fortunately this is a one-time cost.

Figure1_WpfInitialisation

Figure 1. This illustrates the cost of creating a new WPF App instance. Originally this was called without locking from the main thread once as almost the first start-up action. This method is now invoked on a background thread at start-up, and then by anything that creates a WPF element so that, if App creation is incomplete, the main thread will block before element creation until it is complete. Incidentally this illustrates how the average time for a line of code can be misleading – in reality the check to see if the App has been created is only expensive the first time it’s performed, as can be clearly seen from the call graph in Figure 2, which illustrates both execution paths calling this method.

Figure2_WpfInit2

Figure 2. Here you can see that it’s the first call to EnsureMergeWpfApplicationResources() that costs so much – the second call is close to instantaneous. You can also see the significance of the contributions from BAML loading and JIT compilation. You have to wonder how much quicker it would be to get rid of the middle man and turn the BAML into IL, a la WinForms, so it doesn’t need interpreting at runtime. You’d still have the JIT cost of course, but that’s a rant for another day.

Unfortunately there’s not much I can do about (2) because it’s just creating a control that is visible almost immediately upon startup, so it’s not as though I can defer it until later. It also needs to happen on the main thread. I hope that the cost of creating ElementHost controls isn’t a linear relationship proportional to the number of them you create, otherwise this could get really ugly as we replace more bits of the WinForms UI with WPF elements that, for now, will need to be hosted in ElementHost controls. There will come a point where we can flip over to all-WPF for the main UI, and this problem will disappear, but that’s not likely to happen for a while, and it’s not a topic the WPF books cover!

I wonder if (2) contributes to WPF’s reputation for being slow. I think the charge overall is unfair – WPF is certainly quicker than WinForms when comparing like for like interfaces and controls, but perhaps because you can do more with WPF the tendency is to take advantage of that, go overboard with your UI design, and then run into performance problems. You might notice that I’ve slightly de-chromed the tab interface in comparison to 7.1, which should help here too. This wasn’t a massive problem, but it was potentially distracting. I suspect that, because there are often multiple ways of solving a problem with WPF, it’s all too easy to pick a suboptimal solution that underperforms.

Back on message again…

(3) was an easy fix, once I could see it was happening. The tree now generally only updates and repaints once, when all assemblies having finished loading. The only time this might not happen is if an individual assembly took more than a couple of seconds or so to load, in which case there might be an intermediate update. We also still force updates when assemblies are removed, but this obviously doesn’t affect start-up time.

I thought I’d managed to stop (4) some time ago, but either I didn’t or it had been broken again subsequently. This caused a decompilation event for every open tab, which was particularly apparent if you’d chosen to persist tabs when you closed Reflector. Now only the item in the selected tab is decompiled. As a result, loading multiple tabs on start-up and refresh is now very fast, since all we do is populate the tab headers, not the tabs themselves.

All of this has shaved the odd second off, here and there, and has cut start-up time roughly in half for me. It now takes between 3 and 5 seconds to open Reflector, with about 25 assemblies in the list, and 30 or 40 tabs open (obviously a bit excessive), obviously depending upon what else my machine is doing.

OK, so this is all great, but do I think it’s got to the root of the problem reported here?

http://forums.reflector.net/questions/589/startup-time

Not so much. I’d be surprised if there was no improvement but at the same time I can’t help thinking there’s some pathological edge case here that we haven’t yet been able to identify. Thus far nothing in the configuration files from these users indicates anything amiss: no huge lists of assemblies, no misbehaving add-ins, no references to anything on network shares – in fact nothing interesting whatsoever.

We’ll find out soon enough: I’ve just uploaded a new .NET Reflector EA build, which you can get from the www.reflector.net homepage.

This all might sound a little negative, but it’s really not: I’m confident we’ll get to the bottom of the problem, it might just take some time. Once I understand what’s going wrong I suspect the fix will be relatively straightforward.

Life is a journey, not a destination – .NET Reflector 7.2 EA 1 has been released

I’m very pleased to announce that the first early access build of .NET Reflector 7.2 is now available. You can get it from the .NET Reflector homepage at:

http://www.reflector.net/

Let me highlight a couple of the more important items before giving you a full breakdown of the work that’s gone into this release.

Firstly, if you’ve been following the forum at all, you’ll no doubt have noticed the discussion about startup time at http://forums.reflector.net/questions/589/startup-time. It seems that a few users are experiencing very long startup times. There are a couple of potential issues here. The first manifests itself on machines on networks with high latency – it’s related to our licensing code, but I’ve put a fix into this EA build, so if this is affecting you, you should see an improvement. This doesn’t fix the problem for all affected users though, and it seems that in these cases the issue may relate to running in a non-English locale. I’m hoping we’ll be able to get this isolated and fixed as well sometime next week.

Secondly, I’ve now switched on all PowerCommands by default. This isn’t likely to be the way .NET Reflector 7.2 ships, but it’s a great way to quickly shake out problems in the meantime. I’ve already fixed most of the PowerCommands related bugs we were aware of but there obviously could be more problems.

If you do see the Error Reporting dialog box for any reason, I urge you, please submit the error report and we’ll try to get the problem fixed as quickly as possible. We can’t always fix everything because sometimes even an error report doesn’t contain enough information to get to the bottom of a problem, but they do make it significantly easier to find and fix bugs that occur in the field.

And finally, breaking with tradition, here’s an (almost) exhaustive changelog for this release:

  • Fixed problem where invalid path characters in resource name caused ArgumentException in PowerCommands’ “Open With Paint…” command (bug RP-1357)
  • Handle common I/O problems in PowerCommands “Open With Paint…” command and, where necessary, report to user instead of crashing Reflector
  • All PowerCommands invoking an external process, such as “Open With Paint”, now invoke that process with error handling and report errors to the user without crashing Reflector
  • Temporary files can now be managed through the ITemporaryFileService, which is registered in Reflector’s IServiceProvider – this is available to add-ins; files and directories managed by this service should be cleaned up on graceful exit; files and directories are created in the user’s TEMP folder, although the value of the RGTEMP environment variable is also respected
  • PowerCommands’ “Open With Paint” command now uses ITemporaryFileService to create image file opened by Paint – avoids filename conflict issues
  • PowerCommands’ “Open XAML With” command now checks and generates filenames safely and uses ITemporaryFileManager to create temporary files
  • Fixed SEHException in Visual Studio add-in when attempting to retrieve assembly version from reference in Visual Studio project – now attempts to retrieve version from assembly name in reference identity when this happens (bug RP-1484)
  • “Open XAML in Notepad” and “Open XAML in Visual Studio 2010” commands now check and generate filenames safely and use ITemporaryFileManager to create temporary files
  • Fixed rare NullReferenceException during tab closing animation (bug RP-1486)
  • Fixed AccessViolationException when populating browser control with “about:blank” page (bug RP-1491)
  • Fixed InvalidOperationException caused when trying to report translation failure when decompiling code (bug RP-986)
  • Fixed ArgumentNullExceptions caused by inappropriate null assembly references in Visual Studio add-in (bug RP-990)
  • Fixed crash caused by attempt to generate PDB file that is already in use (bug RP-1331) – now shows error message user rather than keel over
  • Fixed crash caused by Win32Exception when PowerCommands launches an external process (bug RP-868)
  • Fixed crash caused by Win32Exception when PowerCommands “Open ILDasm” command executed (bug RP-1388)
  • Added option to run updates as administrator (or not) to prevent update failures when installing into “C:\Program Files”, or other locations for which elevation is required
  • Assembly browser will now strip enclosing quotes from assembly pathnames where these have somehow been included (bugs RP-1502 and RP-1503)
  • Reflector will no longer try to decompile assembly details for assemblies where an error occurred during loading (bugs RP-1502 and RP-1503)
  • Fixed several second delay when retrieving license on machines with borked DNS or slow network connections (bug LC-232)
  • Fixed crash caused by XmlException when importing query from corrupt XML file (bug RP-1306)
  • Fixed crash caused by Win32Exception when opening Reflector StackOverflow tag page (bug RP-872)
  • Fixed crash caused by Win32Exception when sending code by email (bug RP-924)
  • Fixed crash caused by ExternalException when using “Copy As” PowerCOmmand to copy text as plain text, RTF, or HTML (bug RP-921)
  • Fixed a couple of crashes caused by unhandled BadCrcExceptions (bug RP-1389) and BadReadExceptions (RP-918) when working with ZIP files
  • Fixed crash caused by InvalidOperationException thrown when an attempt is made to compile a PowerCommands query when no C# compiler is available (bug RP-1018)
  • Fixed crash caused by NullReferenceException when attempt made to create code:// URL with null value (bug RP-1307)
  • Fixed NullReferenceException in Visual Studio add-in assembly explorer (bug RP-1294)
  • Fixed crash caused by OverflowException when a ridiculously large value was supplied for the code viewer indentation (bug RP-1300)
  • Applied a practical limit of 128 as the maximum permitted indentation, although it’s still possible to specify a larger value – if this is the case the larger value will be ignored and a value of 128 will be used
  • Fixed NullReferenceException when toggling state of “Toggle assembly browser” PowerCommand (bug RP-942)
  • Fixed ArgumentException when setting font in enum viewer when system fonts missing (bug RP-983)
  • Fixed IOException in Visual Studio caused by concurrent modification of files by add-in running in another Visual Studio instance when re-enabling PDB files (bug RP-1302)
  • Added handler and friendly report for user for FileNotFoundException when assembly for type required to instantiate resource cannot be found for conversion to RESX (bug RP-987)
  • Fixed NullReferenceException during query results output (bug RP-919)
  • Can now select a tab representing a decompiled namespace and the assembly browser tree will update to select that namespace (part of work required to fix bug RP-1003 – hitting refresh causes assembly browser tree to lose current position)
  • Selected item now restored in assembly browser tree after the tree has been refreshed (bug RP-1003)
  • Correctly restores tabs and selected item after options edited (bug RP-1292)
  • No longer closes all windows on Refresh – if a window supplies an IRefreshSupport (exported as part of add-in API) implementation it can optionally be kept open across a refresh (bug RP-996); this is implemented by the Disassembler, Search, and Analyser windows, however since the analyser will be cleared after a refresh this window still closes itself; the Search window will re-run any active search after the refresh has completed

Please do post any feedback you have in the forum at http://forums.reflector.net/ using the #7.2 and #EAP tags, and thanks for taking the time to use this early access build. We hope you enjoy using it!

Until next time!