|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Versioning;
using System.StubHelpers;
namespace System.Runtime.InteropServices
{
/// <summary>
/// This class contains methods that are mainly used to marshal between unmanaged
/// and managed types.
/// </summary>
public static partial class Marshal
{
#if FEATURE_COMINTEROP
/// <summary>
/// IUnknown is {00000000-0000-0000-C000-000000000046}
/// </summary>
internal static readonly Guid IID_IUnknown = new Guid(0, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
/// <summary>
/// IDispatch is {00020400-0000-0000-C000-000000000046}
/// </summary>
internal static readonly Guid IID_IDispatch = new Guid(0x00020400, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
#endif //FEATURE_COMINTEROP
internal static int SizeOfHelper(RuntimeType t, [MarshalAs(UnmanagedType.Bool)] bool throwIfNotMarshalable)
=> SizeOfHelper(new QCallTypeHandle(ref t), throwIfNotMarshalable);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_SizeOfHelper")]
private static partial int SizeOfHelper(QCallTypeHandle t, [MarshalAs(UnmanagedType.Bool)] bool throwIfNotMarshalable);
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "Trimming doesn't affect types eligible for marshalling. Different exception for invalid inputs doesn't matter.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static IntPtr OffsetOf(Type t, string fieldName)
{
ArgumentNullException.ThrowIfNull(t);
FieldInfo f = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ??
throw new ArgumentException(SR.Format(SR.Argument_OffsetOfFieldNotFound, t.FullName), nameof(fieldName));
if (f is not RtFieldInfo rtField)
{
throw new ArgumentException(SR.Argument_MustBeRuntimeFieldInfo, nameof(fieldName));
}
nint offset = OffsetOf(rtField.GetFieldDesc());
GC.KeepAlive(rtField);
return offset;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_OffsetOf")]
private static partial nint OffsetOf(IntPtr pFD);
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("ReadByte(Object, Int32) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static byte ReadByte(object ptr, int ofs)
{
return ReadValueSlow(ptr, ofs, ReadByte);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("ReadInt16(Object, Int32) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static short ReadInt16(object ptr, int ofs)
{
return ReadValueSlow(ptr, ofs, ReadInt16);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("ReadInt32(Object, Int32) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static int ReadInt32(object ptr, int ofs)
{
return ReadValueSlow(ptr, ofs, ReadInt32);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("ReadInt64(Object, Int32) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
#pragma warning disable CS0618 // Type or member is obsolete
public static long ReadInt64([MarshalAs(UnmanagedType.AsAny), In] object ptr, int ofs)
#pragma warning restore CS0618 // Type or member is obsolete
{
return ReadValueSlow(ptr, ofs, ReadInt64);
}
/// <summary>Read value from marshaled object (marshaled using AsAny).</summary>
/// <remarks>
/// It's quite slow and can return back dangling pointers. It's only there for backcompat.
/// People should instead use the IntPtr overloads.
/// </remarks>
private static unsafe T ReadValueSlow<T>(object ptr, int ofs, Func<IntPtr, int, T> readValueHelper)
{
// Consumers of this method are documented to throw AccessViolationException on any AV
if (ptr is null)
{
throw new AccessViolationException();
}
const int Flags =
(int)AsAnyMarshaler.AsAnyFlags.In |
(int)AsAnyMarshaler.AsAnyFlags.IsAnsi |
(int)AsAnyMarshaler.AsAnyFlags.IsBestFit;
MngdNativeArrayMarshaler.MarshalerState nativeArrayMarshalerState = default;
AsAnyMarshaler marshaler = new AsAnyMarshaler(new IntPtr(&nativeArrayMarshalerState));
IntPtr pNativeHome = IntPtr.Zero;
try
{
pNativeHome = marshaler.ConvertToNative(ptr, Flags);
return readValueHelper(pNativeHome, ofs);
}
finally
{
marshaler.ClearNative(pNativeHome);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("WriteByte(Object, Int32, Byte) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static void WriteByte(object ptr, int ofs, byte val)
{
WriteValueSlow(ptr, ofs, val, WriteByte);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("WriteInt16(Object, Int32, Int16) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static void WriteInt16(object ptr, int ofs, short val)
{
WriteValueSlow(ptr, ofs, val, WriteInt16);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("WriteInt32(Object, Int32, Int32) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static void WriteInt32(object ptr, int ofs, int val)
{
WriteValueSlow(ptr, ofs, val, WriteInt32);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("WriteInt64(Object, Int32, Int64) may be unavailable in future releases.")]
[RequiresDynamicCode("Marshalling code for the object might not be available")]
public static void WriteInt64(object ptr, int ofs, long val)
{
WriteValueSlow(ptr, ofs, val, WriteInt64);
}
/// <summary>
/// Write value into marshaled object (marshaled using AsAny) and propagate the
/// value back. This is quite slow and can return back dangling pointers. It is
/// only here for backcompat. People should instead use the IntPtr overloads.
/// </summary>
private static unsafe void WriteValueSlow<T>(object ptr, int ofs, T val, Action<IntPtr, int, T> writeValueHelper)
{
// Consumers of this method are documented to throw AccessViolationException on any AV
if (ptr is null)
{
throw new AccessViolationException();
}
const int Flags =
(int)AsAnyMarshaler.AsAnyFlags.In |
(int)AsAnyMarshaler.AsAnyFlags.Out |
(int)AsAnyMarshaler.AsAnyFlags.IsAnsi |
(int)AsAnyMarshaler.AsAnyFlags.IsBestFit;
MngdNativeArrayMarshaler.MarshalerState nativeArrayMarshalerState = default;
AsAnyMarshaler marshaler = new AsAnyMarshaler(new IntPtr(&nativeArrayMarshalerState));
IntPtr pNativeHome = IntPtr.Zero;
try
{
pNativeHome = marshaler.ConvertToNative(ptr, Flags);
writeValueHelper(pNativeHome, ofs, val);
marshaler.ConvertToManaged(ptr, pNativeHome);
}
finally
{
marshaler.ClearNative(pNativeHome);
}
}
/// <summary>
/// Get the last platform invoke error on the current thread
/// </summary>
/// <returns>The last platform invoke error</returns>
/// <remarks>
/// The last platform invoke error corresponds to the error set by either the most recent platform
/// invoke that was configured to set the last error or a call to <see cref="SetLastPInvokeError(int)" />.
/// </remarks>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern int GetLastPInvokeError();
/// <summary>
/// Set the last platform invoke error on the current thread
/// </summary>
/// <param name="error">Error to set</param>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void SetLastPInvokeError(int error);
private static void PrelinkCore(MethodInfo m)
{
if (m is not RuntimeMethodInfo rmi)
{
throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(m));
}
InternalPrelink(((IRuntimeMethodInfo)rmi).Value);
GC.KeepAlive(rmi);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_Prelink")]
private static partial void InternalPrelink(RuntimeMethodHandleInternal m);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern /* struct _EXCEPTION_POINTERS* */ IntPtr GetExceptionPointers();
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("GetExceptionCode() may be unavailable in future releases.")]
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern int GetExceptionCode();
/// <summary>
/// Marshals data from a structure class to a native memory block. If the
/// structure contains pointers to allocated blocks and "fDeleteOld" is
/// true, this routine will call DestroyStructure() first.
/// </summary>
[RequiresDynamicCode("Marshalling code for the object might not be available. Use the StructureToPtr<T> overload instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld)
{
ArgumentNullException.ThrowIfNull(ptr);
ArgumentNullException.ThrowIfNull(structure);
MethodTable* pMT = RuntimeHelpers.GetMethodTable(structure);
if (pMT->HasInstantiation)
throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(structure));
delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub((IntPtr)pMT, &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structure));
if (structMarshalStub != null)
{
if (fDeleteOld)
{
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Marshal, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
else
{
SpanHelpers.Memmove(ref *(byte*)ptr, ref structure.GetRawData(), size);
}
}
/// <summary>
/// Helper function to copy a pointer into a preallocated structure.
/// </summary>
private static unsafe void PtrToStructureHelper(IntPtr ptr, object structure, bool allowValueClasses)
{
MethodTable* pMT = RuntimeHelpers.GetMethodTable(structure);
if (!allowValueClasses && pMT->IsValueType)
throw new ArgumentException(SR.Argument_StructMustNotBeValueClass, nameof(structure));
delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub((IntPtr)pMT, &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structure));
if (structMarshalStub != null)
{
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Unmarshal, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
else
{
SpanHelpers.Memmove(ref structure.GetRawData(), ref *(byte*)ptr, size);
}
}
/// <summary>
/// Frees all substructures pointed to by the native memory block.
/// nameof(structuretype) is used to provide layout information.
/// </summary>
[RequiresDynamicCode("Marshalling code for the object might not be available. Use the DestroyStructure<T> overload instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static unsafe void DestroyStructure(IntPtr ptr, Type structuretype)
{
ArgumentNullException.ThrowIfNull(ptr);
ArgumentNullException.ThrowIfNull(structuretype);
if (structuretype is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(structuretype));
if (rt.IsGenericType)
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(structuretype));
delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub(rt.GetUnderlyingNativeHandle(), &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structuretype));
GC.KeepAlive(rt);
if (structMarshalStub != null)
{
structMarshalStub(ref Unsafe.NullRef<byte>(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement?>());
}
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_TryGetStructMarshalStub")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool TryGetStructMarshalStub(IntPtr th, delegate*<ref byte, byte*, int, ref CleanupWorkListElement?, void>* structMarshalStub, nuint* size);
// Note: Callers are required to keep obj alive
internal static unsafe bool IsPinnable(object? obj)
=> (obj == null) || !RuntimeHelpers.GetMethodTable(obj)->ContainsGCPointers;
#if TARGET_WINDOWS
[FeatureSwitchDefinition("System.Runtime.InteropServices.BuiltInComInterop.IsSupported")]
internal static bool IsBuiltInComSupported { get; } = IsBuiltInComSupportedInternal();
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_IsBuiltInComSupported")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool IsBuiltInComSupportedInternal();
/// <summary>
/// Returns the HInstance for this module. Returns -1 if the module doesn't have
/// an HInstance. In Memory (Dynamic) Modules won't have an HInstance.
/// </summary>
[RequiresAssemblyFiles("Windows only assigns HINSTANCE to assemblies loaded from disk. " +
"This API will return -1 for modules without a file on disk.")]
public static IntPtr GetHINSTANCE(Module m)
{
ArgumentNullException.ThrowIfNull(m);
if (m is RuntimeModule rtModule)
{
return GetHINSTANCE(new QCallModule(ref rtModule));
}
return (IntPtr)(-1);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetHINSTANCE")]
private static partial IntPtr GetHINSTANCE(QCallModule m);
#endif // TARGET_WINDOWS
internal static Exception GetExceptionForHRInternal(int errorCode, IntPtr errorInfo)
{
Exception? exception = null;
GetExceptionForHRInternal(errorCode, errorInfo, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetExceptionForHR")]
private static partial void GetExceptionForHRInternal(int errorCode, IntPtr errorInfo, ObjectHandleOnStack exception);
#if FEATURE_COMINTEROP
/// <summary>
/// Converts the CLR exception to an HRESULT. This function also sets
/// up an IErrorInfo for the exception.
/// </summary>
public static int GetHRForException(Exception? e)
=> GetHRForException(ObjectHandleOnStack.Create(ref e));
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetHRForException")]
private static partial int GetHRForException(ObjectHandleOnStack exception);
/// <summary>
/// Given a managed object that wraps an ITypeInfo, return its name.
/// </summary>
[SupportedOSPlatform("windows")]
public static string GetTypeInfoName(ITypeInfo typeInfo)
{
ArgumentNullException.ThrowIfNull(typeInfo);
typeInfo.GetDocumentation(-1, out string strTypeLibName, out _, out _, out _);
return strTypeLibName;
}
#pragma warning disable IDE0060
// This method is identical to Type.GetTypeFromCLSID. Since it's interop specific, we expose it
// on Marshal for more consistent API surface.
internal static Type? GetTypeFromCLSID(Guid clsid, string? server, bool throwOnError)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
// Note: "throwOnError" is a vacuous parameter. Any errors due to the CLSID not being registered or the server not being found will happen
// on the Activator.CreateInstance() call. GetTypeFromCLSID() merely wraps the data in a Type object without any validation.
Type? type = null;
GetTypeFromCLSID(clsid, server, ObjectHandleOnStack.Create(ref type));
return type;
}
#pragma warning restore IDE0060
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetTypeFromCLSID", StringMarshalling = StringMarshalling.Utf16)]
private static partial void GetTypeFromCLSID(in Guid clsid, string? server, ObjectHandleOnStack retType);
/// <summary>
/// Return the IUnknown* for an Object if the current context is the one
/// where the RCW was first seen. Will return null otherwise.
/// </summary>
[SupportedOSPlatform("windows")]
public static IntPtr /* IUnknown* */ GetIUnknownForObject(object o)
{
ArgumentNullException.ThrowIfNull(o);
return GetIUnknownForObject(ObjectHandleOnStack.Create(ref o));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIUnknownForObject")]
private static partial IntPtr /* IUnknown* */ GetIUnknownForObject(ObjectHandleOnStack o);
/// <summary>
/// Return the IDispatch* for an Object.
/// </summary>
[SupportedOSPlatform("windows")]
public static IntPtr /* IDispatch */ GetIDispatchForObject(object o)
{
ArgumentNullException.ThrowIfNull(o);
return GetIDispatchForObject(ObjectHandleOnStack.Create(ref o));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIDispatchForObject")]
private static partial IntPtr /* IDispatch* */ GetIDispatchForObject(ObjectHandleOnStack o);
/// <summary>
/// Return the IUnknown* representing the interface for the Object.
/// Object o should support Type T
/// </summary>
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static IntPtr /* IUnknown* */ GetComInterfaceForObject(object o, Type T)
=> GetComInterfaceForObject(o, T, CustomQueryInterfaceMode.Allow);
[SupportedOSPlatform("windows")]
public static IntPtr GetComInterfaceForObject<T, TInterface>([DisallowNull] T o)
=> GetComInterfaceForObject(o!, typeof(TInterface), CustomQueryInterfaceMode.Allow);
/// <summary>
/// Return the IUnknown* representing the interface for the Object.
/// Object o should support Type T, it refer the value of mode to
/// invoke customized QueryInterface or not.
/// </summary>
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static IntPtr /* IUnknown* */ GetComInterfaceForObject(object o, Type T, CustomQueryInterfaceMode mode)
{
ArgumentNullException.ThrowIfNull(o);
ArgumentNullException.ThrowIfNull(T);
if (T is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(T));
return GetComInterfaceForObject(ObjectHandleOnStack.Create(ref o), new QCallTypeHandle(ref rt), fEnableCustomizedQueryInterface: mode == CustomQueryInterfaceMode.Allow);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetComInterfaceForObject")]
private static partial IntPtr /* IUnknown* */ GetComInterfaceForObject(ObjectHandleOnStack o, QCallTypeHandle t, [MarshalAs(UnmanagedType.Bool)] bool fEnableCustomizedQueryInterface);
/// <summary>
/// Return the managed object representing the IUnknown*
/// </summary>
[SupportedOSPlatform("windows")]
public static object GetObjectForIUnknown(IntPtr /* IUnknown* */ pUnk)
{
ArgumentNullException.ThrowIfNull(pUnk);
object? retObject = null;
GetObjectForIUnknown(pUnk, ObjectHandleOnStack.Create(ref retObject));
return retObject!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetObjectForIUnknown")]
private static partial void GetObjectForIUnknown(IntPtr /* IUnknown* */ pUnk, ObjectHandleOnStack retObject);
[SupportedOSPlatform("windows")]
public static object GetUniqueObjectForIUnknown(IntPtr unknown)
{
ArgumentNullException.ThrowIfNull(unknown);
object? retObject = null;
GetUniqueObjectForIUnknown(unknown, ObjectHandleOnStack.Create(ref retObject));
return retObject!;
}
/// <summary>
/// Return a unique Object given an IUnknown. This ensures that you receive a fresh
/// object (we will not look in the cache to match up this IUnknown to an already
/// existing object). This is useful in cases where you want to be able to call
/// ReleaseComObject on a RCW and not worry about other active uses ofsaid RCW.
/// </summary>
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetUniqueObjectForIUnknown")]
private static partial void GetUniqueObjectForIUnknown(IntPtr unknown, ObjectHandleOnStack retObject);
/// <summary>
/// Return an Object for IUnknown, using the Type T.
/// Type T should be either a COM imported Type or a sub-type of COM imported Type
/// </summary>
[SupportedOSPlatform("windows")]
public static object GetTypedObjectForIUnknown(IntPtr /* IUnknown* */ pUnk, Type t)
{
ArgumentNullException.ThrowIfNull(pUnk);
ArgumentNullException.ThrowIfNull(t);
if (t is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(t));
object? retObject = null;
GetTypedObjectForIUnknown(pUnk, new QCallTypeHandle(ref rt), ObjectHandleOnStack.Create(ref retObject));
return retObject!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetTypedObjectForIUnknown")]
private static partial void GetTypedObjectForIUnknown(IntPtr /* IUnknown* */ pUnk, QCallTypeHandle t, ObjectHandleOnStack retObject);
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static IntPtr CreateAggregatedObject(IntPtr pOuter, object o)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(pOuter);
ArgumentNullException.ThrowIfNull(o);
return CreateAggregatedObject(pOuter, ObjectHandleOnStack.Create(ref o));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_CreateAggregatedObject")]
private static partial IntPtr CreateAggregatedObject(IntPtr pOuter, ObjectHandleOnStack o);
[SupportedOSPlatform("windows")]
public static IntPtr CreateAggregatedObject<T>(IntPtr pOuter, T o) where T : notnull
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
return CreateAggregatedObject(pOuter, (object)o);
}
public static void CleanupUnusedObjectsInCurrentContext()
=> InternalCleanupUnusedObjectsInCurrentContext();
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_CleanupUnusedObjectsInCurrentContext")]
private static partial void InternalCleanupUnusedObjectsInCurrentContext();
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool AreComObjectsAvailableForCleanup();
/// <summary>
/// Checks if the object is classic COM component.
/// </summary>
public static bool IsComObject(object o)
{
ArgumentNullException.ThrowIfNull(o);
return o is __ComObject;
}
/// <summary>
/// Release the COM component and if the reference hits 0 zombie this object.
/// Further usage of this Object might throw an exception
/// </summary>
[SupportedOSPlatform("windows")]
public static int ReleaseComObject(object o)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
if (o is null)
{
// Match .NET Framework behaviour.
throw new NullReferenceException();
}
if (o is not __ComObject co)
{
throw new ArgumentException(SR.Argument_ObjNotComObject, nameof(o));
}
return ReleaseComObject(ObjectHandleOnStack.Create(ref co));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_ReleaseComObject")]
private static partial int ReleaseComObject(ObjectHandleOnStack o);
/// <summary>
/// Release the COM component and zombie this object.
/// Further usage of this Object might throw an exception
/// </summary>
[SupportedOSPlatform("windows")]
public static int FinalReleaseComObject(object o)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(o);
if (o is not __ComObject co)
{
throw new ArgumentException(SR.Argument_ObjNotComObject, nameof(o));
}
FinalReleaseComObject(ObjectHandleOnStack.Create(ref co));
return 0;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_FinalReleaseComObject")]
private static partial void FinalReleaseComObject(ObjectHandleOnStack o);
[SupportedOSPlatform("windows")]
public static object? GetComObjectData(object obj, object key)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(obj);
ArgumentNullException.ThrowIfNull(key);
if (obj is not __ComObject co)
{
throw new ArgumentException(SR.Argument_ObjNotComObject, nameof(obj));
}
// Retrieve the data from the __ComObject.
return co.GetData(key);
}
/// <summary>
/// Sets data on the COM object. The data can only be set once for a given key
/// and cannot be removed. This function returns true if the data has been added,
/// false if the data could not be added because there already was data for the
/// specified key.
/// </summary>
[SupportedOSPlatform("windows")]
public static bool SetComObjectData(object obj, object key, object? data)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(obj);
ArgumentNullException.ThrowIfNull(key);
if (obj is not __ComObject co)
{
throw new ArgumentException(SR.Argument_ObjNotComObject, nameof(obj));
}
// Retrieve the data from the __ComObject.
return co.SetData(key, data);
}
/// <summary>
/// This method takes the given COM object and wraps it in an object
/// of the specified type. The type must be derived from __ComObject.
/// </summary>
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
[return: NotNullIfNotNull(nameof(o))]
public static object? CreateWrapperOfType(object? o, Type t)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(t);
if (!t.IsCOMObject)
{
throw new ArgumentException(SR.Argument_TypeNotComObject, nameof(t));
}
if (t.IsGenericType)
{
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t));
}
if (o is null)
{
return null;
}
if (!o.GetType().IsCOMObject)
{
throw new ArgumentException(SR.Argument_ObjNotComObject, nameof(o));
}
// Check to see if we have nothing to do.
if (o.GetType() == t)
{
return o;
}
// Check to see if we already have a cached wrapper for this type.
object? Wrapper = GetComObjectData(o, t);
if (Wrapper is null)
{
// Create the wrapper for the specified type.
Wrapper = InternalCreateWrapperOfType(o, t);
// Attempt to cache the wrapper on the object.
if (!SetComObjectData(o, t, Wrapper))
{
// Another thread already cached the wrapper so use that one instead.
Wrapper = GetComObjectData(o, t)!;
}
}
return Wrapper;
}
[SupportedOSPlatform("windows")]
public static TWrapper CreateWrapperOfType<T, TWrapper>(T? o)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
return (TWrapper)CreateWrapperOfType(o, typeof(TWrapper))!;
}
private static object InternalCreateWrapperOfType(object o, Type t)
{
if (t is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(t));
object? retObject = null;
InternalCreateWrapperOfType(ObjectHandleOnStack.Create(ref o), new QCallTypeHandle(ref rt), ObjectHandleOnStack.Create(ref retObject));
return retObject!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_InternalCreateWrapperOfType")]
private static partial void InternalCreateWrapperOfType(ObjectHandleOnStack o, QCallTypeHandle rt, ObjectHandleOnStack retObject);
/// <summary>
/// check if the type is visible from COM.
/// </summary>
public static bool IsTypeVisibleFromCom(Type t)
{
ArgumentNullException.ThrowIfNull(t);
if (t is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(t));
return IsTypeVisibleFromCom(new QCallTypeHandle(ref rt));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_IsTypeVisibleFromCom")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool IsTypeVisibleFromCom(QCallTypeHandle rt);
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static void GetNativeVariantForObject(object? obj, /* VARIANT * */ IntPtr pDstNativeVariant)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(pDstNativeVariant);
GetNativeVariantForObject(ObjectHandleOnStack.Create(ref obj), pDstNativeVariant);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetNativeVariantForObject")]
private static partial void GetNativeVariantForObject(ObjectHandleOnStack obj, /* VARIANT * */ IntPtr pDstNativeVariant);
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static void GetNativeVariantForObject<T>(T? obj, IntPtr pDstNativeVariant)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
GetNativeVariantForObject((object?)obj, pDstNativeVariant);
}
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static object? GetObjectForNativeVariant(/* VARIANT * */ IntPtr pSrcNativeVariant)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(pSrcNativeVariant);
object? retObject = null;
GetObjectForNativeVariant(pSrcNativeVariant, ObjectHandleOnStack.Create(ref retObject));
return retObject;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetObjectForNativeVariant")]
private static partial void GetObjectForNativeVariant(/* VARIANT * */ IntPtr pSrcNativeVariant, ObjectHandleOnStack retObject);
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T? GetObjectForNativeVariant<T>(IntPtr pSrcNativeVariant)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
return (T?)GetObjectForNativeVariant(pSrcNativeVariant);
}
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static object?[] GetObjectsForNativeVariants(/* VARIANT * */ IntPtr aSrcNativeVariant, int cVars)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ArgumentNullException.ThrowIfNull(aSrcNativeVariant);
ArgumentOutOfRangeException.ThrowIfNegative(cVars);
object?[]? retArray = null;
GetObjectsForNativeVariants(aSrcNativeVariant, cVars, ObjectHandleOnStack.Create(ref retArray));
return retArray!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetObjectsForNativeVariants")]
private static partial void GetObjectsForNativeVariants(/* VARIANT * */ IntPtr aSrcNativeVariant, int cVars, ObjectHandleOnStack retArray);
[SupportedOSPlatform("windows")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static T[] GetObjectsForNativeVariants<T>(IntPtr aSrcNativeVariant, int cVars)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
object?[] objects = GetObjectsForNativeVariants(aSrcNativeVariant, cVars);
T[] result = new T[objects.Length];
Array.Copy(objects, result, objects.Length);
return result;
}
/// <summary>
/// <para>Returns the first valid COM slot that GetMethodInfoForSlot will work on
/// This will be 3 for IUnknown based interfaces and 7 for IDispatch based interfaces. </para>
/// </summary>
[SupportedOSPlatform("windows")]
public static int GetStartComSlot(Type t)
{
ArgumentNullException.ThrowIfNull(t);
if (t is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(t));
return GetStartComSlot(new QCallTypeHandle(ref rt));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetStartComSlot")]
private static partial int GetStartComSlot(QCallTypeHandle rt);
/// <summary>
/// <para>Returns the last valid COM slot that GetMethodInfoForSlot will work on. </para>
/// </summary>
[SupportedOSPlatform("windows")]
public static int GetEndComSlot(Type t)
{
ArgumentNullException.ThrowIfNull(t);
if (t is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(t));
return GetEndComSlot(new QCallTypeHandle(ref rt));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetEndComSlot")]
private static partial int GetEndComSlot(QCallTypeHandle rt);
[RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")]
[SupportedOSPlatform("windows")]
public static object BindToMoniker(string monikerName)
{
if (!IsBuiltInComSupported)
{
throw new NotSupportedException(SR.NotSupported_COM);
}
ThrowExceptionForHR(CreateBindCtx(0, out IntPtr bindctx));
try
{
ThrowExceptionForHR(MkParseDisplayName(bindctx, monikerName, out _, out IntPtr pmoniker));
try
{
ThrowExceptionForHR(BindMoniker(pmoniker, 0, in IID_IUnknown, out IntPtr ptr));
try
{
return GetObjectForIUnknown(ptr);
}
finally
{
Release(ptr);
}
}
finally
{
Release(pmoniker);
}
}
finally
{
Release(bindctx);
}
}
[LibraryImport(Interop.Libraries.Ole32)]
private static partial int CreateBindCtx(uint reserved, out IntPtr ppbc);
[LibraryImport(Interop.Libraries.Ole32)]
private static partial int MkParseDisplayName(IntPtr pbc, [MarshalAs(UnmanagedType.LPWStr)] string szUserName, out uint pchEaten, out IntPtr ppmk);
[LibraryImport(Interop.Libraries.Ole32)]
private static partial int BindMoniker(IntPtr pmk, uint grfOpt, in Guid iidResult, out IntPtr ppvResult);
[SupportedOSPlatform("windows")]
public static void ChangeWrapperHandleStrength(object otp, bool fIsWeak)
{
ArgumentNullException.ThrowIfNull(otp);
ChangeWrapperHandleStrength(ObjectHandleOnStack.Create(ref otp), fIsWeak);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_ChangeWrapperHandleStrength")]
private static partial void ChangeWrapperHandleStrength(ObjectHandleOnStack otp, [MarshalAs(UnmanagedType.Bool)] bool fIsWeak);
#endif // FEATURE_COMINTEROP
internal static Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, RuntimeType t)
{
Delegate? retDelegate = null;
GetDelegateForFunctionPointerInternal(ptr, new QCallTypeHandle(ref t), ObjectHandleOnStack.Create(ref retDelegate));
return retDelegate!;
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetDelegateForFunctionPointerInternal")]
private static partial void GetDelegateForFunctionPointerInternal(IntPtr ptr, QCallTypeHandle t, ObjectHandleOnStack retDelegate);
internal static IntPtr GetFunctionPointerForDelegateInternal(Delegate d)
{
return GetFunctionPointerForDelegateInternal(ObjectHandleOnStack.Create(ref d));
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetFunctionPointerForDelegateInternal")]
private static partial IntPtr GetFunctionPointerForDelegateInternal(ObjectHandleOnStack d);
#if DEBUG // Used for testing in Checked or Debug
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIsInCooperativeGCModeFunctionPointer")]
internal static unsafe partial delegate* unmanaged<int> GetIsInCooperativeGCModeFunctionPointer();
#endif
}
}
|