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

Convert OAVariant interop to managed #100176

Merged
merged 37 commits into from
May 14, 2024
Merged

Conversation

huoyaoyuan
Copy link
Member

Still draft because I'm not sure of feature consistency or coverage here.

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Mar 23, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Mar 23, 2024
@jkotas jkotas added area-Interop-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Mar 23, 2024
@huoyaoyuan huoyaoyuan marked this pull request as ready for review March 25, 2024 12:40
@huoyaoyuan
Copy link
Member Author

As my understanding there are two flavors of Variant: COM Variant and OLE Variant.
Should I include more refactor of Variant marshalling in this?

@huoyaoyuan
Copy link
Member Author

Well COM uses OLE VARIANT(System.Runtime.InteropServices.Marshalling.ComVariant), System.Variant/VariantData/CVType are CLR only intermediate representations.

We should favor ComVariant and avoid the System.Variant layer as possible.

@huoyaoyuan huoyaoyuan marked this pull request as draft March 25, 2024 14:02
@huoyaoyuan
Copy link
Member Author

A relevant bug (?) discovered:

typeof(DateTime),
typeof(TimeSpan),
typeof(object),
typeof(decimal),
null, // Enums - what do we do here?
typeof(Missing),
typeof(DBNull),

CV_DATETIME = 0x10, // ELEMENT_TYPE_BYREF
CV_TIMESPAN = 0x11, // ELEMENT_TYPE_VALUETYPE
CV_OBJECT = ELEMENT_TYPE_CLASS,
CV_DECIMAL = 0x13, // ELEMENT_TYPE_UNUSED1
CV_CURRENCY = 0x14, // ELEMENT_TYPE_ARRAY
CV_ENUM = 0x15, //
CV_MISSING = 0x16, //
CV_NULL = 0x17, //

In managed OAVariantLib, it lacks a slot for CURRENCY. So probably if you call ChangeType of OleAutBinder with DBNull (can it ever happen?), you can get Missing.

This comes from .NET Framework and probably we won't ever fix it without refactoring 😆

@huoyaoyuan huoyaoyuan changed the title Convert most of variant to managed Convert OAVariant interop to managed Mar 26, 2024
@huoyaoyuan huoyaoyuan marked this pull request as ready for review March 26, 2024 14:43
@huoyaoyuan
Copy link
Member Author

I think it's at a reasonable step point now. All OAVariant related path are cleaned up.

@AaronRobinsonMSFT
Copy link
Member

Spent several hours to figure out how it's used.

In short, it's only used by values marshaled from COM to CLR, including parameters in COM to CLR call and ref/returns in CLR to COM call. The values are first marshaled using the "standard" OleVariant::MarshalObjectForOleVariant based on the type specified in VARIANT, or corresponding marshaller assigned by GenerateDispParamMarshaler. Then if it doesn't fit type declaration at managed side, the value will converted back to VARIANT using OAVariant::ToOAVariant, and casted with VarChangeTypeEx, then converted to managed again using OAVariant::FromOAVariant.

So it seems not possible to cover OleAutBinder solely without going through the "standard" conversion first.

Nice. These are great learnings as I don't think many of us have given much thought into how far these systems have drifted from when they were originally written. Do you have any thoughts on what this new information means for how to test and/or how to think about the utility of this code?

@huoyaoyuan
Copy link
Member Author

huoyaoyuan commented Mar 31, 2024

There is also a interesting buggy behavior around this code:
ref modifier is included in ref values in the caller ForwardCallToInvokeMember, resulting ChangeType always called even when the type matches. Combining with the indexing bug in OAVariantLib.ClassTypes, this means that a ref DBNull parameter will always throw. It's end up observable.

DBNull is handled earlier by OleAutBinder without passed to OAVariantLib. It looks unavoidable to depend on the behavior of VariantChangeTypeEx.

Do you have any thoughts on what this new information means for how to test and/or how to think about the utility of this code?

For this PR, the cases are now narrowed, because the possible inputs are now limited to anything returned from OleVariant::MarshalObjectForOleVariant, not anything possibly processed by new Variant.

I still think we need a standalone PR for test, and possibly compare the behavior with .NET Framework since it will be more correct.

The test should focus on the effect of this code: what level of type mismatch are allowed in parameters, ref and returns. What are rejected causing by this code should also be covered.

The test shouldn't be exhausting the conversion matrix, nor testing the behavior of VariantChangeTypeEx which is indeed external. It should use the cases with common sense like converting between signed and unsigned positive numbers.

@AaronRobinsonMSFT AaronRobinsonMSFT marked this pull request as draft April 22, 2024 20:08
@huoyaoyuan
Copy link
Member Author

I've cleaned up a bit based on tests in #101841. Only possible types from OleVariant::MarshalObjectForOleVariant are handled.
Also made an assumption that VariantChangeTypeEx won't return a VARENUM that isn't requested. It's technically external but reasonable.

Requesting for another round of review.

@huoyaoyuan huoyaoyuan marked this pull request as ready for review May 5, 2024 08:50
Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @huoyaoyuan. Appreciate your perseverance.

@AaronRobinsonMSFT AaronRobinsonMSFT merged commit 778cc84 into dotnet:main May 14, 2024
149 checks passed
@huoyaoyuan huoyaoyuan deleted the variant branch May 14, 2024 04:28
Ruihan-Yin pushed a commit to Ruihan-Yin/runtime that referenced this pull request May 30, 2024
* Convert BoxEnum to managed

* SetFieldsObject to managed

* Handle decimal and other CV_OBJECT

* Managed ToOAVariant and FromOAVariant

* Marshal for IUnknown and IDispatch

* VariantChangeTypeEx interop

* Use managed reflection for System.Drawing.Color conversion

* Use MarshalNative for IDispatch/IUnknown marshalling

* Move Color conversion to Variant

* Respect VTToCV mapping

* Improve test type coverage

* Test for values in ReturnToManaged

---------

Co-authored-by: Aaron Robinson <arobins@microsoft.com>
@github-actions github-actions bot locked and limited conversation to collaborators Jun 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Interop-coreclr community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants