If your build fails with “MSB6002: The command-line for the “ResGen” task is too long”

If you have the RC build of VS2010, you are targeting the 3.5, 3.0, or 2.0 Framework, you have a VB or C# project which has a lot of references, and you also have a lot of .resx files, your build may break with an error message like this

D:\Windows\Microsoft.NET\Framework\v4.0.30128\Microsoft.Common.targets(1835,9): warning MSB6002: The command-line for the "ResGen" task is too long.
Command-lines longer than 32000 characters are likely to fail. Try reducing the length of the command-line by breaking down the call to "ResGen" into multiple calls with fewer parameters per call. [D:\Users\danmose\Documents\Visual Studio 2010\Projects\WindowsForms Application3\WindowsFormsApplication3.csproj]
D:\Windows\Microsoft.NET\Framework\v4.0.30128\Microsoft.Common.targets(1835,9): error MSB6003: The specified task executable "ResGen.exe" could not be run. The filename or extension is too long [D:\Users\danmose\Documents\Visual Studio 2010\Projects\resgen\WindowsFormsApplication3\WindowsFormsApplication3.csproj]

This bug was reported in Beta 2, but unfortunately the fix we made in time for the RC release only fixed the most common case. The other case will be fixed for the final RTM release.

Meanwhile, here’s some workarounds to get you unblocked while you use the RC build. Essentially the problem is the combined length of the paths to all your references isn’t being included when we figure out whether to break up the inputs over more than one resgen.exe command. So what we want to do is make those paths shorter. There’s three options:

(1) Retarget the project to 4.0 until RTM

Obviously, if you’re working on a serious project, that’s likely not acceptable, but if you’re just experimenting with the RC, you might be okay with that, and it’s easy to do. Just open up your project properties, and change the dropdown (see image)

(2) If that’s not possible, remove any references your project has that it doesn’t actually use in the code. That might reduce the length of the command line just enough.

(3) If your build still fails, or there’s no references you can remove, this workaround should avoid the problem:

(a) Dial up the build verbosity to Normal in Tools>Options>Project>Build. Here’s the Options dialog with the dropdown circled. (You can set it back after you’re done applying the workaround)

(b) Build the project with the problem and look in the build log for the resgen.exe command line. You’ll see your references’ paths getting passed to resgen.exe. Find where the .NET Framework references are coming from. It’s probably something like "C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client”. Copy that directory.

(c) In a console window, use subst to make an unused drive letter point there. For example.,

Subst j: "C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client”

(d) Back in VS, open up the project properties and look for Reference Paths. It’s in slightly different places for VB and C# projects:

VB

C#

Add “j:\” or whatever your subst drive was to the Reference Paths. (If there’s already existing entries, put it at the front.)

(e) Your build should now succeed. If it doesn’t, that means that your remaining references are still too long. You can do the same technique, using a different drive, to point to some place where several other references are

If you have more than one project with this problem, you may be able to avoid setting the ReferencePath on all of them if they don’t already have a ReferencePath. Simply set an environment variable ReferencePath with the value “j:\” and launch VS from that window. Those that do already have a ReferencePath will ignore this environment variable.

(4) If that’s still not enough, you can resort to extreme measures! This means modifying the GenerateResource target so that it doesn’t try to consume all those references at once, by modifying the condition in the CoreResGen target in Microsoft.Common.targets (find the line in bold below). This file is in your %windir%\microsoft.net\framework\v4.0.30128. Be sure to back up your copy – if you make a mistake, you won’t be able to build at all.

<GenerateResource
Sources="@(EmbeddedResource)"
UseSourcePath="$(UseSourcePath)"
References="@(ReferencePath)"
AdditionalInputs="$(MSBuildAllProjects)"
NeverLockTypeAssemblies="$(GenerateResourceNeverLockTypeAssemblies)"
StronglyTypedClassName="%(EmbeddedResource.StronglyTypedClassName)"
StronglyTypedFileName="%(EmbeddedResource.StronglyTypedFileName)"
StronglyTypedLanguage="%(EmbeddedResource.StronglyTypedLanguage)"
StronglyTypedNamespace="%(EmbeddedResource.StronglyTypedNamespace)"
StronglyTypedManifestPrefix="%(EmbeddedResource.StronglyTypedManifestPrefix)"
PublicClass="%(EmbeddedResource.PublicClass)"
OutputResources="@(EmbeddedResource->'$(IntermediateOutputPath)%(ManifestResourceName).resources')"
Condition="'%(EmbeddedResource.Type)' == 'Resx' and '%(EmbeddedResource.GenerateResource)' != 'false'"
SdkToolsPath="$(TargetFrameworkSDKToolsDirectory)"
ExecuteAsTool="$(ResGenExecuteAsTool)"
EnvironmentVariables="$(ResGenEnvironment)"
MinimalRebuildFromTracking="$(MinimalRebuildFromTracking)"
TrackFileAccess="$(TrackFileAccess)"
TrackerLogDirectory="$(TrackerLogDirectory)"
ToolArchitecture="$(ResGenToolArchitecture)"
TrackerFrameworkPath="$(ResGenTrackerFrameworkPath)"
TrackerSdkPath="$(ResGenTrackerSdkPath)">

<Output TaskParameter="FilesWritten" ItemName="FileWrites"/>
<Output TaskParameter="StronglyTypedFileName" ItemName="Compile"/>

<!-- Gather Sources as an output since it will contain OutputResource metadata indicating the final output resource that it was compiled into -->
<Output TaskParameter="Sources" ItemName="_Temporary" />

</GenerateResource>

One extreme would be to add and '%(EmbeddedResource.Identity)' != '' to the Condition – this has the effect of causing GenerateResource to batch on every single resource individually. However, given that you have enough resources to be hitting the 32000 character command line limit, you probably don’t want to be starting up and shutting down an individual ResGen.exe for every resource – it will work but be very slow. (That might be okay, if you’re building them once, then don’t plan to modify them again.)

A more reasonable solution, if it works with your project setup, is to add the clause and '%(EmbeddedResource.RelativeDir)' == '%(EmbeddedResource.RelativeDir)' instead. RelativeDir is a built-in metadata on all items that contains the directory part of the Include, so if you have your resources divided into several folders, GenerateResource would run once against each folder, instead of against all resources at once. This will solve the problem as long as you don’t have more than 32000 characters worth of resources in a particular folder. If not, you could move them into several folders to make this work.

If you have any trouble, please email me at danmose@microsoft.com

Dan Moseley
Developer Lead - MSBuild