Skip to content

Override marshalling methods for Com objects #4

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions ComMarshallingHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace SharpGen.Runtime
{
public static class ComMarshallingHelpers
{
/// <summary>
/// Instantiate a CppObject from a native pointer.
/// </summary>
/// <typeparam name="T">The CppObject class that will be returned</typeparam>
/// <param name="cppObjectPtr">The native pointer to a com object.</param>
/// <returns>An instance of T binded to the native pointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T FromPointer<T>(IntPtr cppObjectPtr) where T : CppObject
{
return MarshallingHelpers.FromPointer<T>(cppObjectPtr);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToCallbackPtr<TCallback>(ComObject comObject)
where TCallback : ICallbackable
{
if (comObject == null)
return IntPtr.Zero;
// do not create extra object in case of the same type
if (comObject.GetType() != typeof(TCallback))
{
var interfacePtr = comObject.QueryInterfaceOrNull(typeof (TCallback).GetTypeInfo().GUID);
var newInterface = (TCallback)Activator.CreateInstance(typeof(TCallback), interfacePtr);
// New pointer given from QI has one extra reference, so we need to release it, but we also need to create a holder for this pointer to make proper AddRef/Release in finalizer
if (!(newInterface is ComObject newComObject))
throw new InvalidOperationException("Created object is now COM object");
newComObject.Release();
return newComObject.NativePointer;
}
return comObject.NativePointer;
}

/// <summary>
/// Return the unmanaged C++ pointer from a <see cref="SharpGen.Runtime.ICallbackable"/> instance.
/// </summary>
/// <typeparam name="TCallback">The type of the callback.</typeparam>
/// <param name="callback">The callback.</param>
/// <returns>A pointer to the unmanaged C++ object of the callback</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToCallbackPtr<TCallback>(ICallbackable callback)
where TCallback : ICallbackable
{
// we have to make QI to normalize pointer when passing COM object into COM call
if (callback is ComObject comObject)
{
return ToCallbackPtr<TCallback>(comObject);
}
return MarshallingHelpers.ToCallbackPtr<TCallback>(callback);
}

/// <summary>
/// Return the unmanaged C++ pointer from a <see cref="SharpGen.Runtime.CppObject"/> instance.
/// </summary>
/// <typeparam name="TCallback">The type of the callback.</typeparam>
/// <param name="obj">The object.</param>
/// <returns>A pointer to the unmanaged C++ object of the callback</returns>
/// <remarks>This method is meant as a fast-path for codegen to use to reduce the number of casts.</remarks>
public static IntPtr ToCallbackPtr<TCallback>(CppObject obj)
where TCallback : ICallbackable
{
return ToCallbackPtr<TCallback>((ICallbackable) obj);
}

/// <summary>
/// Makes additional transformation for the object which was unmarshalled from unmanaged code
/// </summary>
/// <param name="obj"></param>
/// <param name="isOutParam">Indicates that the object was obtained via 'Obj**' parameter</param>
/// <returns>Transformed object</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T TransformObjectFromUnmanaged<T>(T obj, bool isOutParam) where T : CppObject
{
// objects returned via Obj** has one extra AddRef, so we need to call Release to balance references
if (isOutParam && obj is ComObject comObject)
comObject.Release();
return obj;
}

}
}
26 changes: 22 additions & 4 deletions ComObject.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -95,7 +95,7 @@ public virtual T QueryInterface<T>() where T : ComObject
IntPtr parentPtr;
var result = this.QueryInterface(typeof(T).GetTypeInfo().GUID, out parentPtr);
result.CheckError();
return FromPointer<T>(parentPtr);
return MarshallingHelpers.FromPointer<T>(parentPtr);
}

/// <summary>
Expand Down Expand Up @@ -168,7 +168,7 @@ public static T QueryInterfaceOrNull<T>(IntPtr comPointer) where T : ComObject
var guid = typeof(T).GetTypeInfo().GUID;
IntPtr pointerT;
var result = (Result)Marshal.QueryInterface(comPointer, ref guid, out pointerT);
return (result.Failure) ? null : FromPointer<T>(pointerT);
return (result.Failure) ? null : ComMarshallingHelpers.FromPointer<T>(pointerT);
}

///<summary>
Expand All @@ -181,7 +181,7 @@ public static T QueryInterfaceOrNull<T>(IntPtr comPointer) where T : ComObject
/// <unmanaged-short>IUnknown::QueryInterface</unmanaged-short>
public virtual T QueryInterfaceOrNull<T>() where T : ComObject
{
return FromPointer<T>(QueryInterfaceOrNull(typeof(T).GetTypeInfo().GUID));
return ComMarshallingHelpers.FromPointer<T>(QueryInterfaceOrNull(typeof(T).GetTypeInfo().GUID));
}

///<summary>
Expand All @@ -196,6 +196,24 @@ protected void QueryInterfaceFrom<T>(T fromObject) where T : ComObject
NativePointer = parentPtr;
}

// Called with old ptr
protected override unsafe void NativePointerUpdating()
{
// make Release when dropping the pointer
if (_nativePointer != null)
Release();
base.NativePointerUpdating();
}

// Called with new ptr
protected override unsafe void NativePointerUpdated(IntPtr oldNativePointer)
{
// when taking new pointer need to make AddRef
if (_nativePointer != null)
AddRef();
base.NativePointerUpdated(oldNativePointer);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion SharpGen.Runtime.COM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@
<PackageReference Include="SharpGen.Doc.Msdn.Tasks" Version="1.1.0" PrivateAssets="All" />
<PackageReference Include="SharpGenTools.Sdk" Version="1.2.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<SharpGenGlobalNamespaceOverrides Include="MarshallingHelpers" Override="ComMarshallingHelpers" />
</ItemGroup>
</Project>