msbuild icon indicating copy to clipboard operation
msbuild copied to clipboard

ProjectInstance.FromFile throws InvalidProjectFileException when 'Windows-1252' is specified in xml header

Open AArnott opened this issue 2 years ago • 3 comments

Issue Description

When a valid project file starts with this line:

<?xml version="1.0" encoding="Windows-1252"?>

MSBuild can build it, but the ProjectInstance.FromFile throws an InvalidProjectFileException.

Steps to Reproduce

Add <?xml version="1.0" encoding="Windows-1252"?> to the top of any project file, then try to load it with code such as:

using ProjectCollection projectCollection = new();
EvaluationContext evaluationContext = EvaluationContext.Create(EvaluationContext.SharingPolicy.Shared);
ImmutableDictionary<string, string> traversalProperties = ImmutableDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase);
    ProjectOptions projectOptions = new()
    {
        EvaluationContext = evaluationContext,
        GlobalProperties = globalProperties,
        LoadSettings = ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports,
        ProjectCollection = projectCollection,
    };
    ProjectInstance projectInstance = ProjectInstance.FromFile(projectPath, projectOptions);

Expected Behavior

No exception, and a valid graph.

Actual Behavior

Microsoft.Build.Exceptions.InvalidProjectFileException: The project file could not be loaded. 'Windows-1252' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter 'name')  c:\vs.r\src\vset\QTools\suitesrc\Execution\Agent\agentservice\agentservicetests.csproj
 ---> System.ArgumentException: 'Windows-1252' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter 'name')
   at System.Text.EncodingTable.InternalGetCodePageFromName(String name)
   at System.Text.EncodingTable.GetCodePageFromName(String name)
   at System.Text.Encoding.GetEncoding(String name)
   at Microsoft.Build.Internal.XmlReaderExtension..ctor(String file, Boolean loadAsReadOnly)
   at Microsoft.Build.Construction.ProjectRootElement.LoadDocument(String fullPath, Boolean preserveFormatting, Boolean loadAsReadOnly)
   --- End of inner exception stack trace ---
   at Microsoft.Build.Shared.ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(Boolean condition, String errorSubCategoryResourceName, BuildEventFileInfo projectFile, Exception innerException, String resourceName, Object[] args)
   at Microsoft.Build.Shared.ProjectFileErrorUtilities.ThrowInvalidProjectFile(BuildEventFileInfo projectFile, Exception innerException, String resourceName, Object[] args)
   at Microsoft.Build.Construction.ProjectRootElement.LoadDocument(String fullPath, Boolean preserveFormatting, Boolean loadAsReadOnly)
   at Microsoft.Build.Construction.ProjectRootElement..ctor(String path, ProjectRootElementCacheBase projectRootElementCache, Boolean preserveFormatting)
   at Microsoft.Build.Construction.ProjectRootElement.CreateProjectFromPath(String projectFile, ProjectRootElementCacheBase projectRootElementCache, Boolean preserveFormatting)
   at Microsoft.Build.Evaluation.ProjectRootElementCache.Get(String projectFile, OpenProjectRootElement openProjectRootElement, Boolean isExplicitlyLoaded, Nullable`1 preserveFormatting)
   at Microsoft.Build.Construction.ProjectRootElement.OpenProjectOrSolution(String fullPath, IDictionary`2 globalProperties, String toolsVersion, ProjectRootElementCacheBase projectRootElementCache, Boolean isExplicitlyLoaded)
   at Microsoft.Build.Execution.ProjectInstance..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, Nullable`1 projectLoadSettings, EvaluationContext evaluationContext)
   at Microsoft.Build.Execution.ProjectInstance.FromFile(String file, ProjectOptions options)
   at RazzleProjectUpdater.RepoCache.<>c__DisplayClass26_1.<CreateAsync>b__6(String projectPath, Dictionary`2 globalProperties, ProjectCollection projectCollection) in C:\Users\andarno\source\repos\MSBuild converters\RazzleProjectUpdater\src\RazzleProjectUpdater\RepoCache.cs:line 405
   at Microsoft.Build.Graph.GraphBuilder.ParseProject(ConfigurationMetadata configurationMetadata)
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at Microsoft.Build.Graph.ParallelWorkSet`2.ExecuteWorkItem()
   at Microsoft.Build.Graph.ParallelWorkSet`2.<<CreateProcessorItemTask>b__16_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Build.Graph.ParallelWorkSet`2.WaitForAllWorkAndComplete()
   at Microsoft.Build.Graph.GraphBuilder.FindGraphNodes()
   at Microsoft.Build.Graph.GraphBuilder.BuildGraph()
   at Microsoft.Build.Graph.ProjectGraph..ctor(IEnumerable`1 entryPoints, ProjectCollection projectCollection, ProjectInstanceFactoryFunc projectInstanceFactory, Int32 degreeOfParallelism, CancellationToken cancellationToken)
   at Microsoft.Build.Graph.ProjectGraph..ctor(IEnumerable`1 entryPoints, ProjectCollection projectCollection, ProjectInstanceFactoryFunc projectInstanceFactory)
   at RazzleProjectUpdater.RepoCache.<>c__DisplayClass26_1.<CreateAsync>g__CreateProjectGraph|5(Boolean msbuildRetail, <>c__DisplayClass26_0& ) in C:\Users\andarno\source\repos\MSBuild converters\RazzleProjectUpdater\src\RazzleProjectUpdater\RepoCache.cs:line 396
   at RazzleProjectUpdater.RepoCache.<CreateAsync>g__DiscoverBuildableProjects|26_2(HashSet`1& msbuildRetail, HashSet`1& corext, <>c__DisplayClass26_0& ) in C:\Users\andarno\source\repos\MSBuild converters\RazzleProjectUpdater\src\RazzleProjectUpdater\RepoCache.cs:line 358

Analysis

Versions & Configurations

dotnet build (17.0.0+c9eb9dd64) can build this project. msbuild.exe (17.2.0-preview-22157-01+9c1732964) can build this project.

AArnott avatar Mar 16 '22 16:03 AArnott

I made this unit test:

        [Fact]
        public void Windows1252()
        {
            using (TestEnvironment env = TestEnvironment.Create())
            {
                string project = @"<?xml version=""1.0"" encoding=""Windows-1252""?>
<Project>
<Target Name=""Todo"" />
</Project>
";
                TransientTestFile file = env.CreateFile("myFile.proj", project);
                using ProjectCollection projectCollection = new();
                EvaluationContext evaluationContext = EvaluationContext.Create(EvaluationContext.SharingPolicy.Shared);
                ImmutableDictionary<string, string> traversalProperties = ImmutableDictionary.Create<string, string>(StringComparer.OrdinalIgnoreCase);
                ProjectOptions projectOptions = new()
                {
                    EvaluationContext = evaluationContext,
                    GlobalProperties = new Dictionary<string, string>(),
                    LoadSettings = ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.IgnoreInvalidImports,
                    ProjectCollection = projectCollection,
                };
                ProjectInstance projectInstance = ProjectInstance.FromFile(file.Path, projectOptions);
            }
        }

And it passed with current main. What did I do wrong? Does the project need to actually do something?

Forgind avatar Mar 23 '22 23:03 Forgind

This project seems to be able to repro the problem: ConsoleApp6.zip

In fact I didn't even have to write an msbuild API client to hit it. Just running the .NET Upgrade Assistant hit it:

C:\temp\ConsoleApp6❯ upgrade-assistant analyze .\ConsoleApp6.sln
-----------------------------------------------------------------------------------------------------------------
Microsoft .NET Upgrade Assistant v0.3.312801+f6e45d1c8e28b25c8054a8d984216fa0403740e7

We are interested in your feedback! Please use the following link to open a survey: https://aka.ms/DotNetUASurvey
-----------------------------------------------------------------------------------------------------------------

[16:32:32 INF] Loaded 6 extensions
[16:32:34 INF] Using MSBuild from C:\Program Files\dotnet\sdk\6.0.300-preview.22154.4\
[16:32:34 INF] Using Visual Studio install from C:\dev18\fitest3 [v17]
[16:32:37 ERR] Unexpected error
Microsoft.Build.Exceptions.InvalidProjectFileException: The project file could not be loaded. 'Windows-1252' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter 'name')  C:\temp\ConsoleApp6\ConsoleApp6\ConsoleApp6.csproj
 ---> System.ArgumentException: 'Windows-1252' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter 'name')
   at System.Text.EncodingTable.InternalGetCodePageFromName(String name)
   at System.Text.EncodingTable.GetCodePageFromName(String name)
   at System.Text.Encoding.GetEncoding(String name)
   at Microsoft.Build.Internal.XmlReaderExtension.GetEncodingFromAttribute(XmlReader reader)
   at Microsoft.Build.Internal.XmlReaderExtension..ctor(String file, Boolean loadAsReadOnly)
   at Microsoft.Build.Construction.ProjectRootElement.LoadDocument(String fullPath, Boolean preserveFormatting, Boolean loadAsReadOnly)
   --- End of inner exception stack trace ---
   at Microsoft.Build.Shared.ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(Boolean condition, String errorSubCategoryResourceName, BuildEventFileInfo projectFile, Exception innerException, String resourceName, Object[] args)
   at Microsoft.Build.Shared.ProjectFileErrorUtilities.ThrowInvalidProjectFile(BuildEventFileInfo projectFile, Exception innerException, String resourceName, Object[] args)
   at Microsoft.Build.Construction.ProjectRootElement.LoadDocument(String fullPath, Boolean preserveFormatting, Boolean loadAsReadOnly)
   at Microsoft.Build.Construction.ProjectRootElement..ctor(String path, ProjectRootElementCacheBase projectRootElementCache, Boolean preserveFormatting)
   at Microsoft.Build.Construction.ProjectRootElement.OpenLoader(String path, ProjectRootElementCacheBase projectRootElementCache)
   at Microsoft.Build.Evaluation.ProjectRootElementCache.Get(String projectFile, OpenProjectRootElement loadProjectRootElement, Boolean isExplicitlyLoaded, Nullable`1 preserveFormatting)
   at Microsoft.Build.Construction.ProjectRootElement.Open(String path, ProjectRootElementCacheBase projectRootElementCache, Boolean isExplicitlyLoaded, Nullable`1 preserveFormatting)
   at Microsoft.Build.Construction.ProjectRootElement.Open(String path, ProjectCollection projectCollection, Nullable`1 preserveFormatting)
   at Microsoft.Build.Construction.ProjectRootElement.Open(String path, ProjectCollection projectCollection)
   at Microsoft.DotNet.UpgradeAssistant.MSBuild.MSBuildProject.get_ProjectRoot() in /_/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs:line 34
   at Microsoft.DotNet.UpgradeAssistant.MSBuild.MSBuildProject.get_References() in /_/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.cs:line 156
   at Microsoft.DotNet.UpgradeAssistant.Extensions.Web.WebComponentIdentifier.GetComponentsAsync(IProject project, CancellationToken token)
   at Microsoft.DotNet.UpgradeAssistant.MSBuild.MSBuildProject.GetComponentsAsync(CancellationToken token) in /_/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.cs:line 128
   at Microsoft.DotNet.UpgradeAssistant.ProjectExtensions.IsApplicableAsync(IProject project, Object obj, CancellationToken token) in /_/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/ProjectExtensions.cs:line 61
   at Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.WinformsResultProvider.IsApplicableAsync(AnalyzeContext analysis, CancellationToken token)
   at Microsoft.DotNet.UpgradeAssistant.Cli.ConsoleAnalyze.<>c__DisplayClass8_0.<<RunAsync>b__0>d.MoveNext() in /_/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Commands/Analyze/ConsoleAnalyze.cs:line 53
--- End of stack trace from previous location ---
   at System.Linq.AsyncEnumerable.WhereEnumerableAsyncIteratorWithTask`1.MoveNextCore() in /_/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Where.cs:line 277
   at System.Linq.AsyncIteratorBase`1.MoveNextAsync() in /_/Ix.NET/Source/System.Linq.Async/System/Linq/AsyncIterator.cs:line 70
   at System.Linq.AsyncIteratorBase`1.MoveNextAsync() in /_/Ix.NET/Source/System.Linq.Async/System/Linq/AsyncIterator.cs:line 75
   at Microsoft.DotNet.UpgradeAssistant.Cli.ConsoleAnalyze.RunAsync(CancellationToken token) in /_/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Commands/Analyze/ConsoleAnalyze.cs:line 53
   at Microsoft.DotNet.UpgradeAssistant.Cli.ConsoleAnalyze.RunAsync(CancellationToken token) in /_/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Commands/Analyze/ConsoleAnalyze.cs:line 53
   at Microsoft.DotNet.UpgradeAssistant.Cli.ConsoleRunner.StartAsync(CancellationToken token) in /_/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/ConsoleRunner.cs:line 61

You can install the upgrade assistant with dotnet tool update -g upgrade-assistant.

AArnott avatar Mar 25 '22 22:03 AArnott

ProjectRootElement.LoadDocument uses the XML DOM (as it needs to r/w), and ProjectInstance.FromFile uses XML reader directly - perhaps that's the difference.

danmoseley avatar Mar 28 '22 19:03 danmoseley

@AArnott - are you still hitting this?

I wasn't able to repro on 7.0.3xx:

> dotnet --version
7.0.300-preview.23179.2

> upgrade-assistant analyze .\ConsoleApp6.sln
-----------------------------------------------------------------------------------------------------------------
Microsoft .NET Upgrade Assistant v0.4.421302+be0ea11e8234f2a0bde2d170b0fdd455fa4f9a45

We are interested in your feedback! Please use the following link to open a survey: https://aka.ms/DotNetUASurvey
-----------------------------------------------------------------------------------------------------------------

[20:46:32 INF] Loaded 9 extensions

(...)

[20:46:43 INF] Analysis Complete, the report is available at (...)

Looking into history I suspect this might have been adressed by https://github.com/dotnet/msbuild/pull/7532

JanKrivanek avatar Apr 25 '23 18:04 JanKrivanek

I am not hitting it anymore. Thank you.

AArnott avatar Apr 26 '23 20:04 AArnott