12

我刚刚将包含 WinForms、通用库和 Web 应用程序的 VS 2008 解决方案升级到 VS 2010,但所有项目仍以 .NET 3.5 SP 1 为目标。我使用这种技术为我的通用库生成 XmlSerializer。WinForms 应用程序运行良好。当我的 Web 应用程序尝试使用这些引用相同 XmlSerializer 的库运行时,它会抛出以下内容:

“/WebSubscribers”应用程序中的服务器错误。无法加载文件或程序集“Ceoimage.Basecamp.XmlSerializers”或其依赖项之一。此程序集由比当前加载的运行时更新的运行时构建,无法加载。说明:执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。

异常详细信息:System.BadImageFormatException:无法加载文件或程序集“Ceoimage.Basecamp.XmlSerializers”或其依赖项之一。此程序集由比当前加载的运行时更新的运行时构建,无法加载。

我使用.NET Reflector查看了 XmlSerializer 的引用,发现它引用了 .NET Reflector 的 2.0 和 4.0 版本mscorlib以及System.Data.Linq. 奇怪的是,它只使用了 4.0 版本的System.Xml. 那可能是我的问题。

如何使用这些 XmlSerializer 让 Web 应用程序运行?当我简单地删除那些 XmlSerializer 时,Web 应用程序运行良好。这是一个选项,但如何强制 MSBUILD 为特定版本的 CLR 创建序列化程序?

这是我添加到项目文件中的 MSBuild 任务,它强制创建 XmlSerializer:

<Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)" Outputs="$(OutputPath)$(_SGenDllName)">
 <Delete Files="$(TargetDir)$(TargetName).XmlSerializers.dll" ContinueOnError="true" />
 <SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)" References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="$(SGenToolPath)">
  <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
 </SGen>
</Target>
4

3 回答 3

8

MSBuild 4 将(应该...)使用 3.5 工具来构建 3.5 项目。但是,它似乎无法确定 3.5 工具在哪里以及正在使用 4.0 工具。结果是它正确构建了您的 3.5 项目(使用 CLR 2.0.50727 程序集),但 4.0 sgen.exe 工具正在生成 Ceoimage.Basecamp.XmlSerializers.dll 作为 CLR 4.0.30319 程序集。

MSBuild 使用注册表来获取 v3.5 工具的路径。如果无法识别 3.5 工具的路径,则需要 v3.5 SDK 工具的 MSBuild 任务将回退到 v4.0 路径 - 查看用于在 C:\Windows\Microsoft 中设置 TargetFrameworkSDKToolsDirectory 属性的逻辑。 NET\Framework\v4.0.30319\Microsoft.NETFramework.props 如果你真的感兴趣的话。

您可以按如下方式诊断和修复可能的注册表问题:

安装 Process Monitor 并设置过滤器来监控 msbuild 的注册表访问(事件类:Registry,进程名称:msbuild.exe,所有类型的结果)

运行你的构建

搜索匹配“MSBuild\ToolsVersions\4.0\SDK35ToolsPath”的 RegQueryValue 访问的进程监视器。请注意,这可能位于“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft”或“HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft”下

如果您查看注册表中的这个键,您会看到它为另一个注册表值设置别名,例如“$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1\WinSDK-NetFx35Tools-x86@ InstallationFolder)" 此后不久,您可能会看到“未找到名称”结果,因为 msbuild 尝试从指定的键中加载值。

应该清楚您需要从这里添加/修改哪些键。

注册表值错误有几个可能的原因。就我而言,Microsoft SDK v7.1 安装的问题意味着注册表项的名称不正确,此处已将其识别为错误:

http://connect.microsoft.com/VisualStudio/feedback/details/594338/tfs-2010-build-agent-and-windows-7-1-sdk-targeting-net-3-5-generates-wrong-embedded-资源

于 2011-04-18T12:34:25.257 回答
2

您是否依赖任何特定于 4.0 的东西?

如果您调用 MSBuild 4.0,您将获得 4.0 工具。如果您调用 MSBuild 3.5,您将获得 3.5 工具(这是您想要的,因为您显然托管在 2.0 CLR 中)。

另一种选择是将 4.0 CLR 放在您的 Web 服务器上。如果它没有打开,那么您的流中不应该有任何 4.0 目标内容。

于 2010-08-24T08:40:09.923 回答
2

我发现我可以明确指定 SGEN 任务的工具路径以使用 3.5 版本,如下所示:

<SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)" References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin">
于 2010-08-25T14:55:01.900 回答