Skip to content

Commit

Permalink
Add Variant marshalling (#1142)
Browse files Browse the repository at this point in the history
* Add Variant marshalling

* Add variant bool and OLE currency marshalling

* Add missing interop

* Use Type.GUID instead of querying for custom attributes

* Do not support reverse direction for marshalling of VARIANT
  • Loading branch information
kant2002 authored May 28, 2021
1 parent d707f62 commit 662f1db
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,16 @@ internal static DateTime OleDateTimeToDateTime(double value)
return DateTime.FromOADate(value);
}

internal static long DecimalToOleCurrency(decimal value)
{
return decimal.ToOACurrency(value);
}

internal static decimal OleCurrencyToDecimal(long value)
{
return decimal.FromOACurrency(value);
}

internal static unsafe string BstrBufferToString(char* buffer)
{
if (buffer == null)
Expand Down Expand Up @@ -549,6 +559,18 @@ o is string ||
Marshal.DestroyStructure(address, o.GetType());
}

internal static unsafe void CleanupVariant(IntPtr pDstNativeVariant)
{
#if TARGET_WINDOWS
#pragma warning disable CA1416
Variant* data = (Variant*)pDstNativeVariant;
data->Clear();
#pragma warning restore CA1416
#else
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
#endif
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct ModuleFixupCell
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
<ItemGroup Condition="'$(TargetsWindows)'=='true'">
<Compile Include="Microsoft\Win32\SafeHandles\SafeThreadPoolIOHandle.cs" />
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.Windows.cs" />
<Compile Include="$(CommonPath)\System\Runtime\InteropServices\Variant.cs">
<Link>System\Runtime\InteropServices\Variant.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GetCurrentThreadId.cs">
<Link>Interop\Windows\Kernel32\Interop.GetCurrentThreadId.cs</Link>
</Compile>
Expand All @@ -273,6 +276,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\Ole32\Interop.CoGetApartmentType.cs">
<Link>Interop\Windows\Ole32\Interop.CoGetApartmentType.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\OleAut32\Interop.VariantClear.cs">
<Link>Interop\Windows\OleAut32\Interop.VariantClear.cs</Link>
</Compile>
<Compile Include="System\Environment.CoreRT.Windows.cs" />
<Compile Include="System\Environment.CoreRT.Win32.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GetTickCount64.cs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace System.Runtime.InteropServices
{
public static partial class Marshal
{
private const int DISP_E_PARAMNOTFOUND = unchecked((int)0x80020004);

public static int GetHRForException(Exception? e)
{
return PInvokeMarshal.GetHRForException(e);
Expand Down Expand Up @@ -69,7 +71,17 @@ public static int FinalReleaseComObject(object o)
[SupportedOSPlatform("windows")]
public static IntPtr GetComInterfaceForObject(object o, Type T)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
if (o is null)
{
throw new ArgumentNullException(nameof(o));
}

if (T is null)
{
throw new ArgumentNullException(nameof(T));
}

return ComWrappers.ComInterfaceForObject(o, T.GUID);
}

[SupportedOSPlatform("windows")]
Expand All @@ -81,7 +93,7 @@ public static IntPtr GetComInterfaceForObject(object o, Type T, CustomQueryInter
[SupportedOSPlatform("windows")]
public static IntPtr GetComInterfaceForObject<T, TInterface>([DisallowNull] T o)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
return GetComInterfaceForObject(o!, typeof(T));
}

[SupportedOSPlatform("windows")]
Expand All @@ -93,7 +105,12 @@ public static IntPtr GetComInterfaceForObject<T, TInterface>([DisallowNull] T o)
[SupportedOSPlatform("windows")]
public static IntPtr GetIDispatchForObject(object o)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
if (o is null)
{
throw new ArgumentNullException(nameof(o));
}

return ComWrappers.ComInterfaceForObject(o, new Guid(0x00020400, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) /* IID_IDispatch */);
}

[SupportedOSPlatform("windows")]
Expand All @@ -103,15 +120,116 @@ public static IntPtr GetIUnknownForObject(object o)
}

[SupportedOSPlatform("windows")]
public static void GetNativeVariantForObject(object? obj, IntPtr pDstNativeVariant)
public static unsafe void GetNativeVariantForObject(object? obj, IntPtr pDstNativeVariant)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
if (pDstNativeVariant == IntPtr.Zero)
{
throw new ArgumentNullException(nameof(pDstNativeVariant));
}

Variant* data = (Variant*)pDstNativeVariant;
if (obj == null)
{
data->VariantType = VarEnum.VT_EMPTY;
return;
}

switch (obj)
{
// Int and String most used types.
case int value:
data->AsI4 = value;
break;
case string value:
data->AsBstr = value;
break;

case bool value:
data->AsBool = value;
break;
case byte value:
data->AsUi1 = value;
break;
case sbyte value:
data->AsI1 = value;
break;
case short value:
data->AsI2 = value;
break;
case ushort value:
data->AsUi2 = value;
break;
case uint value:
data->AsUi4 = value;
break;
case long value:
data->AsI8 = value;
break;
case ulong value:
data->AsUi8 = value;
break;
case float value:
data->AsR4 = value;
break;
case double value:
data->AsR8 = value;
break;
case DateTime value:
data->AsDate = value;
break;
case decimal value:
data->AsDecimal = value;
break;
case char value:
data->AsUi2 = value;
break;
case BStrWrapper value:
data->AsBstr = value.WrappedObject;
break;
case CurrencyWrapper value:
data->AsCy = value.WrappedObject;
break;
case UnknownWrapper value:
data->AsUnknown = value.WrappedObject;
break;
case DispatchWrapper value:
data->AsDispatch = value.WrappedObject;
break;
case ErrorWrapper value:
data->AsError = value.ErrorCode;
break;
case VariantWrapper value:
// Do not want to implement that yet.
throw new NotSupportedException();
case DBNull value:
data->SetAsNULL();
break;
case System.Reflection.Missing value:
data->AsError = DISP_E_PARAMNOTFOUND;
break;
default:
var type = obj.GetType();
if (type.IsValueType)
{
throw new NotSupportedException();
}
else if (type.IsArray)
{
// SAFEARRAY implementation goes here.
throw new NotSupportedException();
}
else
{
data->AsDispatch = obj;
}
break;
}
}

[SupportedOSPlatform("windows")]
public static void GetNativeVariantForObject<T>(T? obj, IntPtr pDstNativeVariant)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
GetNativeVariantForObject((object?)obj, pDstNativeVariant);
}

[SupportedOSPlatform("windows")]
Expand All @@ -127,9 +245,50 @@ public static object GetObjectForIUnknown(IntPtr pUnk)
}

[SupportedOSPlatform("windows")]
public static object? GetObjectForNativeVariant(IntPtr pSrcNativeVariant)
public static unsafe object? GetObjectForNativeVariant(IntPtr pSrcNativeVariant)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
if (pSrcNativeVariant == IntPtr.Zero)
{
throw new ArgumentNullException(nameof(pSrcNativeVariant));
}

Variant* data = (Variant*)pSrcNativeVariant;

if (data->IsEmpty)
{
return null;
}

switch (data->VariantType)
{
case VarEnum.VT_NULL:
return DBNull.Value;

case VarEnum.VT_I1: return data->AsI1;
case VarEnum.VT_I2: return data->AsI2;
case VarEnum.VT_I4: return data->AsI4;
case VarEnum.VT_I8: return data->AsI8;
case VarEnum.VT_UI1: return data->AsUi1;
case VarEnum.VT_UI2: return data->AsUi2;
case VarEnum.VT_UI4: return data->AsUi4;
case VarEnum.VT_UI8: return data->AsUi8;
case VarEnum.VT_INT: return data->AsInt;
case VarEnum.VT_UINT: return data->AsUint;
case VarEnum.VT_BOOL: return data->AsBool;
case VarEnum.VT_ERROR: return data->AsError;
case VarEnum.VT_R4: return data->AsR4;
case VarEnum.VT_R8: return data->AsR8;
case VarEnum.VT_DECIMAL: return data->AsDecimal;
case VarEnum.VT_CY: return data->AsCy;
case VarEnum.VT_DATE: return data->AsDate;
case VarEnum.VT_BSTR: return data->AsBstr;
case VarEnum.VT_UNKNOWN: return data->AsUnknown;
case VarEnum.VT_DISPATCH: return data->AsDispatch;

default:
// Other VARIANT types not supported yet.
throw new NotSupportedException();
}
}

[return: MaybeNull]
Expand Down
16 changes: 15 additions & 1 deletion src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ internal static TypeDesc GetNativeTypeFromMarshallerKind(TypeDesc type,
case MarshallerKind.CBool:
return context.GetWellKnownType(WellKnownType.Byte);

case MarshallerKind.VariantBool:
return context.GetWellKnownType(WellKnownType.Int16);

case MarshallerKind.Enum:
case MarshallerKind.BlittableStruct:
case MarshallerKind.Decimal:
Expand Down Expand Up @@ -162,6 +165,12 @@ internal static TypeDesc GetNativeTypeFromMarshallerKind(TypeDesc type,
case MarshallerKind.ComInterface:
return context.GetWellKnownType(WellKnownType.IntPtr);

case MarshallerKind.Variant:
return InteropTypes.GetVariant(context);

case MarshallerKind.OleCurrency:
return context.GetWellKnownType(WellKnownType.Int64);

case MarshallerKind.Unknown:
default:
throw new NotSupportedException();
Expand Down Expand Up @@ -254,6 +263,9 @@ internal static MarshallerKind GetMarshallerKind(
case NativeTypeKind.I1:
return MarshallerKind.CBool;

case NativeTypeKind.VariantBool:
return MarshallerKind.VariantBool;

default:
return MarshallerKind.Invalid;
}
Expand Down Expand Up @@ -355,6 +367,8 @@ internal static MarshallerKind GetMarshallerKind(
return MarshallerKind.Decimal;
else if (nativeType == NativeTypeKind.LPStruct && !isField)
return MarshallerKind.BlittableStructPtr;
else if (nativeType == NativeTypeKind.Currency)
return MarshallerKind.OleCurrency;
else
return MarshallerKind.Invalid;
}
Expand Down Expand Up @@ -566,7 +580,7 @@ internal static MarshallerKind GetMarshallerKind(
|| nativeType == NativeTypeKind.IUnknown)
return MarshallerKind.ComInterface;
else
return MarshallerKind.Invalid;
return MarshallerKind.Variant;
}
else if (InteropTypes.IsStringBuilder(context, type))
{
Expand Down
Loading

0 comments on commit 662f1db

Please sign in to comment.