We have a single AssemblyInfoGlobal.cs file at solution level, like the other answers. Then we have as part of our build script a little application that takes the CCNetLabel that CruiseControl.net defines (and using the defaultLabeller to generate the labels) and checkout the global file, modify the version number, and then check the global file back in.
Some of our projects have multiple solutions, so the global file sits above the solution level in the directory structure, high enough so that everything that needs it is below it.
We also then update the deployment projects version numbers - the number for these is found within the VDProj file, but I'm not sure you will find these in the express versions - I think that's a full Professional upwards feature. And some projects have a dozen deployment projects, but no easy way to externalise the version numberf from the VDPROJ file itself. (or is there?)
Then the build happens which means that all the outputs from the build have exactly the same version number, at any time we can get the version from the MSI or assembly and we know exactly what source was used to build that output.