-
Notifications
You must be signed in to change notification settings - Fork 985
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
Adding new strongly typed methods to Clipboard, DataObject and IDataObject. #11545
base: main
Are you sure you want to change the base?
Conversation
e246a67
to
a89070f
Compare
...ndows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectExtensions.cs
Outdated
Show resolved
Hide resolved
...ndows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectExtensions.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some comments.
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ComposedBinder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ComposedBinder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ComposedBinder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
816f4c9
to
7cc2fdc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some more comments. I'm a little concerned that we're using TypeName where it isn't necessary in some cases. We should optimize this before we fully validate this as changing it afterwords would be risky.
I still need to spend more time reviewing.
src/System.Windows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsArrayRecordExtensions.cs
Outdated
Show resolved
Hide resolved
...ndows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectExtensions.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ComposedBinder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ComposedBinder.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
.../System/Windows/Forms/OLE/DataObject.ComposedDataObject.NativeDataObjectToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/IDataObject.cs
Outdated
Show resolved
Hide resolved
b670471
to
c1e4bcd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not be falling into the default interface implementations for our own types that derive from IDataObject
.
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/IDataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #11545 +/- ##
===================================================
+ Coverage 75.71735% 75.77604% +0.05868%
===================================================
Files 3152 3158 +6
Lines 635709 636985 +1276
Branches 46970 47067 +97
===================================================
+ Hits 481342 482682 +1340
+ Misses 150932 150838 -94
- Partials 3435 3465 +30
Flags with carried forward coverage won't be shown. Click here to find out more. |
7903b3c
to
9f06188
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got through about half
src/Common/tests/TestUtilities/BinaryFormatterInClipboardScope.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
Outdated
Show resolved
Hide resolved
...m.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.BinaryFormatUtilities.cs
Outdated
Show resolved
Hide resolved
...Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
...Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
...m.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.BinaryFormatUtilities.cs
Show resolved
Hide resolved
} | ||
catch (Exception ex) when (!ex.IsCriticalException()) | ||
{ | ||
// Couldn't parse for some reason, let the BinaryFormatter try to handle it. | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we catching at all? Shouldn't resolvers always throw through?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the case when we couldn't verify if type is assignable to T, I think we should return false. Otherwise resolver should resolve all possible types, which is impossible or we would always throw on type mismatch. Exception is more suitable for when serialization fails.
// This is needed to resolve fields of the requested type T when using deserializers. | ||
private readonly Dictionary<string, Type> _mscorlibTypeCache = new() | ||
{ | ||
{ "System.Byte", typeof(byte) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think that TypeName
is better here. @adamsitnik there might be some sharing we can do of core type handling? Would very much like your feedback on this file in any case. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you think that TypeName is a better option - TypeName class does not expose functions to compare TypeNames
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because then you are comparing the right components- it is the whole point of TypeName. String comparison is fraught with errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeName class does not expose functions to compare TypeNames
It does not, because different users want to compare different parts of type name. Some want to compare just the full names, some want to include assembly names, but without version. Some want to make it always a full match.
For example, SerializationRecord.TypeNameMatches
ignores assembly names: https://github.com/dotnet/runtime/blob/5d69e2dca30524a93b00cd613be218144b5f95d1/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecord.cs#L48
So we decided to not include any default comparison method just to force all the users to think what they want to compare and implement it themselves.
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
Outdated
Show resolved
Hide resolved
...Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
Show resolved
Hide resolved
...Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
Show resolved
Hide resolved
} | ||
|
||
object? baseVar = null; | ||
if (_data.TryGetValue(format, out DataStoreEntry? dse)) | ||
if (_data.TryGetValue(format, out DataStoreEntry? dse) && dse.Data is T t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be is T data
? Should probably change _data
to _mappedData
for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is T data
would be a second declaration of data
as a local variable , while it's already a function parameter
// When BinaryFormatter is not available to write the managed object to the Clipboard, | ||
// we write an exception with instructions how to modify the application to enable the binary formatter, | ||
// doing the same thing when reading. | ||
if (typeof(NotSupportedException).IsAssignableTo(typeof(T))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When do we call this with T = NotSupportedException?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would want to try to read this exception in case the write for the format failed and the exception was placed on the clipboard
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, but IIUC I don't think we ever call this method with T = NotSupportedException so we will never get into this if statement.
...Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/tests/ComDisabledTests/ClipboardComTests.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormatUtilitiesTests.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormatUtilitiesTests.cs
Outdated
Show resolved
Hide resolved
478a31c
to
a93eeab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More comments
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ITypedDataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ITypedDataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ITypedDataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ITypedDataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/ITypedDataObject.cs
Outdated
Show resolved
Hide resolved
src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
Outdated
Show resolved
Hide resolved
return true; | ||
} | ||
|
||
private static bool IsUnboundedType<T>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For anything where there is just one caller they should be an inline method or the code should just be inlined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made several functions here local
...System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.TypeNameComparer.cs
Show resolved
Hide resolved
return Matches(x, y); | ||
} | ||
|
||
public int GetHashCode(TypeName obj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did this come from? If the code is trying to avoid allocating the full name string, ok, but it should probably use HashCode.Combine()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't use the full name as an identity because it could contain assembly name in constructed types, for example, that might be type-forwarded to another assembly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did this come from?
- I didn't find anything in the runtime.. Do you have suggestions where to search?
// This is needed to resolve fields of the requested type T when using deserializers. | ||
private readonly Dictionary<string, Type> _mscorlibTypeCache = new() | ||
{ | ||
{ "System.Byte", typeof(byte) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because then you are comparing the right components- it is the whole point of TypeName. String comparison is fraught with errors.
e571f82
to
18e6274
Compare
…l)] from T as it's not sufficient, it does not preserve types recursively and can't be used with the Func NRBF deserialization is on by default, user has to opt in into the BF deserialization and opt-out from the NRBF deserialization to get full compatibility clean up in xml-doc comments
…not stored, we were lucky with switches that have "false" as a default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I provided the answers to all the questions, please feel free to tag me again.
/// | ||
/// internal static Type MyResolver(TypeName typeName) | ||
/// { | ||
/// Type[] allowedTypes = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any thoughts on our canonical example for this??
The main gotchas here are:
- different assembly versions. Example: the user has persisted and instance of
MyClass1
using an older version of the same assembly:MyClass1, MyAssembly, Version=1.0.0.0
vsMyClass1, MyAssembly, Version=2.0.0.0
. Full name does not include the version for all the type names except of generic types. - type forwarding (how to get it). I am not sure how common it is for the end users to use it, but it's quite common in runtime for the most basic types. Example: https://github.com/dotnet/runtime/blob/e5129e5323155feec25cd75e8d67986b601921d1/src/libraries/System.Private.CoreLib/src/System/DateTime.cs#L48
For both cases, it gets really tricky for generic types, where typeof(T).FullName
is different than the name exported by binary formatter (because full name of the generic type includes the assembly names of generic type arguments). That is why I was pushing so hard to include SerializationRecord.TypeNameMatches
in the public API (so the users would not need to worry about it).
So as long as you don't expect generic types with generic arguments, the example is fine. If you expect them to be common, you could extend the comparison to do something similar to https://github.com/dotnet/runtime/blob/5d69e2dca30524a93b00cd613be218144b5f95d1/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/SerializationRecord.cs#L68.
And BTW it's great that you return the type itself, as it avoids the possibility to load a hostile assembly. Example: if we have a Type
: "MyClass1, MyNiceAssembly" and a type name "MyClass1, HostileAssembly", the full names match and returning the type avoids loading the HostileAssembly
, which users could do with
if (type.FullName == typeName.FullName)
{
- return type;
+ return Type.GetType(typeName.AssemblyQualifiedName);
}
is it possible to make your Matches method public for use in this meth
The method you have referenced (SerializationRecord.TypeNameMatches
) is already public, but from the context I guess that you meant the helper method that it calls with TypeName
and Type
arguments which makes it unrelated to SerializationRecord
:
The job it does is specific to NRBF, so I am pretty sure that we can't include it in TypeName
(System.Reflection.Metadata needs to be unaware of the existence of NRBF and can't offer anything specific to NRBF). But I would not mind exposing it in System.Formats.Nrbf
, I am just not sure what would be the best way to do it (we can't expose extension methods for types that we own) and if you want to have a depedency to System.Formats.Nrbf
.
Related to ##12362
Fixes #11350
The
TryGetData
methods use NRBF deserializer by default and will fall back to use BinaryFormatter if the application opts into BinaryFormatter use in this context.The
GetData
methods have a compatibility mode when the appropriate AppContext switches are enabled but by default they can read only known and primitive types or POCOs with primitive fields. These methods in theDataObjec
t class are obsoleted.Microsoft Reviewers: Open in CodeFlow