UPDATE: This issue is fixed in .NET 4.5. As always, feedback is welcome! Please leave your comments in this blog post and report any bugs on Microsoft Connect.
Recently, we’ve seen a number of people reporting problems with resource generation in Visual Studio 2010, particularly when they target .NET Framework 3.5 on 64-bit machines. In this blog post, I will highlight one of the most common errors; when and where it occurs, and how to work around it. In a later post, we’ll take a look at some other somewhat less common errors, and some background on the differences between the GenerateResource of VS 2008 and that of VS 2010.
Sample error:
ResourceForm.resx(1436,5): error RG0000: Could not load file or assembly ‘file:///C:/Users/sjoiner/Desktop/TestForm/ResTest/bin/x86/Debug/Foo.dll’ or one of its dependencies. An attempt was made to load a program with an incorrect format. Line 1436, position 5.
Circumstances under which this error is found:
All of the below conditions must be met:
- 64-bit operating system
- Project that contains resource files (Project A)
- Assembly that is referenced by project A (Assembly B). This can be either a third-party assembly, or another project in your solution that is referenced via a project-to-project reference
- One or more of Project A’s resources files use types from Assembly B. For example, if Project A is a form and Assembly B contains controls used by that form.
- One of the ‘X’-marked combinations of configuration below must be true. Note: Building using the Visual Studio IDE is equivalent to building using 32-bit MSBuild.exe.
VS 2008 | VS 2010 Targeting 3.5 or below | VS2010 Targeting 4.0 | |||||
MSBuild.exe Architecture |
32-bit |
64-bit |
32-bit |
64-bit |
32-bit |
64-bit |
|
Assembly B’s Architecture | 32-bit | X | X | X | X | ||
64-bit | X | X | |||||
AnyCPU |
Workaround 1:
Make the assembly you’re referencing (Assembly B) AnyCPU rather than being architecture-specific.
Pros: Easy, means that Assembly B can be loaded by any referencing assembly.
Cons: Not feasible if you made the assembly you’re referencing architecture-specific for a specific reason, or if you’re referencing a 3rd party assembly that is architecture-specific.
Notes on Workarounds 2 and 3:
Workarounds 2 and 3 apply specifically to the scenario where a project targeting .NET 3.5 has a .resx file that references a 32-bit assembly. There is currently no workaround for targeting 4.0 and trying to reference a 32-bit assembly from 64-bit MSBuild or vice versa – aside from changing to use the MSBuild.exe of the architecture you’re targeting.
Please be aware that by forcing ResGen.exe to execute as 32-bit, you are essentially introducing the opposite problem: ResGen.exe will now error if you try to load 64-bit architecture-specific reference assemblies in it.
Workaround 2:
Make ResGen.exe 32-bit by:
1. Cd “%ProgramFiles(x86)%Microsoft SDKsWindowsv7.0ABin”
2. Corflags /32Bit+ /Force ResGen.exe
However, since our current build process has a heuristic that assumes that ResGen.exe is MSIL, just doing the above step will cause a different error (“The specified task executable “ResGen.exe” could not be run. The handle is invalid”) unless you inform GenerateResource that ResGen.exe is now 32-bit via setting the property “ResGenToolArchitecture” to “Managed32Bit”. This can be accomplished by doing one of the following:
1. Adding <ResGenToolArchitecture>Managed32Bit</ResGenToolArchitecture> to a PropertyGroup in the project file of any project that generates resources targeting .NET 3.5 – since the corflags trick affects the bitness of ResGen.exe on a system-wide level, the property must be set for all affected projects as well.
2. OR If running MSBuild.exe directly, passing it using the global property switch: ‘/p:ResGenToolArchitecture=Managed32Bit’
3. OR Setting it as an environment variable in the command window that MSBuild.exe is run from or the VS 2010 IDE is opened from: ‘set RESGENTOOLARCHITECTURE=Managed32Bit’
Pros: Doesn’t require changing the architecture of the referenced assembly
Cons: More complicated; requires baking the workaround either into the project file, or into your build system as a whole.
Workaround 3:
Force the CLR to load all MSIL applications as 32-bit by:
1. Cd “%windir%Microsoft.NETFramework64v2.0.50727”
2. Ldr64.exe setwow
Then, since this also is a way to force ResGen.exe to execute as though it’s a 32-bit process, you must also go through the second part of Workaround 2 (setting ResGenToolArchitecture=Managed32Bit).
Pros: Doesn’t require changing the architecture of the referenced assembly
Cons: Affects how the CLR works for the entire machine. Also the same cons as Workaround 2.
Explanation:
This problem occurs whenever an attempt is made to load a referenced assembly that is of an architecture that is incompatible with the architecture of the currently running process. Since framework reference assemblies are all AnyCPU – so they can be loaded by any architecture with no problem – this typically happens when you have two bit-specific user projects that reference each other, for example one with a form and one that implements a control, and the referencing project contains a resx that uses types defined in the referenced project. Obviously, the same problem also occurs if your projects reference 3rd-party assemblies that are architecture-specific.
This problem existed in VS 2008 as well, but it was a lot less noticeable because, since we generated resources in-proc in a typically 32-bit MSBuild.exe (64-bit MSBuild.exe existed, but was not frequently used), in order for this problem to be noticed, the user would have had to have referenced a specifically 64-bit assembly; and even then, a simple workaround would have been to just use 64-bit MSBuild.
However, in MSBuild 4.0, due to the fact that we use ResGen.exe when targeting .NET 3.5 or below, and due to the fact that the ResGen.exe associated with .NET 3.5 is an MSIL application, this error now occurs for anyone who targets .NET 3.5 on a 64-bit machine and has a resx that tries to load types from a specifically 32-bit assembly.
The first workaround removes the bitness mismatch by changing the referenced assembly to be loadable by any application. The second and third workarounds remove the bitness mismatch by making ResGen.exe 32-bit, so that it can successfully load 32-bit assemblies.
I hope this post has provided you with an improved understanding of this error and with the tools you need to work around it.
Sara Joiner
Software Development Engineer, MSBuild
0 comments