Troubleshooting a misbehaving designer in Visual Studio 2010
This article originally appeared on earthli News and has been cross-posted here.
Anyone who’s used Visual Studio 2010 for a non-trivial Windows Forms project has run into situations wherein the designer can no longer be opened. Usually, it’s because the class encounters null-reference exceptions when referencing data that is unavailable until runtime. Those are easy to fix: just avoid referencing that data in the constructor or load-routine while in design-mode.
However, sometimes Visual Studio has problems loading assemblies that it seems it should have available. Sometimes Visual Studio seems to have a devil of a time loading assemblies whose location it has quite explicitly been told.
If you like, there is a walkthrough—with screenshots!—at the end of this article, which shows how to solve even the most intractable designer problems.
A Tale of Two Platforms
One of the troubles is that many developers have moved to 64-bit Windows in order to take advantage of the higher RAM limits. The move to 64-bit causes some issues with many .NET assemblies in that the developer (i.e. probably YOU) didn’t remember to take into account that an assembly might be loaded by
x86 code or
x64 code or some combination thereof. The designer will sometimes be unable to load an assembly because it has been compiled in a way that cannot be loaded by the runtime currently being used by the designer as explicitly requested in the project settings. That said, the request is explicit as far as Visual Studio is concerned, but implicit as far as the developer is concerned.
The only long-lasting solution is to learn how assemblies are loaded and what the best compile settings are for different assemblies so that you will run into as few problems as possible.
There are several considerations:
- It would be nice to have class libraries that can be loaded by any executable instead of having separate versions for
- It would also be nice to be able to benefit from as many debugging features of the environment as possible (e.g. the Edit & Continue feature does not work with
- It would be nice to have the most optimal executable for the target platform. (This is usually taken to mean an executable compiled to run natively on the target, but turns out not necessarily to be so, as shown below.)
In order to help decide what to do, it’s best to go to the source: Microsoft. To that end, the article AnyCPU Exes are usually more trouble than they’re worth by Rick Byers (MSDN Blogs) provides a lot of guidance.
- “Running in two very different modes increases product complexity and the cost of testing”: two different platforms equals two times as much testing. Build servers have to compile and run tests for all configurations because there can be subtle differences.
- “32-bit tends to be faster anyway”: the current version of the WOW (Windows-on-Windows) runtime on 64-bit systems actually runs code faster than the native 64-bit runtime. That still holds true as of this writing.
- “Some features aren’t avai[l]able in 64-bit”: the aforementioned Edit & Continue counts among these, as does historical debugging if you’re lucky enough to have a high-end version of Visual Studio.
Given all of the points made above and assuming that your application does not actually need to be 64-bit (i.e. it needs to address more RAM than is available in the 32-bit address space), your best bet is to use the following rules as your guide when setting up default build and release settings.
- Pure class libraries should always be compiled for “Any CPU” (i.e. able to be loaded by both
- Executables should always be compiled as
- Unit-test assemblies should also be compiled as
x86in order to be able to use Edit & Continue.
Where Did You Find That?!
Once you’ve set up your build configuration appropriately and rebuilt everything, you will avoid many design-time errors.
Though not all of them.
Visual Studio has a nasty habit of loading assemblies wherever it can find one that matches your requirements, regardless of the location from which you linked in the assembly. If you look in the project file for a C# Visual Studio project (the
.csproj-file), you’ll actually see an XML element called
<HintPath> after each assembly reference. The name is appropriately chosen: Visual Studio will look for an assembly in this location first, but will continue looking elsewhere if it’s not there. It will look in the GAC and it will look in the
bin/x86/Debug folder to see if can scrounge up something against which to link. Only if the assembly is not to be found anywhere will Visual Studio give up and actually emit an error message.
At Encodo, we stopped using the GAC entirely, relying instead on local folders containing all required third-party libraries. In this way, we try to control the build configuration and assemblies used when code is downloaded to a new environment (such as a build server). However, when working locally, it is often the case that a developer’s environment is a good deal dirtier than that of a build server and must be cleaned.
Though Visual Studio offers an option to clean a project or solution, it doesn’t do what you’d expect: assemblies remain in the
bin/x86/Debug folders. We’ve added a batch command that we use to explicitly delete all of these folders so that Visual Studio once again must rely on the
HintPath to find its assemblies.
If you find yourself switching between
x64 assemblies with any amount of frequency, you will run into designer loading errors when the designer manages to find an assembly compiled for the wrong platform. When this happens, you must shut down Visual Studio, clean all output folders as outlined above and re-open the solution.
Including References with ReSharper
A final note on references: if you adopt the same policy as Encodo of very carefully specifying the location of all external references, you have to watch out for ReSharper. If ReSharper offers to “reference assembly X” and “include the namespace Y”, you should politely decline and reference the assembly yourself. ReSharper will reference the assembly as expected but will not include a
HintPath so the reference will be somewhere in the
bin/x86/Debug folder and will break as soon as you clean all of those directories (as will be the case on a build server).
This almost always works, but Visual Studio can still find ways of loading assemblies over which you have little to no control: the designer assemblies.
In all likelihood, you won’t be including the designer assemblies in your third-party binaries folder for several reasons:
- They are not strictly required for compilation
- The are usually a good deal larger than the assembly that they support and are only used during design-time
- Design-time assemblies are usually associated with visual component packages that must be installed anyway in order for a compiled executable to be considered licensed.
For all of the reasons above, it’s best not to even try to get Visual Studio to load designer assemblies out of a specific folder and just let it use the GAC instead.
Walkthrough: Solving a Problem in the Designer
Despite all of the precautions mentioned above, it is still possible to have a misbehaving designer. The designer can be so mischievous that it simply refuses to load, showing neither a stack not an error message, keeping its reasons to itself. How do we solve such a problem?
You know you have a problem when the designer presents the following view instead of your form or user control.
In the worst case, you will be given neither a useful error message nor a stack from which to figure out what happened.
There’s a little link at the top—right in the middle—that you can try that may provide you with more information.
The designer will try to scare you off one last time before giving up its precious secrets; ignore it.
At this point, the designer will finally show the warnings and errors that describe the reason it cannot load.
The text is a bit dense, but one thing pops out immediately:
It looks like Visual Studio is checking some cached location within your application settings to find referenced assemblies and their designer assemblies. This is a bit strange as Visual Studio has been explicitly instructed to load those assemblies from the third-party folder that we carefully prepared above. Perhaps this cache represents yet another location that must be cleared manually every once in a while in order to keep the designer running smoothly.
[A]DevExpress.XtraLayout.LayoutControl cannot be cast to [B]DevExpress.XtraLayout.LayoutControl. Type A originates from 'DevExpress.XtraLayout.v10.2, Version=10.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a' in the context 'LoadNeither' at location 'C:\Documents and Settings\Marco\Local Settings\Application Data\Microsoft\VisualStudio\10.0\ProjectAssemblies\kn8q9qdt01\DevExpress.XtraLayout.v10.2.dll'. Type B originates from 'DevExpress.XtraLayout.v10.2, Version=10.2.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a' in the context 'Default' at location 'C:\WINDOWS\assembly\GAC_MSIL\DevExpress.XtraLayout.v10.2\10.2.4.0__b88d1754d700e49a\DevExpress.XtraLayout.v10.2.dll'.
This will turn out to be a goose chase, however. The problem does not lie in the location of the assemblies, but rather in the version. We can see that the designer was attempting to load version 10.2.4.0 of the third-party component library for DevExpress. However, the solution and all projects were referencing the 10.2.5.0 version, which had not been officially installed on that workstation. It was unofficially available because the assemblies were included in the solution-local third-party folder, but the designer files were not.
Instead of simply showing an error message that the desired version of a required assembly could not be loaded, Visual Studio chose instead to first hide the warnings quite well, then to fail to mention the real reason the assembly could not be loaded (i.e. that it conflicted with a newer version already in memory). Instead, the designer left it up to the developer to puzzle out that the error message only mentioned versions that were older than the current one.
From there, a quick check of the installed programs and the GAC confirmed that the required version was not installed, but the solution was eminently non-obvious.
That’s about all for Visual Studio Designer troubleshooting tips. Hopefully, they’ll be useful enough to prevent at least some hair from being torn out and some keyboards from being thrown through displays.
GetHashCode(): the .NET implementation is optimized for speed, not portability so the codes generated by the 32-bit and 64-bit runtimes are different.↩