Skip to content

Commit dd5d9f7

Browse files
authored
Add warning for using BinaryFormatter in GenerateResource on .NET 8 (#8524)
It will be removed in .NET 9; doing so should be discouraged. Note that this does nothing by default, but we can change that in the SDK. Fixes #8453 Context BinaryFormatter is deprecated and will be removed in .NET 9. In addition to the possibility of using a modern MSBuild with an older framework, there are apparently ways you can exempt your project, so we are not currently removing it entirely, and this warning (which is off by default) can be disabled even if it is enabled in the SDK. Changes Made I deleted using System.Runtime.Serialization.Formatters.Binary; in GenerateResource, then put a warning before the one usage of BinaryFormatter. That isn't necessarily the best way to figure out where it's used, as it would be helpful to know early, so feel free to comment to that effect. Then I disabled it via a property and will make a separate PR to enable it in the 8.0 SDK. Testing Notes
1 parent 8ead272 commit dd5d9f7

21 files changed

+205
-38
lines changed

src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,6 +1940,48 @@ public void InvalidStateFile()
19401940
}
19411941
}
19421942

1943+
[Fact]
1944+
public void GenerateResourceWarnsWhenUsingBinaryFormatter()
1945+
{
1946+
using TestEnvironment env = TestEnvironment.Create();
1947+
TransientTestFile resource = env.CreateFile(".resx", @"<?xml version=""1.0"" encoding=""utf-8""?>
1948+
<root>
1949+
<data name=""$this.Icon"" type=""System.Drawing.Icon, System.Drawing"" mimetype=""application/x-microsoft.net.object.binary.base64"">
1950+
<value>
1951+
AAABAAEAEBAAAAAAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAA
1952+
AAD///8BoqKiDaKiotmioqL5oqKiK////wH///8B////Af///wH///8B////AaKioiGioqLxoqKi5aKi
1953+
ohn///8B////AbS0tBW0tLTz29vb/7Ozsu18Wi+Be1gswXtYLO17WCzte1gswXtYLIGzs7Lz2dnZ/7S0
1954+
tPu0tLQj////Af///wH///8BxsbGQdPT0//Cv739nGs7/6ZsNf+ubzf/rm83/6ZsNf+hdkr/xcTD/8bG
1955+
xf/GxsY/////Af///wH///8B////AYxlNmejiGn1r3hE/7uMXv/Ck3H/xJF0/8OPcf+/kGz/uIpd/7SG
1956+
Wf+hhWT1jGU2Z////wH///8B////AZZtOzWWbTvVs31G/8KZcf/Yqon/79/P//r28//69fP/79/R/9en
1957+
hf++lGz/s31G/5ZtO9WWbTs1////Af///wGhdUGBsIBK/8abb//Zqoj///7r///67v///fL///7y///8
1958+
7////ev/2aN6/8KZbP+wgEr/oXVBgf///wH///8BrH5Iwb+PWP/No4H/8NvB///35v/68uP/xcC2//Ht
1959+
3v///Oj///Xf/+/Ur//ImXL/v49Y/6x+SMH///8B////AbeHTu3JnGb/z5+A//rz4v/99un/8vDj/42M
1960+
hP+Bf3f/0s/C///76//67Mz/x5Bt/8mcZv+3h07t////Af///wHCkFTtzqZx/9Glif/69un//fju////
1961+
+f+BgHn/sa6k/4F/d//Jxrr/+vDT/8mWcv/OpnH/wpBU7f///wH///8BzZlbwdOsdf/Zt5j/8ePW//77
1962+
9f/19fP/n56V//Dw6f/4+PL/vrmt//Dawv/Sqof/06x1/82ZW8H///8B////AbOddIvTrXf/38Sa/969
1963+
qv//////8PDu/+fl2v////f////3///+8//ctJj/28CW/8Kqfv/Gn2qF////AQCZ3T0KmtjZLpzF9d6/
1964+
iv/iyaf/37+u//Hj3P/z8ez/9PHr//Hi2f/cuqP/38Oe/4yxqf84ptH5DprWzwCZ3ScAoON9fNHy7WHD
1965+
6O86pMb74seS/+bRqf/gwqb/1a6W/9Wrkv/evaD/5M+m/7/Bnv9Hstf9q+P2/Smw6NkAoOMnAKfpe13J
1966+
8eW16Pn/Ycfr7zqqzPPsxIj/6cuU/+fQnf/n0J3/6cuU/97Cjv8yqtD1gdPw9XPQ8+sAp+nNAKfpBQCu
1967+
7wUAru+LW8v05b/s+v9cy/HpTbLJxfq8dMH6vHTt+rx07fq8dMFRssjDac/y7XzW9u0Aru/JAK7vHf//
1968+
/wH///8BALX0AwC19IEAtfTRALX0ywC19Af///8B////Af///wH///8BALX0FwC19NEAtfTJALX0J///
1969+
/wH///8BAAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA
1970+
//8AAP//AAD//w==
1971+
</value>
1972+
</data>
1973+
</root>
1974+
");
1975+
1976+
GenerateResource gr = Utilities.CreateTask(_output, usePreserialized: true, env: env);
1977+
gr.Sources = new ITaskItem[] { new TaskItem(resource.Path) };
1978+
gr.WarnOnBinaryFormatterUse = true;
1979+
1980+
gr.Execute().ShouldBeTrue();
1981+
1982+
Utilities.AssertLogContainsResource(gr, "GenerateResource.BinaryFormatterUse", "$this.Icon", "System.Drawing.Icon, System.Drawing");
1983+
}
1984+
19431985
/// <summary>
19441986
/// Cause failures in ResourceReader
19451987
/// </summary>

src/Tasks.UnitTests/ResourceHandling/MSBuildResXReader_Tests.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public void ParsesSingleStringAsString()
3232
@"<data name=""StringResource"" xml:space=""preserve"">
3333
<value>StringValue</value>
3434
<comment>Comment</comment>
35-
</data>"));
35+
</data>"), null, false);
3636

3737
AssertSingleStringResource(resxWithSingleString, "StringResource", "StringValue");
3838
}
@@ -45,7 +45,7 @@ public void ParsesSingleStringWithoutPreserveAsString()
4545
@"<data name=""StringResource"">
4646
<value> StringValue </value>
4747
<comment>Comment</comment>
48-
</data>"));
48+
</data>"), null, false);
4949

5050
AssertSingleStringResource(resxWithSingleString, "StringResource", " StringValue ");
5151
}
@@ -58,7 +58,7 @@ public void ParsesSingleWhitespaceStringAsString()
5858
@"<data name=""StringResource"" xml:space=""preserve"">
5959
<value> </value>
6060
<comment>Comment</comment>
61-
</data>"));
61+
</data>"), null, false);
6262

6363
AssertSingleStringResource(resxWithSingleString, "StringResource", " ");
6464
}
@@ -71,7 +71,7 @@ public void ParsesSingleWhitespaceStringWithNoPreserveAsEmptyString()
7171
@"<data name=""StringResource"">
7272
<value> </value>
7373
<comment>Comment</comment>
74-
</data>"));
74+
</data>"), null, false);
7575

7676
AssertSingleStringResource(resxWithSingleString, "StringResource", "");
7777
}
@@ -83,7 +83,7 @@ public void ParsesSingleStringWithPartialTypeName()
8383
ResXHelper.SurroundWithBoilerplate(
8484
@"<data name=""StringResource"" type=""System.String"">
8585
<value>StringValue</value>
86-
</data>"));
86+
</data>"), null, false);
8787

8888
AssertSingleStringResource(resxWithSingleString, "StringResource", "StringValue");
8989
}
@@ -100,7 +100,7 @@ public void LoadsMultipleStringsPreservingOrder()
100100
</data>
101101
<data name=""2StringResource2"" xml:space=""preserve"">
102102
<value>2StringValue2</value>
103-
</data>"));
103+
</data>"), null, false);
104104

105105
resxWithTwoStrings.Count.ShouldBe(2);
106106

@@ -121,7 +121,7 @@ public void ResXNullRefProducesNullLiveObject()
121121
@" <assembly alias=""System.Windows.Forms"" name=""System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" />
122122
<data name=""$this.AccessibleDescription"" type=""System.Resources.ResXNullRef, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"">
123123
<value />
124-
</data>"));
124+
</data>"), null, false);
125125

126126
resxWithNullRef.ShouldHaveSingleItem();
127127

@@ -143,7 +143,7 @@ public void LoadsStringFromFileRefAsString(string stringType)
143143
$@" <assembly alias=""System.Windows.Forms"" name=""System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" />
144144
<data name=""TextFile1"" type=""System.Resources.ResXFileRef, System.Windows.Forms"">
145145
<value>ResourceHandling\TextFile1.txt;{stringType};utf-8</value>
146-
</data>"));
146+
</data>"), null, false);
147147

148148
AssertSingleStringResource(resxWithLinkedString, "TextFile1", "Contents of TextFile1");
149149
}
@@ -174,6 +174,8 @@ public void LoadsStringFromFileRefAsStringWithShiftJISEncoding()
174174
<data name=""TextFile1"" type=""System.Resources.ResXFileRef, System.Windows.Forms"">
175175
<value>ResourceHandling\TextFileInShiftJIS.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;shift_jis</value>
176176
</data>"),
177+
null,
178+
false,
177179
Path.Combine(baseDir.Path, nameof(LoadsStringFromFileRefAsStringWithShiftJISEncoding) + ".resx"),
178180
useRelativePath: true);
179181

@@ -210,7 +212,7 @@ public void PassesThroughBitmapInResx()
210212
b7eblRw4yy8Ta2GCpaZp1sIzz2LfCMS+EYh9401iw/gG1gYfvzjQIXcAAAAASUVORK5CYII=
211213
</value>
212214
</data>
213-
"));
215+
"), null, false);
214216
resxWithEmbeddedBitmap.ShouldHaveSingleItem();
215217
resxWithEmbeddedBitmap[0].ShouldBeOfType(typeof(TypeConverterByteArrayResource));
216218

@@ -228,7 +230,7 @@ public void TypeConverterStringWellFormatted()
228230
<data name=""color"" type=""System.Drawing.Color, System.Drawing"">
229231
<value>Blue</value>
230232
</data>
231-
"));
233+
"), null, false);
232234
resxWithEmbeddedBitmap.ShouldHaveSingleItem();
233235
resxWithEmbeddedBitmap[0].ShouldBeOfType(typeof(TypeConverterStringResource));
234236

@@ -252,7 +254,7 @@ public void TypeConverterStringDirectValue()
252254
ResXHelper.SurroundWithBoilerplate(
253255
@" <assembly alias=""System.Drawing"" name=""System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"" />
254256
<data name=""Color1"" type=""System.Drawing.Color, System.Drawing"">Blue</data>
255-
"));
257+
"), null, false);
256258
resxWithEmbeddedBitmap.ShouldHaveSingleItem();
257259
resxWithEmbeddedBitmap[0].ShouldBeOfType(typeof(TypeConverterStringResource));
258260

@@ -272,7 +274,7 @@ public void ResXFileRefToBitmap()
272274
$@" <data name='Image1' type='System.Resources.ResXFileRef, System.Windows.Forms'>
273275
<value>{bitmapPath};System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
274276
</data>
275-
"));
277+
"), null, false);
276278
resxWithLinkedBitmap.ShouldHaveSingleItem();
277279
resxWithLinkedBitmap[0].ShouldBeOfType(typeof(FileStreamResource));
278280

@@ -301,7 +303,7 @@ public void ResXFileRefToMemoryStream(string typeNameInResx)
301303
$@" <data name='Image1' type='System.Resources.ResXFileRef, System.Windows.Forms'>
302304
<value>{linkedTextFile.Path};{typeNameInResx}</value>
303305
</data>
304-
"));
306+
"), null, false);
305307

306308
var resource = resources.ShouldHaveSingleItem()
307309
.ShouldBeOfType<LiveObjectResource>();
@@ -321,7 +323,7 @@ public void AssemblyElementWithNoAliasInfersSimpleName()
321323
ResXHelper.SurroundWithBoilerplate(
322324
@" <assembly name=""System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"" />
323325
<data name=""Color1"" type=""System.Drawing.Color, System.Drawing""><value>Blue</value></data>
324-
"));
326+
"), null, false);
325327
resxWithEmbeddedBitmap.ShouldHaveSingleItem();
326328
resxWithEmbeddedBitmap[0].ShouldBeOfType(typeof(TypeConverterStringResource));
327329

src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void DirtyCleanScenario(bool useMSBuildResXReader)
4040
cache.IsDirty.ShouldBeFalse();
4141

4242
// Getting a file that wasn't in the cache is a write operation.
43-
cache.GetResXFileInfo(resx, useMSBuildResXReader);
43+
cache.GetResXFileInfo(resx, useMSBuildResXReader, null, false);
4444
cache.IsDirty.ShouldBeTrue();
4545

4646
// Add linkedFiles to further test serialization and deserialization.
@@ -72,7 +72,7 @@ public void DirtyCleanScenario(bool useMSBuildResXReader)
7272
resX2.linkedFiles[1].ShouldBe(resX.linkedFiles[1]);
7373

7474
// Asking for a file that's in the cache should not dirty the cache.
75-
cache2.GetResXFileInfo(resx, useMSBuildResXReader);
75+
cache2.GetResXFileInfo(resx, useMSBuildResXReader, null, false);
7676
cache2.IsDirty.ShouldBeFalse();
7777

7878
// Changing UseSourcePath to false should dirty the cache.

src/Tasks/GenerateResource.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ public string StronglyTypedLanguage
274274
}
275275
}
276276

277+
// Indicates whether any BinaryFormatter use should lead to a warning.
278+
public bool WarnOnBinaryFormatterUse
279+
{
280+
get; set;
281+
}
282+
277283
/// <summary>
278284
/// Specifies the namespace to use for the generated class source for the
279285
/// strongly typed resource. If left blank, no namespace is used.
@@ -808,7 +814,8 @@ public override bool Execute()
808814
StronglyTypedClassName,
809815
PublicClass,
810816
ExtractResWFiles,
811-
OutputDirectory);
817+
OutputDirectory,
818+
WarnOnBinaryFormatterUse);
812819

813820
this.StronglyTypedClassName = process.StronglyTypedClassName; // in case a default was chosen
814821
this.StronglyTypedFileName = process.StronglyTypedFilename; // in case a default was chosen
@@ -1510,7 +1517,7 @@ private bool ShouldRebuildResgenOutputFile(string sourceFilePath, string outputF
15101517
ResGenDependencies.ResXFile resxFileInfo;
15111518
try
15121519
{
1513-
resxFileInfo = _cache.GetResXFileInfo(sourceFilePath, UsePreserializedResources);
1520+
resxFileInfo = _cache.GetResXFileInfo(sourceFilePath, UsePreserializedResources, Log, WarnOnBinaryFormatterUse);
15141521
}
15151522
catch (Exception e) when (!ExceptionHandling.NotExpectedIoOrXmlException(e) || e is MSBuildResXException)
15161523
{
@@ -1971,7 +1978,7 @@ private bool DetermineWhetherSerializedObjectLoads(string data)
19711978
{
19721979
byte[] serializedData = ByteArrayFromBase64WrappedString(data);
19731980

1974-
BinaryFormatter binaryFormatter = new BinaryFormatter();
1981+
BinaryFormatter binaryFormatter = new();
19751982

19761983
using (MemoryStream memoryStream = new MemoryStream(serializedData))
19771984
{
@@ -2337,6 +2344,8 @@ internal bool StronglyTypedResourceSuccessfullyCreated
23372344
/// </summary>
23382345
private bool _useSourcePath = false;
23392346

2347+
private bool _logWarningForBinaryFormatter = false;
2348+
23402349
#endregion
23412350

23422351
/// <summary>
@@ -2357,7 +2366,8 @@ internal void Run(
23572366
string classname,
23582367
bool publicClass,
23592368
bool extractingResWFiles,
2360-
string resWOutputDirectory)
2369+
string resWOutputDirectory,
2370+
bool logWarningForBinaryFormatter)
23612371
{
23622372
_logger = log;
23632373
_assemblyFiles = assemblyFilesList;
@@ -2376,6 +2386,7 @@ internal void Run(
23762386
_resWOutputDirectory = resWOutputDirectory;
23772387
_portableLibraryCacheInfo = new List<ResGenDependencies.PortableLibraryFile>();
23782388
_usePreserializedResources = usePreserializedResources;
2389+
_logWarningForBinaryFormatter = logWarningForBinaryFormatter;
23792390

23802391
#if !FEATURE_ASSEMBLYLOADCONTEXT
23812392
// If references were passed in, we will have to give the ResxResourceReader an object
@@ -2980,7 +2991,7 @@ private void ReadResources(String filename, bool shouldUseSourcePath, String out
29802991
}
29812992
else
29822993
{
2983-
foreach (IResource resource in MSBuildResXReader.GetResourcesFromFile(filename, shouldUseSourcePath))
2994+
foreach (IResource resource in MSBuildResXReader.GetResourcesFromFile(filename, shouldUseSourcePath, _logger, _logWarningForBinaryFormatter))
29842995
{
29852996
AddResource(reader, resource, filename, 0, 0);
29862997
}

src/Tasks/Microsoft.Common.CurrentVersion.targets

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3328,8 +3328,10 @@ Copyright (C) Microsoft Corporation. All rights reserved.
33283328
SdkToolsPath="$(ResgenToolPath)"
33293329
ExecuteAsTool="$(ResGenExecuteAsTool)"
33303330
EnvironmentVariables="$(ResGenEnvironment)"
3331+
WarnOnBinaryFormatterUse="$(GenerateResourceWarnOnBinaryFormatterUse)"
33313332
MSBuildRuntime="$(GenerateResourceMSBuildRuntime)"
3332-
MSBuildArchitecture="$(GenerateResourceMSBuildArchitecture)">
3333+
MSBuildArchitecture="$(GenerateResourceMSBuildArchitecture)"
3334+
>
33333335

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

src/Tasks/ResGenDependencies.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ public override void Translate(ITranslator translator)
125125
translator.Translate(ref baseLinkedFileDirectory);
126126
}
127127

128-
internal ResXFile GetResXFileInfo(string resxFile, bool useMSBuildResXReader)
128+
internal ResXFile GetResXFileInfo(string resxFile, bool useMSBuildResXReader, TaskLoggingHelper log, bool logWarningForBinaryFormatter)
129129
{
130130
// First, try to retrieve the resx information from our hashtable.
131131
if (!resXFiles.TryGetValue(resxFile, out ResXFile retVal))
132132
{
133133
// Ok, the file wasn't there. Add it to our cache and return it to the caller.
134-
retVal = AddResxFile(resxFile, useMSBuildResXReader);
134+
retVal = AddResxFile(resxFile, useMSBuildResXReader, log, logWarningForBinaryFormatter);
135135
}
136136
else
137137
{
@@ -141,19 +141,19 @@ internal ResXFile GetResXFileInfo(string resxFile, bool useMSBuildResXReader)
141141
{
142142
resXFiles.Remove(resxFile);
143143
_isDirty = true;
144-
retVal = AddResxFile(resxFile, useMSBuildResXReader);
144+
retVal = AddResxFile(resxFile, useMSBuildResXReader, log, logWarningForBinaryFormatter);
145145
}
146146
}
147147

148148
return retVal;
149149
}
150150

151-
private ResXFile AddResxFile(string file, bool useMSBuildResXReader)
151+
private ResXFile AddResxFile(string file, bool useMSBuildResXReader, TaskLoggingHelper log, bool logWarningForBinaryFormatter)
152152
{
153153
// This method adds a .resx file "file" to our .resx cache. The method causes the file
154154
// to be cracked for contained files.
155155

156-
var resxFile = new ResXFile(file, BaseLinkedFileDirectory, useMSBuildResXReader);
156+
var resxFile = new ResXFile(file, BaseLinkedFileDirectory, useMSBuildResXReader, log, logWarningForBinaryFormatter);
157157
resXFiles.Add(file, resxFile);
158158
_isDirty = true;
159159
return resxFile;
@@ -230,7 +230,7 @@ internal sealed class ResXFile : DependencyFile, ITranslatable
230230

231231
internal string[] LinkedFiles => linkedFiles;
232232

233-
internal ResXFile(string filename, string baseLinkedFileDirectory, bool useMSBuildResXReader) : base(filename)
233+
internal ResXFile(string filename, string baseLinkedFileDirectory, bool useMSBuildResXReader, TaskLoggingHelper log, bool logWarningForBinaryFormatter) : base(filename)
234234
{
235235
// Creates a new ResXFile object and populates the class member variables
236236
// by computing a list of linked files within the .resx that was passed in.
@@ -239,7 +239,7 @@ internal ResXFile(string filename, string baseLinkedFileDirectory, bool useMSBui
239239

240240
if (FileSystems.Default.FileExists(FileName))
241241
{
242-
linkedFiles = GetLinkedFiles(filename, baseLinkedFileDirectory, useMSBuildResXReader);
242+
linkedFiles = GetLinkedFiles(filename, baseLinkedFileDirectory, useMSBuildResXReader, log, logWarningForBinaryFormatter);
243243
}
244244
}
245245

@@ -260,7 +260,7 @@ public void Translate(ITranslator translator)
260260
/// </summary>
261261
/// <exception cref="ArgumentException">May be thrown if Resx is invalid. May contain XmlException.</exception>
262262
/// <exception cref="XmlException">May be thrown if Resx is invalid</exception>
263-
private static string[] GetLinkedFiles(string filename, string baseLinkedFileDirectory, bool useMSBuildResXReader)
263+
private static string[] GetLinkedFiles(string filename, string baseLinkedFileDirectory, bool useMSBuildResXReader, TaskLoggingHelper log, bool logWarningForBinaryFormatter)
264264
{
265265
// This method finds all linked .resx files for the .resx file that is passed in.
266266
// filename is the filename of the .resx file that is to be examined.
@@ -270,7 +270,7 @@ private static string[] GetLinkedFiles(string filename, string baseLinkedFileDir
270270

271271
if (useMSBuildResXReader)
272272
{
273-
foreach (IResource resource in MSBuildResXReader.GetResourcesFromFile(filename, pathsRelativeToBasePath: baseLinkedFileDirectory == null))
273+
foreach (IResource resource in MSBuildResXReader.GetResourcesFromFile(filename, pathsRelativeToBasePath: baseLinkedFileDirectory == null, log, logWarningForBinaryFormatter))
274274
{
275275
if (resource is FileStreamResource linkedResource)
276276
{

0 commit comments

Comments
 (0)