Details matter. Especially in building solutions people pay for. You've heard the term WYSIWYG. Well why isn't the term YGWYPF more prevalent? It stands for You Get What You Pay For, and it's truer in today's economy than most people realize. So where are the little details in server controls? What are the mundane, time-consuming details that set apart a proof-of-concept from a true solution? My last few articles have dealt with .NET development in general, but now it's time to get specific for server controls.
Registering in the "Add References" dialog
Hopefully, Microsoft will someday give control developers a programmatic way to accomplish registration in the "Add References" dialog. Controls in the global assembly cache (GAC) should be automatically referenced here, but they're not. To show up in this dialog, your assembly has to be referenced in a very specific registry key: HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders. From there, each assembly needs to have a key, using the format AssemblyName.MajorVersion.MinorVersion.Build.Revision. So, for GenX.NET 3.0, you'll see the entries in Figure A.
Each new key has to have a default string value equal to the location of the assembly. This brings up an interesting problem: How can you be sure where your assemblies are going to be upon installation? Well, there are four approaches to accomplish this:
- Install assemblies to a predictable location, and then manually add a registry key.
- Pass the installer target location to the manual registry key.
- Install assemblies to a predictable location, and then dynamically add a registry key using a custom installer action.
- Pass the installer target location to a custom installer action. and add a registry key dynamically.
I'm working on changing Interscape's current installation system to use custom installer actions, which are the more preferable option. Since we currently use the manual approaches, I'll detail them here, and save the other approaches for a future article.
Interscape used to take the simple approach, which is to always install a second copy of the assemblies in the [WINDIR]Program Files\Common Files folder, as shown in Figure B (corresponding folder locations are highlighted in yellow).
Like I said, this is not necessarily the best way, just the most predictable and the most manual. It also leaves the possibility of extra assembly copies floating around, which could be trouble later on.
Thanks to a comment on my weblog, Interscape now uses a simpler method of accomplishing this task. Since it is very similar to Xheo's approach, the example shown in Figure C is based off of the default installation of XHEO|Licensing 2.0.
Using this approach in your installer project, the default value of the registry key for your .NET 1.0 version would be [TARGETDIR]\v2.0\Redistributable\For .NET 1.0. Similarly, the default value for the 1.1 key would be [TARGETDIR]\v2.0\Redistributable\For .NET 1.1.
While it is a lot of work for you just to get some assemblies listed in the "Add References" dialog, the ease of use for your end users is well worth the extra effort. The end result is shown in Figure D.
Now, as you may have noticed, this system follows very closely my previous articles on versioning and compatibility. The point of all this is not to tell you that my way is the only right way of doing things or that I think I'm a genius or the smartest guy in the world for coming up with it. The point, instead, is to show you how to come up with a complete system and follow through with it. Experiment, improve, refine—remember you're not writing server controls to simply improve your own existence, but to ease the stress on others as well.
In a future article, I'll discuss improving usability through thorough and consistent documentation, which is probably the most time-consuming part of any decent API or control. This is also one of the main reasons why GenX.NET 3.0 took so long to complete. Your component is only as good as its documentation, and, remember, neatness counts. Until next time, keep coding!