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 RCW support #992

Merged
merged 34 commits into from
Apr 29, 2021
Merged

Add RCW support #992

merged 34 commits into from
Apr 29, 2021

Conversation

kant2002
Copy link
Contributor

@kant2002 kant2002 commented Apr 21, 2021

See #306

  • Implemented RCW creation for COM interfaces.
  • Implemented returning original object, if COM interface is ManagedObjectWrapper
  • Do not return original object if unique instance requested.

@kant2002 kant2002 changed the title Add RCW support Add CCW support Apr 21, 2021
@jkotas
Copy link
Member

jkotas commented Apr 21, 2021

Add some basic tests?

@kant2002
Copy link
Contributor Author

@jkotas I am definitely ready for you to review this.

@jkotas
Copy link
Member

jkotas commented Apr 26, 2021

Add CCW support

This shold say RCW. I had it swapped in the tracking issue - I have fixed it there.

@kant2002 kant2002 changed the title Add CCW support Add RCW support Apr 26, 2021
@kant2002
Copy link
Contributor Author

I had it swapped in the tracking issue - I have fixed it there.

Thanks. It was a big linguistic battle inside me. Since I have a lot of other question, in order to not too much distract you, I decide that I do not understand some language tricks.


~NativeObjectWrapper()
{
_comWrappers._rcwCache.Remove(_externalComObject);
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to take the lock?

@@ -317,7 +349,7 @@ public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, Create
/// <remarks>
/// If <paramref name="impl" /> is <c>null</c>, the global instance (if registered) will be used.
/// </remarks>
private static bool TryGetOrCreateObjectForComInstanceInternal(
private static unsafe bool TryGetOrCreateObjectForComInstanceInternal(
ComWrappers impl,
Copy link
Member

Choose a reason for hiding this comment

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

Can this be an instance method instead? I do not see the point of passing in impl explicitly. (CreateCCW has the same issue.)

{
if (impl._rcwCache.TryGetValue(externalComObject, out var existingHandle))
{
if (existingHandle.IsAllocated)
Copy link
Member

Choose a reason for hiding this comment

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

IsAllocated should be always true here. We should not ever get unallocated GCHandle from the Dictionary.

}
else
{
GCHandle proxyHandle = GCHandle.Alloc(retValue, GCHandleType.Weak);
Copy link
Member

Choose a reason for hiding this comment

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

For symetry, it would be nice to allocated the GCHandle and add it to the table in NativeObjectWrapper constructor - since the opposite is done in the NativeObjectWrapper finalizer.

@kant2002
Copy link
Contributor Author

@jkotas I think I address your feedback.

public delegate* unmanaged<IntPtr, uint> Release;
private IntPtr _externalComObject;
private ComWrappers _comWrappers;
public GCHandle ProxyHandle;
Copy link
Member

Choose a reason for hiding this comment

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

I would keep it as _proxyHandle. (Some of the places in the file are actually still refering to the field as _proxyHandle.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

~NativeObjectWrapper()
{
_comWrappers.RemoveRCWFromCache(_externalComObject);
_proxyHandle.Free();
Copy link
Member

Choose a reason for hiding this comment

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

This can be unallocated handle if GCHandle.Alloc in the constructor fails. This should get the IsAllocated check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@kant2002
Copy link
Contributor Author

@jkotas I think I address your feedback again


StoreManagedValue(codeStream);

codeStream.EmitLabel(lNull);
Copy link
Member

Choose a reason for hiding this comment

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

Is it correct to skip storing the managed value for null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think yes. At least this pattern is everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Managed value initialized as null by runtime guarantee, so load/store usually going in pairs. Then somebody do actual return. At least this is how I understand this.

Copy link
Member

Choose a reason for hiding this comment

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

Managed value initialized as null by runtime guarantee,

Where is it guaranteed?

I went through all TransformNativeToManaged methods and all of them end with Store. I have not found one that skips the store.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

LayoutClassPtrMarshaller, AsAnyMarshaller for example

Copy link
Member

Choose a reason for hiding this comment

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

The managed value for these marshallers is a pointer to the structure that they are marshalling. It is not the case here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What I was trying to say that

.method public hidebysig specialname 
        instance int32  get_Set() cil managed
{
  .maxstack  1
  .locals init (int32 V_0)
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method SetProperty::get_Set

is equivalent for

int Set => 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I miss your previous message. Will have to check what kind of IL actually generated to validate that what I'm saying is correct.

Copy link
Member

Choose a reason for hiding this comment

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

You may be right, but I would still simplify this to a simple ConvertNativeComInterfaceToManaged. If the store is redudandant, the JIT will eliminate it after inlining ConvertNativeComInterfaceToManaged.

@jkotas
Copy link
Member

jkotas commented Apr 29, 2021

LGTM otherwise. Thank you!

@jkotas jkotas merged commit c09295d into dotnet:feature/NativeAOT Apr 29, 2021
@kant2002 kant2002 deleted the kant/com-rcw branch April 29, 2021 13:20
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.

2 participants