diff --git a/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs b/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs
index e1df8ac1..40ec8ede 100644
--- a/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs
+++ b/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs
@@ -532,6 +532,14 @@ private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringToke
#pragma warning disable CS0618 // Type or member is obsolete (Temporaily create a potentially invalid solution folder until nested projects is interpreted.)
SolutionFolderModel folder = solution.CreateSlnFolder(name: displayName.ToString());
#pragma warning restore CS0618 // Type or member is obsolete
+
+ // Solution folders with duplicate ids should not error when reading sln files to preserve legacy behavior.
+ if (solution.FindItemById(projectId) is not null)
+ {
+ projectId = Guid.NewGuid();
+ this.tarnished = true;
+ }
+
folder.Id = projectId;
return folder;
}
diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs
index e962799c..b5fc7a13 100644
--- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs
+++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs
@@ -208,4 +208,35 @@ public async Task InvalidSlnxVersionAsync()
Assert.Equal(1, ex.Line);
Assert.Equal(2, ex.Column);
}
+
+ ///
+ /// The legacy sln solution parser would ignore duplicate folder guids.
+ /// Ensure this behavior is maintained.
+ ///
+ [Fact]
+ public async Task DuplicateFolderIdSlnAsync()
+ {
+ ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateFolderId.sln");
+ SolutionModel solution = await SolutionSerializers.SlnFileV12.OpenAsync(duplicateId.Stream, CancellationToken.None);
+
+ Assert.Equal(4, solution.SolutionFolders.Count);
+
+ Assert.NotNull(solution.SerializerExtension);
+ Assert.True(solution.SerializerExtension.Tarnished);
+ }
+
+ ///
+ /// Ensure the slnx parser does fail on duplicate folder guids.
+ ///
+ [Fact]
+ public async Task DuplicateFolderIdSlnxAsync()
+ {
+ ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateFolderId.slnx");
+ SolutionException ex = await Assert.ThrowsAsync(
+ async () => _ = await SolutionSerializers.SlnXml.OpenAsync(duplicateId.Stream, CancellationToken.None));
+
+ Assert.StartsWith(string.Format(Errors.DuplicateItemRef_Args2, "11111111-1111-1111-1111-111111111111", nameof(SolutionFolderModel)), ex.Message);
+ Assert.Equal(3, ex.Line);
+ Assert.Equal(4, ex.Column);
+ }
}
diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt
new file mode 100644
index 00000000..9c5a5cbe
--- /dev/null
+++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt
@@ -0,0 +1,10 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apple", "Apple", "{11111111-1111-1111-1111-111111111111}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mango", "Mango", "{11111111-1111-1111-1111-111111111111}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pear", "Pear", "{11111111-1111-1111-1111-111111111111}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Orange", "Orange", "{11111111-1111-1111-1111-111111111111}"
+EndProject
diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml
new file mode 100644
index 00000000..bdbd9979
--- /dev/null
+++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+