net, build comments edit

When building a Sandcastle Help File Builder (SHFB) project in the GUI, you manually specify the list of assemblies you want to document. However, if you want to make the execution of it a little more flexible, you can do a bit of MSBuild magic to dynamically build up the list of documentation sources for the project prior to execution.

The reason this works is that SHFB has changed its command-line execution to run through MSBuild using build tasks rather than a standalone executable. (Though with the executable, you could go through some steps to fashion a response file, you can skip the temporary file creation now.)

First, create your SHFB project in the GUI. Set up all the various settings including a few assemblies and make sure it builds the documentation properly.

Next, run the SHFB build from the command line. SHFB projects are MSBuild projects now, so you can run them through MSBuild at a Visual Studio command prompt:

MSBuild Documentation.shfbproj

This establishes that everything is working properly from the command line. You’ll need that because when you make the project dynamic, you’ll have to run it from the command line.

Create a new MSBuild project file that you will use to dynamically build the list of documentation sources (assemblies/XML files) and add a target in it that will run the SHFB project.

If you are using SHFB 1.9.3.0 from .NET 4.0, you can use the MSBuild task directly, like this:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <Target Name="Build">
    <MSBuild ToolsVersion="4.0" Projects="Documentation.shfbproj" />
  </Target>
</Project>

On the other hand, if you’re using SHFB 1.9.1.0 from .NET 4.0, you’ll need to use the Exec task to run a .NET 3.5 MSBuild process manually, like this:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <Target Name="Build">
    <!--
      You have to use MSBuild 3.5 with SHFB or you get a warning
      telling you that any parameters you pass will be ignored.
    -->
    <Exec
      Command="&quot;$(windir)\Microsoft.NET\Framework\v3.5\MSBuild.exe&quot; Documentation.shfbproj"
      WorkingDirectory="$(MSBuildProjectDirectory)" />
  </Target>
</Project>

Execute your new MSBuild project and make sure the documentation still builds.

Open up the SHFB project in a text editor and find the DocumentationSources XML node. This is the bit that we’re going to make dynamic. The node in question looks like this:

<DocumentationSources>
  <DocumentationSource sourceFile="..\build_output\bin\Assembly1.dll" />
  <DocumentationSource sourceFile="..\build_output\bin\Assembly1.xml" />
  <DocumentationSource sourceFile="..\build_output\bin\Assembly2.dll" />
  <DocumentationSource sourceFile="..\build_output\bin\Assembly2.xml" />
</DocumentationSources>

The tricky part here is that DocumentationSources is a property, not an item, so when you make this value dynamic you actually need to build an XML string, not a list of files like you would for other tasks.

Remove the DocumentationSources node from the SHFB project file (or comment it out). We’ll be building that dynamically and passing it in as a parameter.

In your new MSBuild project file, use an ItemGroup to locate all of the .dll and .xml files that should be included in your documentation. These are the files you will have seen in the list of DocumentationSources:

<ItemGroup>
  <DocTarget Include="..\build_output\bin\*.dll;..\build_output\bin\*.xml;" />
</ItemGroup>

Use the CreateProperty task to build the XML string that contains each DocumentationSource node:

<CreateProperty Value="@(DocTarget -> '&lt;DocumentationSource sourceFile=%27%(FullPath)%27 /&gt;', '')">
  <Output TaskParameter="Value" PropertyName="DocumentationSources" />
</CreateProperty>

Finally, pass the list of DocumentationSources to the SHFB project when you run it.

If you’re using the MSBuild task:

<MSBuild
  ToolsVersion="4.0"
  Projects="Documentation.shfbproj"
  Properties="DocumentationSources=$(DocumentationSources)" />

If you’re using the Exec task:

<Exec
  Command="&quot;$(windir)\Microsoft.NET\Framework\v3.5\MSBuild.exe&quot; Documentation.shfbproj /p:DocumentationSources=&quot;$(DocumentationSources)&quot;"
  WorkingDirectory="$(MSBuildProjectDirectory)" />

A complete MSBuild script that uses SHFB 1.9.3.0 and the MSBuild task looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <Target Name="Build">
    <ItemGroup>
      <DocTarget Include="..\build_output\bin\*.dll;..\build_output\bin\*.xml;" />
    </ItemGroup>
    <CreateProperty Value="@(DocTarget -> '&lt;DocumentationSource sourceFile=%27%(FullPath)%27 /&gt;', '')">
      <Output TaskParameter="Value" PropertyName="DocumentationSources" />
    </CreateProperty>
    <MSBuild
      ToolsVersion="4.0"
      Projects="Documentation.shfbproj"
      Properties="DocumentationSources=$(DocumentationSources)" />
  </Target>
</Project>

Done! A dynamic Sandcastle Help File Builder project that you’ll never have to update as you add or change the assemblies you want documented.

web, gists, windows comments edit

I run in Windows as a non-admin user. Whenever I need to install something and Windows prompts for credentials, I have a whole separate user account that runs admin tasks.

This appears to be a problem for the automated Firefox software update process. What I run into goes something like this:

  1. Open Firefox and get notified there’s a software update.
  2. Click the button to close Firefox and apply the update.
  3. Firefox closes and prompts me for admin credentials, which I provide.
  4. The update installs, Firefox restarts, and then notifies me there is a software update.

What seems to be happening is that Firefox downloads the update for my non-admin user account, then when I provide admin credentials it re-downloads the update under that admin user account. After the update is done, Firefox still sees a pending update for my non-admin user account and wants me to apply it.

This happens with every update to the Firefox browser. Add-ons seem to update just fine, but the browser just can’t handle it.

Of course, if I try to apply the update with my non-admin user account, Firefox closes and re-opens without applying anything. Then it sees the pending update and wants to fake-apply it again.

The solution is to manually reset the automated Firefox software updater by removing all of the pending updates from the download cache.

Per this Mozillazine article, to do this:

  1. **Close Firefox. **
  2. Open the temporary application data folder for Firefox. In Windows Vista and above, this is: C:\Users\your-username-here\AppData\Local\Mozilla\Firefox\Mozilla Firefox

  3. Inside that folder, you’ll see a folder called “updates” and two files “active-update.xml” and “updates.xml” - delete the “updates” folder and the two XML files.

Aaaand… DONE. Next time you open Firefox it will do its automated check for updates and apply as necessary.

UPDATE: Here’s a batch file that does it on Windows 7 and Windows 2008.

@echo off
del "%localappdata%\Mozilla\Firefox\Mozilla Firefox\active-update.xml"
del "%localappdata%\Mozilla\Firefox\Mozilla Firefox\updates.xml"
rmdir /s /q "%localappdata%\Mozilla\Firefox\Mozilla Firefox\updates"

media comments edit

I have a lot of DVDs and they get stored in binders, but I don’t have so many Blu-ray discs and I like to keep them separate from the DVDs. I don’t rip the Blu-rays to my movie server, so it’s good to have them a little more accessible.

I had previously been storing them on a shelf like books in a library, but the shelf is out of space and things are looking bad. Time to find something else.

Trucking around The Container Store I found these DiscSox HiDef Pro Sleeves that are the perfect solution.

DiscSox HiDef Pro Sleeves and rack. Click to
enlarge.

These sleeves allow you to store two discs per sleeve and the cover is actually the cover from the Blu-ray case, so it’s a nice browsing experience. For the fewer-than-65 Blu-ray discs I have, this is great. Recommended.

net, aspnet comments edit

Ran into an interesting gotcha while working with routing and a handler on a web site. We had a route set up like this:

CustomHandler/{value}

Nothing too special, except the “value” route parameters are fairly long strings. In some cases 300 characters or more.

We were finding that in some cases things worked great, but in others IIS would return a 400 error claiming “invalid URL.”

The URL total length was less than 1000 characters, so it wasn’t that.

Turns out there’s a registry setting for indicating the maximum length of any individual path segment called “UrlMaxSegmentLength.” The default value is 260 characters. When our route value was longer than that, we’d get the 400 error.

The solution is to pass the value on the querystring rather than as a route value. Something to consider if you have route values that are going to be long.

Of course, this isn’t a problem with routing, just that you can get into the situation pretty easily when you get into the habit of pushing parameters into the route. It might be nice in future versions of routing to have some sort of max length check when generating full route URLs so you can’t generate a path segment greater than, say, 256 characters without getting an exception.