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

Add Variant marshalling #1142

Merged
merged 16 commits into from
May 28, 2021

Conversation

kant2002
Copy link
Contributor

@kant2002 kant2002 commented May 19, 2021

@kant2002
Copy link
Contributor Author

I did not test that yet. Just trying to gather feedback on the direction of the change.

kant2002 added a commit to kant2002/runtime that referenced this pull request May 20, 2021
I found this one when implement VARIANT marshalling for NativeAOT. Without that I have to resort to duplicate implementation in that class.
PR where I discover this - dotnet/runtimelab#1142
Problem is `Marshal.PtrToStringBSTR` throw exception when passed `IntPtr.Zero`, but `Marshal.StringToBSTR` produce `IntPtr.Zero` when given null.
@kant2002
Copy link
Contributor Author

With half backed implementation I hit this

image

Seems to be IDispatch is inevitable. Will need advice after this would be done.

@kant2002
Copy link
Contributor Author

Not tested

  • Marshal_XXX_Boolean
  • Marshal_XXX_Currency
  • Marshal_XXX_Object
  • Marshal_XXX_Object_IUnknown

Current tests in the Interop\Pinvoke\Variant depends on the built-in marshalling. Is it possible split tests that built-in marshalling would be not required. Or maybe I can create similar set of test for ComWrappers code path which will share most of code with current tests by reusing files? Do I need go to runtime for that?

@jkotas
Copy link
Member

jkotas commented May 20, 2021

Is it possible split tests that built-in marshalling would be not required.

Yes. Note that there is ongoing work to split the tests that require built-in COM marshalling in dotnet/runtime, so contributing to that would be definitely welcomed:

dotnet/runtime#50500
dotnet/runtime#51265

@jkotas
Copy link
Member

jkotas commented May 20, 2021

Seems to be IDispatch is inevitable. Will need advice after this would be done.

Does this really only need to QueryInterface for IID_IDispatch?

@kant2002
Copy link
Contributor Author

Does this really only need to QueryInterface for IID_IDispatch?

Yes. Pedantic me want to implement Marshal.GetObjectForComInterface<object, T> which is overkill or this specific case. If I change Variant implementation in runtime to just QI to IDispatch that should just works.

AaronRobinsonMSFT pushed a commit to dotnet/runtime that referenced this pull request May 20, 2021
* Return null when Variant contains BSTR

I found this one when implement VARIANT marshalling for NativeAOT. Without that I have to resort to duplicate implementation in that class.
PR where I discover this - dotnet/runtimelab#1142
Problem is `Marshal.PtrToStringBSTR` throw exception when passed `IntPtr.Zero`, but `Marshal.StringToBSTR` produce `IntPtr.Zero` when given null.

* Update nullable annotations
kant2002 added a commit to kant2002/runtime that referenced this pull request May 21, 2021
In NativeAOT scenario this allow to not rely on reflection to get GUID of the IDispatch, since it is well known. 
Related to dotnet/runtimelab#1142
@kant2002
Copy link
Contributor Author

Locally seems to be working. Build issues seems to be unrelated. But current PR I rely on the changes to runtime which is not yet landed, and as such not integrated.

ComboBox now demands IDispatch implementation from my ComWrappers instance. Sigh.
Will try to use https://github.com/dotnet/samples/blob/main/core/interop/comwrappers/IDispatch/AnyObjectProxy.cs as example how to do that. But overall work on the Variant marshalling seems to be done in technical sense. Ready to accept feedback.

@kant2002
Copy link
Contributor Author

Actually three lines may not working

https://github.com/dotnet/runtime/blob/01b7e73cd378145264a7cb7a09365b41ed42b240/src/tests/Interop/PInvoke/Variant/VariantTest.cs#L46-L48

It's not clear for me

@jkotas
Copy link
Member

jkotas commented May 21, 2021

First line means that TimeSpan not marshalled, or specifically TimeSpan.Zero cannot be marshalled.

Yeah, that looks odd. It would be useful to run it on CoreCLR to see where it is comming from.

Does any not implemented cases should throw NotSupportedException?

It should throw whatever CoreCLR throws. In general, there is no much consistency around NotSupportedException and similar exceptions - see dotnet/runtime#22069 (comment) for some stats.

The full Variant marshalling is very complex. It is fine to only implement a reasonable subset.

@kant2002
Copy link
Contributor Author

The full Variant marshalling is very complex. It is fine to only implement a reasonable subset.

Can we leave this 3 cases? I would check TimeSpan for sure, but would like to move forward if possible

ghost pushed a commit to dotnet/runtime that referenced this pull request May 21, 2021
In NativeAOT scenario this allow to not rely on reflection to get GUID of the IDispatch, since it is well known. 
Related to dotnet/runtimelab#1142
kant2002 added a commit to kant2002/runtimelab that referenced this pull request May 24, 2021
In NativeAOT scenario this allow to not rely on reflection to get GUID of the IDispatch, since it is well known. 
Related to dotnet#1142
@kant2002
Copy link
Contributor Author

@jkotas build issues seems to be unrelated. Do I miss anything else here?

@jkotas
Copy link
Member

jkotas commented May 24, 2021

It is failing with:

2021-05-24T09:16:57.1984007Z   Assertion failed 'genActualType(lclTyp) == genActualType(op1->gtType) || (genActualType(lclTyp) == TYP_I_IMPL && op1->IsLocalAddrExpr() != nullptr) || (genActualType(lclTyp) == TYP_I_IMPL && (op1->gtType == TYP_BYREF || op1->gtType == TYP_REF)) || (genActualType(op1->gtType) == TYP_I_IMPL && lclTyp == TYP_BYREF) || (varTypeIsFloating(lclTyp) && varTypeIsFloating(op1->TypeGet())) || ((genActualType(lclTyp) == TYP_BYREF) && genActualType(op1->TypeGet()) == TYP_REF) : Possibly bad IL with CEE_stloc.s at offset 0013h (op1=ref op2=NULL stkDepth=0)' in 'Internal.CompilerGenerated.<Module>:ReverseDelegateStub__Get(int,long,long,int,long,long,long):int' during 'Importation' (IL size 84)

It looks like bad IL generated by the new marshaler.

@kant2002
Copy link
Contributor Author

kant2002 commented May 25, 2021

@jkotas this was fixed in ea76dd0 as part of #1102 . Exception was part of System.Management.WmiNetUtilsHelper.GetNames

internal delegate int GetNames(
  int vFunc,
  IntPtr pWbemClassObject,
  [In][MarshalAs(UnmanagedType.LPWStr)] string wszQualifierName,
  [In] int lFlags,
  [In] ref object pQualifierVal, 
->>  [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out string[] pNames);

@kant2002
Copy link
Contributor Author

Other location is

// System.Management.WmiNetUtilsHelper.GetDemultiplexedStub

internal delegate int GetDemultiplexedStub(
  [In][MarshalAs(UnmanagedType.IUnknown)] object pIUnknown,
  [In] bool isLocal, 
-->  [MarshalAs(UnmanagedType.IUnknown)] out object ppIUnknown);

For me this seems as unrelated change. (This one actually exposes another issue with IUnknown marshalling). I need more time to understand what's going on.

@kant2002
Copy link
Contributor Author

So based on my understanding, problem is with delegates who has [In][Out]ref object as the parameter.
Current implementation of marshaller does not handle that. And seems I would like somehow to opt-out of that temporary, but do not know how. If possible and somebody know any hints, I would appreciate.

This indicates missing coverage in tests. If I have courage, I will find out what case trigger issue, then I may create test. Should I do that, if so, where should I do that?

@jkotas
Copy link
Member

jkotas commented May 27, 2021

Throw exception for the unhandled case before it crashes the JIT.

@kant2002
Copy link
Contributor Author

Phew, I'm finally understand where to put exception. My understanding that this was related to #176 because I was able to replicate with

Get xxx = Marshal.GetDelegateForFunctionPointer<Get>(IntPtr.Zero);
internal delegate int Get(ref object pVal);

@kant2002
Copy link
Contributor Author

@jkotas anything here?

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you!

@jkotas jkotas merged commit 662f1db into dotnet:feature/NativeAOT May 28, 2021
@kant2002 kant2002 deleted the kant/variant-marshalling branch May 28, 2021 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants