Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary formatter alternatives for resource serialisation #80155

Open
ericcdub opened this issue Dec 30, 2022 · 15 comments
Open

Binary formatter alternatives for resource serialisation #80155

ericcdub opened this issue Dec 30, 2022 · 15 comments
Labels
area-System.Runtime binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity
Milestone

Comments

@ericcdub
Copy link

BinaryFormatter is obsolete and soon to be removed from. NET due to security concerns regarding serialisation of arbitrary data. Coupled with this are changes in recent .NET releases to the way .resources files should be serialised to disk with ResourceWriter.

Resources of certain (non-primitive) types can no longer be serialised using ResourceWriter.AddResource as was possible in .NET Framework, and must be first serialised to byte arrays before being passed to PreserializedResourceWriter, but there is no suggestion as to what is the best method to serialise the resources into a byte array.

This is a problem because I'm serialising properties of form controls like System.Drawing.Size into
.resources files as part of creating satellite assemblies. These types don't implement their own serialisation to my knowledge.

Therefore, I've been using BinaryFormatter to serialise objects to byte arrays before passing them PreserializedResourceWriter.AddBinaryaformattedResource.

If BinaryFormatter is now obsolete, how are developers supposed to serialise their resources?

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Dec 30, 2022
@marcpopMSFT marcpopMSFT transferred this issue from dotnet/sdk Jan 4, 2023
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@Clockwork-Muse
Copy link
Contributor

This is a problem because I'm serialising properties of form controls like System.Drawing.Size into
.resources files as part of creating satellite assemblies.

For your specific case, assuming the properties are config equivalents (ie, width, text direction, etc), you should likely be using some form of text-based config or json file.

@ericcdub
Copy link
Author

This is a problem because I'm serialising properties of form controls like System.Drawing.Size into
.resources files as part of creating satellite assemblies.

For your specific case, assuming the properties are config equivalents (ie, width, text direction, etc), you should likely be using some form of text-based config or json file.

I don't think that'll work unfortunately as the .resources files being serialised will be used by Al.exe to create satellite assemblies for 3rd-party customer apps, so we don't get to pick and choose the format the resources are serialised with.

@ghost
Copy link

ghost commented Feb 16, 2023

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

BinaryFormatter is obsolete and soon to be removed from. NET due to security concerns regarding serialisation of arbitrary data. Coupled with this are changes in recent .NET releases to the way .resources files should be serialised to disk with ResourceWriter.

Resources of certain (non-primitive) types can no longer be serialised using ResourceWriter.AddResource as was possible in .NET Framework, and must be first serialised to byte arrays before being passed to PreserializedResourceWriter, but there is no suggestion as to what is the best method to serialise the resources into a byte array.

This is a problem because I'm serialising properties of form controls like System.Drawing.Size into
.resources files as part of creating satellite assemblies. These types don't implement their own serialisation to my knowledge.

Therefore, I've been using BinaryFormatter to serialise objects to byte arrays before passing them PreserializedResourceWriter.AddBinaryaformattedResource.

If BinaryFormatter is now obsolete, how are developers supposed to serialise their resources?

Author: ericcdub
Assignees: -
Labels:

area-Serialization, area-System.Runtime, untriaged

Milestone: -

@dakersnar
Copy link
Contributor

cc @GrabYourPitchforks

@sengiv
Copy link

sengiv commented Mar 21, 2023

It's high time to make your own BinaryFormatter

@tannergooding
Copy link
Member

@GrabYourPitchforks, could you take a look at this as it seems related to binary serialization?

@ericstj ericstj added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed untriaged New issue has not been triaged by the area owner labels Aug 14, 2023
@ericstj ericstj added this to the Future milestone Aug 14, 2023
@Neme12
Copy link

Neme12 commented Aug 24, 2023

properties of form controls like System.Drawing.Size

System.Drawing.Size should have a TypeConverter, so you should be able to do:

TypeDescriptor.GetConverter(typeof(Size)).ConvertToInvariantString(size);

and

TypeDescriptor.GetConverter(typeof(Size)).ConvertFromInvariantString(size);

Many .NET types have a TypeConverter, so it might be enough for your needs.

@GrabYourPitchforks
Copy link
Member

The only way to do this safely is to have the resource reader and writer special-case specific types or data structures and to write them out using whatever format is appropriate for them.

For example, if you're trying to serialize a System.Drawing.Bitmap, you could write out a marker saying "this is an image" and then follow it up with the raw image data. On deserialization, the resource reader would read the "this is an image" marker and say ok - I need to take this byte[] and call the Bitmap ctor over it.

If you're trying to serialize a Rect or a Size or a similar type, the writer would emit the appropriate marker followed by the data, and when the reader reads the marker, it goes down whatever specialized logic is needed to read the resource.

Here's the crucial detail. If you want this to not have the same problems BinaryFormatter and related serializers have, you must follow one of the below patterns.

  1. The resource reader / writer needs to special-case all the types it cares about. This path means the set of resource-enabled types is finite, and you cannot add your own custom types to this list.

  2. Or you need to invent a brand new interface for resource reading / writing extensibility, and custom types would need to implement this interface. (TypeConverter isn't really meant for structured data.) Crucially, since the resource reader would be forbidden from calling Type.GetType or Assembly.GetType, this would require some agent external to the .resx file itself to provide the full closure of resource-enabled serializable types.

This is all a lot of work, when a much simpler solution exists today:

Use string-based resources. If you need to smuggle custom types, then use JSON or XML or whatever mechanism is appropriate to turn your custom data object into a string. Then when you read the resource, read it back as a string, then JSON/XML/whatever-deserialize it back into the data structure you expect.

@ericcdub
Copy link
Author

ericcdub commented Aug 28, 2023

The only way to do this safely is to have the resource reader and writer special-case specific types or data structures and to write them out using whatever format is appropriate for them.

For example, if you're trying to serialize a System.Drawing.Bitmap, you could write out a marker saying "this is an image" and then follow it up with the raw image data. On deserialization, the resource reader would read the "this is an image" marker and say ok - I need to take this byte[] and call the Bitmap ctor over it.

If you're trying to serialize a Rect or a Size or a similar type, the writer would emit the appropriate marker followed by the data, and when the reader reads the marker, it goes down whatever specialized logic is needed to read the resource.

Here's the crucial detail. If you want this to not have the same problems BinaryFormatter and related serializers have, you must follow one of the below patterns.

  1. The resource reader / writer needs to special-case all the types it cares about. This path means the set of resource-enabled types is finite, and you cannot add your own custom types to this list.
  2. Or you need to invent a brand new interface for resource reading / writing extensibility, and custom types would need to implement this interface. (TypeConverter isn't really meant for structured data.) Crucially, since the resource reader would be forbidden from calling Type.GetType or Assembly.GetType, this would require some agent external to the .resx file itself to provide the full closure of resource-enabled serializable types.

This is all a lot of work, when a much simpler solution exists today:

Use string-based resources. If you need to smuggle custom types, then use JSON or XML or whatever mechanism is appropriate to turn your custom data object into a string. Then when you read the resource, read it back as a string, then JSON/XML/whatever-deserialize it back into the data structure you expect.

Thanks for your detailed reply. The key point here is that our use case is creating satellite assemblies for third party applications supplied by customers, not our own applications. As such, we're not in a position to impose our own custom resource serialization protocols on third party applications. The customer would have to modify their app to perform this custom serialization in their own code, wouldn't they? The .NET runtime presumably expects resources in satellite assemblies to adhere to a proprietary Microsoft serialization format so a custom format will never work out of the box - I believe it's called MS-NRBF?

I'm wondering if it's better for us to write a .ResX file instead of a .resources file, and pass that ResX to Al.exe?

@ericcdub
Copy link
Author

Just commenting here to see if there's been any update regarding BinaryFormatter and what to replace it with for WinForms resources?

@mastahg
Copy link

mastahg commented Jan 7, 2024

I'm coming across this issue in porting my .net framework application to .net8. Our software allows the compilation of C# during runtime via Microsoft.CodeAnalysis.CSharp.CSharpCompilation

The following works on .net framework, but will throw an error when you feed it a .resx created by visual studio that contains icons,sizes,points etc on .net8

private static Stream ProvideResourceData(string resourceFullFilename)
        {
            // For non-.resx files just create a FileStream object to read the file as binary data
            if (!resourceFullFilename.EndsWith(".resx", StringComparison.OrdinalIgnoreCase))
                return new FileStream(resourceFullFilename, FileMode.Open);

            // Remainder of this method converts a .resx file into .resource file data and returns it 
            //  as a MemoryStream
            MemoryStream shortLivedBackingStream = new MemoryStream();
            using (ResourceWriter resourceWriter = new ResourceWriter(shortLivedBackingStream))
            {
                //resourceWriter.TypeNameConverter = TypeNameConverter;
                using (ResXResourceReader resourceReader = new ResXResourceReader(resourceFullFilename))
                {

                    resourceReader.BasePath = Path.GetDirectoryName(Path.GetFullPath(resourceFullFilename));
                                        foreach (DictionaryEntry dictionaryEnumerator in resourceReader)
                    {

                        string resourceKey = dictionaryEnumerator.Key as string;
                            resourceWriter.AddResource(resourceKey, dictionaryEnumerator.Value);
                    
                }
            }

            // This needed because shortLivedBackingStream is now closed
            return new MemoryStream(shortLivedBackingStream.GetBuffer());
        }

Using ericcdub's workaround

                                using var tempMS = new MemoryStream();
                                bf.Serialize(tempMS,dictionaryEnumerator.Value);
                                tempMS.Position = 0;
                                resourceWriter.AddBinaryFormattedResource(resourceKey, tempMS.ToArray());

@jkotas jkotas added the binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it label May 29, 2024
@jeffhandley
Copy link
Member

We incorporated WinForms resource scenarions in the BinaryFormatter removal during .NET 9. Here is the documentation related to the scenario: BinaryFormatter migration guide: WinForms apps.

@mastahg @ericcdub -- Please let us know if this addresses your questions/issues.

/cc @JeremyKuhne

@jeffhandley jeffhandley added needs-author-action An issue or pull request that requires more info or actions from the author. and removed needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration labels Feb 9, 2025
Copy link
Contributor

This issue has been marked needs-author-action and may be missing some important information.

Copy link
Contributor

This issue has been automatically marked no-recent-activity because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove no-recent-activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Runtime binaryformatter-migration Issues related to the removal of BinaryFormatter and migrations away from it needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity
Projects
None yet
Development

No branches or pull requests