My process is similar to that of the other posters.
Say I have two projects, call them CoreProject and AppProject. CoreProject is shared. AppProject has a folder called SharedBinaries. This is where all the assembly references point to.
My TFSBuild script for CoreProject is configured to do the following:
-Get Latest
-Build Drop to drop zone (something like \\SERVER\DropZone\CoreProjectBuildNameAndNumber)
-Drop is copied to a folder in the drop zone (something like \\SERVER\DropZone\Latest\CoreProject)
The TFSBuild script for AppProject is configured to do the following:
-Get Latest
-Check out files in the SharedBinaries folder
-Copy files from \\SERVER\DropZone\Latest\CoreProject
-Build
-Drop to drop zone (something like \\SERVER\DropZone\AppProjectBuildNameAndNumber)
-If the build is successful the build is copied to a folder drop zone (something like \\SERVER\DropZone\Latest\AppProject) and the files in SharedBinaries are checked in
-If the build fails the files copied into SharedBinaries have the checkout undone.
I've found this works out really well. AppProject is always building with the most current bits from CoreProject, so we know right away if there is a breaking change. By having SharedBinaries checked into TFS I can get a specific version and run the code with the same dlls from CoreProject that were used at that time. Also I just have to get latest and my local machine is building with the latest bits as well.