public unsafe static int QueryInterface(IntPtr pUnk, ref Guid iid, out IntPtr ppv) { if (pUnk == IntPtr.Zero) { throw new ArgumentNullException(nameof(pUnk)); } IntPtr pComIUnk; int hr; fixed(Guid *unsafe_iid = &iid) { hr = CalliIntrinsics.StdCall__QueryInterface(((__com_IUnknown *)(void *)pUnk)->pVtable-> pfnQueryInterface, pUnk, new IntPtr(unsafe_iid), new IntPtr(&pComIUnk)); } if (hr != 0) { ppv = default(IntPtr); } else { ppv = pComIUnk; } return(hr); }
private static __ComObject CreateComObjectInternal(RuntimeTypeHandle classType, IntPtr pComItf) { Debug.Assert(!classType.IsNull()); if (classType.Equals(McgModule.s_DependencyReductionTypeRemovedTypeHandle)) { // We should filter out the strongly typed RCW in TryGetClassInfoFromName step #if !RHTESTCL Environment.FailFast(McgTypeHelpers.GetDiagnosticMessageForMissingType(classType)); #else Environment.FailFast("We should never see strongly typed RCW discarded here"); #endif } //Note that this doesn't run the constructor in RH but probably do in your reflection based implementation. //If this were a real RCW, you would actually 'new' the RCW which is wrong. Fortunately in CoreCLR we don't have //this scenario so we are OK, but we should figure out a way to fix this by having a runtime API. object newClass = InteropExtensions.RuntimeNewObject(classType); Debug.Assert(newClass is __ComObject); __ComObject newObj = InteropExtensions.UncheckedCast <__ComObject>(newClass); IntPtr pfnCtor = AddrOfIntrinsics.AddrOf <AddrOfIntrinsics.AddrOfAttachingCtor>(__ComObject.AttachingCtor); CalliIntrinsics.Call <int>(pfnCtor, newObj, pComItf, classType); return(newObj); }
/// <summary> /// This method validates that the given weak reference is alive. /// 1. IWeakReference->Resolve method returns the target's interface for the mapping IID passed to it. /// 2. If the object is not alive it returns null. /// 2. From the returned interface we get or create a new RCW and return it. /// </summary> /// <returns></returns> internal unsafe object Resolve() { IntPtr pInspectable; __com_IWeakReference *pComIWeakReference = (__com_IWeakReference *)m_pComWeakRef; Guid inspectableIID = Interop.COM.IID_IInspectable; int result = CalliIntrinsics.StdCall__int( pComIWeakReference->pVtable->pfnResolve, m_pComWeakRef, &inspectableIID, &pInspectable); if (result >= 0 && pInspectable != IntPtr.Zero) { try { return(McgMarshal.ComInterfaceToObject(pInspectable, McgModuleManager.IInspectable)); } finally { // Release the pInspectable. McgMarshal.ComRelease(pInspectable); } } return(null); }
internal static void GetReference(System.IntPtr pRestrictedErrorInfo, out string errReference) { Debug.Assert(pRestrictedErrorInfo != IntPtr.Zero); IntPtr pReference = IntPtr.Zero; errReference = null; try { __com_IRestrictedErrorInfo *pComRestrictedErrorInfo = (__com_IRestrictedErrorInfo *)pRestrictedErrorInfo; int result = CalliIntrinsics.StdCall <int>(pComRestrictedErrorInfo->pVtable->pfnGetReference, pRestrictedErrorInfo, out pReference); if (result >= 0) { errReference = Interop.COM.ConvertBSTRToString(pReference); } else { errReference = null; } } finally { if (pReference != IntPtr.Zero) { ExternalInterop.SysFreeString(pReference); } } }
/// <summary> /// Returns runtime class name for a specific WinRT interface /// </summary> internal static string GetRuntimeClassName(IntPtr pWinRTItf) { #if ENABLE_MIN_WINRT void *unsafe_hstring = null; try { int hr = CalliIntrinsics.StdCall__int( ((__com_IInspectable *)(void *)pWinRTItf)->pVtable->pfnGetRuntimeClassName, pWinRTItf, &unsafe_hstring); // Don't throw if the call fails if (hr < 0) { return(String.Empty); } return(McgMarshal.HStringToString(new IntPtr(unsafe_hstring))); } finally { if (unsafe_hstring != null) { McgMarshal.FreeHString(new IntPtr(unsafe_hstring)); } } #else throw new PlatformNotSupportedException("GetRuntimeClassName(IntPtr)"); #endif }
/// <summary> /// Returns whether the IUnknown* is a free-threaded COM object /// </summary> /// <param name="pUnknown"></param> internal static unsafe bool IsFreeThreaded(IntPtr pUnknown) { // // Does it support IAgileObject? // IntPtr pAgileObject = McgMarshal.ComQueryInterfaceNoThrow(pUnknown, ref Interop.COM.IID_IAgileObject); if (pAgileObject != default(IntPtr)) { // Anything that implements IAgileObject is considered to be free-threaded // NOTE: This doesn't necessarily mean that the object is free-threaded - it only means // we BELIEVE it is free-threaded McgMarshal.ComRelease_StdCall(pAgileObject); return(true); } IntPtr pMarshal = McgMarshal.ComQueryInterfaceNoThrow(pUnknown, ref Interop.COM.IID_IMarshal); if (pMarshal == default(IntPtr)) { return(false); } try { // // Check the un-marshaler // Interop.COM.__IMarshal *pIMarshalNativePtr = (Interop.COM.__IMarshal *)(void *) pMarshal; fixed(Guid *pGuid = &Interop.COM.IID_IUnknown) { Guid clsid; int hr = CalliIntrinsics.StdCall__int( pIMarshalNativePtr->vtbl->pfnGetUnmarshalClass, new IntPtr(pIMarshalNativePtr), pGuid, default(IntPtr), (uint)Interop.COM.MSHCTX.MSHCTX_INPROC, default(IntPtr), (uint)Interop.COM.MSHLFLAGS.MSHLFLAGS_NORMAL, &clsid); if (hr >= 0 && InteropExtensions.GuidEquals(ref clsid, ref Interop.COM.CLSID_InProcFreeMarshaler)) { // The un-marshaller is indeed the unmarshaler for the FTM so this object // is free threaded. return(true); } } return(false); } finally { McgMarshal.ComRelease_StdCall(pMarshal); } }
public unsafe static int Release(IntPtr pUnk) { if (pUnk == IntPtr.Zero) { throw new ArgumentNullException(nameof(pUnk)); } return(CalliIntrinsics.StdCall__Release(((__com_IUnknown *)(void *)pUnk)->pVtable-> pfnRelease, pUnk)); }
/// <summary> /// Retrieve the corresponding P/invoke instance from the stub /// </summary> public static Delegate GetPInvokeDelegateForStub(IntPtr pStub, RuntimeTypeHandle delegateType) { if (pStub == IntPtr.Zero) { return(null); } // // First try to see if this is one of the thunks we've allocated when we marshal a managed // delegate to native code // s_thunkPoolHeap will be null if there isn't any managed delegate to native // IntPtr pContext; IntPtr pTarget; if (s_thunkPoolHeap != null && RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, pStub, out pContext, out pTarget)) { GCHandle handle; unsafe { // Pull out Handle from context handle = ((ThunkContextData *)pContext)->Handle; } Delegate target = InteropExtensions.UncheckedCast <Delegate>(handle.Target); // // The delegate might already been garbage collected // User should use GC.KeepAlive or whatever ways necessary to keep the delegate alive // until they are done with the native function pointer // if (target == null) { Environment.FailFast(SR.Delegate_GarbageCollected); } return(target); } // // Otherwise, the stub must be a pure native function pointer // We need to create the delegate that points to the invoke method of a // NativeFunctionPointerWrapper derived class // McgPInvokeDelegateData pInvokeDelegateData; if (!RuntimeAugments.InteropCallbacks.TryGetMarshallerDataForDelegate(delegateType, out pInvokeDelegateData)) { return(null); } return(CalliIntrinsics.Call <Delegate>( pInvokeDelegateData.ForwardDelegateCreationStub, pStub )); }
internal static bool GetErrorDetails(System.IntPtr pRestrictedErrorInfo, out string errMsg, out int hr, out string resErrMsg, out string errCapSid) { Debug.Assert(pRestrictedErrorInfo != IntPtr.Zero); IntPtr pErrDes, pResErrDes, pErrCapSid; pErrDes = pResErrDes = pErrCapSid = IntPtr.Zero; int result; try { // Get the errorDetails associated with the restrictedErrorInfo. __com_IRestrictedErrorInfo *pComRestrictedErrorInfo = (__com_IRestrictedErrorInfo *)pRestrictedErrorInfo; result = CalliIntrinsics.StdCall <int>( pComRestrictedErrorInfo->pVtable->pfnGetErrorDetails, pRestrictedErrorInfo, out pErrDes, out hr, out pResErrDes, out pErrCapSid); if (result >= 0) { // RestrictedErrorInfo details can be used since the pRestrictedErrorInfo has the same hr value as the hr returned by the native code. errMsg = Interop.COM.ConvertBSTRToString(pErrDes); resErrMsg = Interop.COM.ConvertBSTRToString(pResErrDes); errCapSid = Interop.COM.ConvertBSTRToString(pErrCapSid); } else { errMsg = resErrMsg = errCapSid = null; hr = 0; } } finally { if (pErrDes != IntPtr.Zero) { ExternalInterop.SysFreeString(pErrDes); } if (pResErrDes != IntPtr.Zero) { ExternalInterop.SysFreeString(pResErrDes); } if (pErrCapSid != IntPtr.Zero) { ExternalInterop.SysFreeString(pErrCapSid); } } return(result >= 0); }
/// <summary> /// Given a IStream*, seek to its beginning /// </summary> internal static unsafe bool SeekStreamToBeginning(IntPtr pStream) { Interop.COM.__IStream *pStreamNativePtr = (Interop.COM.__IStream *)(void *) pStream; UInt64 newPosition; int hr = CalliIntrinsics.StdCall__int( pStreamNativePtr->vtbl->pfnSeek, pStreamNativePtr, 0UL, (uint)Interop.COM.STREAM_SEEK.STREAM_SEEK_SET, &newPosition); return(hr >= 0); }
#pragma warning restore 649, 169 /// <summary> /// This method gets called every time the WeakReference or WeakReference'1 set the Target. /// We can have 4 possible combination here. /// a. Target is a GC object and it is either getting set for the first time or previous object is also GC object. /// In this case we do not need to do anything. /// /// b. Target is a GC object and previous target was __ComObject /// i. We remove the element from ConditionalWeakTable. /// ii. When the GC collects this ComWeakReference the finalizer will ensure that the native object is released. /// /// c. Target is a __ComObject and the previous target was null or GC object. /// We simply add the new target to the dictionary. /// /// d. Target is a __COmObject and the previous object was __COmObject. /// i. We first remove the element from the ConditionalWeakTable. /// ii. When the GC collects the previous ComWeakReference the finalizer will ensure that the native object is released. /// iii. We add the new ComWeakReference to the conditionalWeakTable. /// </summary> /// <param name="weakReference"></param> /// <param name="target"></param> public static unsafe void SetTarget(object weakReference, object target) { Contract.Assert(weakReference != null); // Check if this weakReference is already associated with a native target. ComWeakReference pOldComWeakReference; if (s_COMWeakReferenceTable.TryGetValue(weakReference, out pOldComWeakReference)) { // Remove the previous target. // We do not have to release the native ComWeakReference since it will be done as part of the finalizer. s_COMWeakReferenceTable.Remove(weakReference); } // Now check whether the current target is __ComObject. // However, we don't want to pass the QI to a managed object deriving from native object - we // would end up passing the QI to back the CCW and end up stack overflow __ComObject comObject = target as __ComObject; if (comObject != null && !comObject.ExtendsComObject) { // Get the IWeakReferenceSource for the given object IntPtr pWeakRefSource = McgMarshal.ObjectToComInterface(comObject, McgModuleManager.IWeakReferenceSource); if (pWeakRefSource != IntPtr.Zero) { IntPtr pWeakRef = IntPtr.Zero; try { // Now that we have the IWeakReferenceSource , we need to call the GetWeakReference method to get the corresponding IWeakReference __com_IWeakReferenceSource *pComWeakRefSource = (__com_IWeakReferenceSource *)pWeakRefSource; int result = CalliIntrinsics.StdCall <int>( pComWeakRefSource->pVtable->pfnGetWeakReference, pWeakRefSource, out pWeakRef); if (result >= 0 && pWeakRef != IntPtr.Zero) { // Since we have already checked s_COMWeakReferenceTable for the weak reference, we can simply add the entry w/o checking. // PS - We do not release the pWeakRef as it should be alive until the ComWeakReference is alive. s_COMWeakReferenceTable.Add(weakReference, new ComWeakReference(ref pWeakRef)); } } finally { McgMarshal.ComSafeRelease(pWeakRef); McgMarshal.ComRelease(pWeakRefSource); } } } }
/// <summary> /// Given a IStream*, change its size /// </summary> internal static unsafe bool SetStreamSize(IntPtr pStream, ulong lSize) { Interop.COM.__IStream *pStreamNativePtr = (Interop.COM.__IStream *)(void *) pStream; UInt64 newPosition; int hr = CalliIntrinsics.StdCall__int( pStreamNativePtr->vtbl->pfnSetSize, pStreamNativePtr, lSize, (uint)Interop.COM.STREAM_SEEK.STREAM_SEEK_SET, &newPosition); return(hr >= 0); }
public static unsafe void DestroyStructure(IntPtr ptr, Type structuretype) { if (ptr == IntPtr.Zero) { throw new ArgumentNullException(nameof(ptr)); } if (structuretype == null) { throw new ArgumentNullException(nameof(structuretype)); } RuntimeTypeHandle structureTypeHandle = structuretype.TypeHandle; if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) { throw new ArgumentException(SR.Argument_NeedNonGenericType, "t"); } if (structureTypeHandle.IsEnum() || structureTypeHandle.IsInterface() || InteropExtensions.AreTypesAssignable(typeof(Delegate).TypeHandle, structureTypeHandle)) { throw new ArgumentException(SR.Format(SR.Argument_MustHaveLayoutOrBeBlittable, structureTypeHandle.LastResortToString)); } if (structureTypeHandle.IsBlittable()) { // ok to call with blittable structure, but no work to do in this case. return; } IntPtr destroyStructureStub = RuntimeAugments.InteropCallbacks.GetDestroyStructureStub(structureTypeHandle, out bool hasInvalidLayout); if (hasInvalidLayout) { throw new ArgumentException(SR.Format(SR.Argument_MustHaveLayoutOrBeBlittable, structureTypeHandle.LastResortToString)); } // DestroyStructureStub == IntPtr.Zero means its fields don't need to be destroied if (destroyStructureStub != IntPtr.Zero) { CalliIntrinsics.Call <int>( destroyStructureStub, (void *)ptr // unsafe (no need to adjust as it is always struct) ); } }
internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) { RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; // Boxed struct start at offset 1 (EEType* at offset 0) while class start at offset 0 int offset = structureTypeHandle.IsValueType() ? 1 : 0; IntPtr unmarshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { unmarshalStub = IntPtr.Zero; } } else { unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); } if (unmarshalStub != IntPtr.Zero) { InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { CalliIntrinsics.Call <int>( unmarshalStub, (void *)ptr, // unsafe (no need to adjust as it is always struct) ((void *)((IntPtr *)unboxedStructPtr + offset)) // safe (need to adjust offset as it could be class) ); }); } else { int structSize = Marshal.SizeOf(structure); InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { InteropExtensions.Memcpy( (IntPtr)((IntPtr *)unboxedStructPtr + offset), // safe (need to adjust offset as it could be class) ptr, // unsafe (no need to adjust as it is always struct) structSize ); }); } }
internal static unsafe void PtrToStructureImpl(IntPtr ptr, object structure) { RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; IntPtr unmarshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructUnmarshalStub(structureTypeHandle, out unmarshalStub)) { unmarshalStub = IntPtr.Zero; } } else { unmarshalStub = RuntimeAugments.InteropCallbacks.GetStructUnmarshalStub(structureTypeHandle); } if (unmarshalStub != IntPtr.Zero) { if (structureTypeHandle.IsValueType()) { CalliIntrinsics.Call( unmarshalStub, ref *(byte *)ptr, ref structure.GetRawData()); } else { CalliIntrinsics.Call( unmarshalStub, ref *(byte *)ptr, structure); } } else { nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); fixed(byte *pDest = &structure.GetRawData()) { Buffer.Memmove(pDest, (byte *)ptr, size); } } }
public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld) { if (structure == null) { throw new ArgumentNullException(nameof(structure)); } if (ptr == IntPtr.Zero) { throw new ArgumentNullException(nameof(ptr)); } if (fDeleteOld) { DestroyStructure(ptr, structure.GetType()); } RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) { throw new ArgumentException(nameof(structure), SR.Argument_NeedNonGenericObject); } IntPtr marshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) { marshalStub = IntPtr.Zero; } } else { marshalStub = RuntimeAugments.InteropCallbacks.GetStructMarshalStub(structureTypeHandle); } if (marshalStub != IntPtr.Zero) { if (structureTypeHandle.IsValueType()) { CalliIntrinsics.Call(marshalStub, ref structure.GetRawData(), ref *(byte *)ptr); } else { CalliIntrinsics.Call(marshalStub, structure, ref *(byte *)ptr); } } else { nuint size = (nuint)RuntimeAugments.InteropCallbacks.GetStructUnsafeStructSize(structureTypeHandle); fixed(byte *pSrc = &structure.GetRawData()) { Buffer.Memmove((byte *)ptr, pSrc, size); } } }
/// <summary> /// This method returns a new Exception object given the HR value. /// /// 1. We check whether we have our own LanguageException associated with this hr. If so we simply use it since it helps preserve the stacktrace, message and type. /// This is done using GetLanguageException API on ILanguageExceptionErrorInfo from IRestrictedErrorInfo. Since ILanguageExceptionErrorInfo is available only on Windows Blue /// we can only do this WindowsBlue and above. In desktop CLR we could use GetErroInfo and check whether we have our IErroInfo and retrieve our own exception. /// For Win8 in .NET Native we simply create the exception using the RestrictedErrorInfo and hence only able to give the exception with restrictedErrorMsg. /// 2. In case we do not have the languageException we simply check RestrictedErrorInfo for errorMsg and create an exception using /// <errorMsg>\r\n<restrictedErrorMsg>. This is done for only windows blue. To be backward compatible we only use errorMsg for creating exception in win8. /// 3. PS - This class puts all the logic in try, catch block to ensure that none of the exception helpers /// throw exception themselves. /// </summary> /// <param name="hr"></param> /// <param name="isWinRTScenario"></param> internal static Exception GetExceptionForHRInternalNoThrow(int hr, bool isWinRTScenario, bool isClassicCOM) { Exception ex; IntPtr pRestrictedErrorInfo = IntPtr.Zero; try { if (TryGetRestrictedErrorInfo(out pRestrictedErrorInfo)) { // This is to check whether we need to give post win8 behavior or not. if (isWinRTScenario) { // Check whether the given IRestrictedErrorInfo object supports ILanguageExceptionErrorInfo IntPtr pLanguageExceptionErrorInfo = McgMarshal.ComQueryInterfaceNoThrow(pRestrictedErrorInfo, ref Interop.COM.IID_ILanguageExceptionErrorInfo); if (pLanguageExceptionErrorInfo != IntPtr.Zero) { // We have an LanguageExceptionErrorInfo. IntPtr pUnk; __com_ILanguageExceptionErrorInfo *pComLanguageExceptionErrorInfo = (__com_ILanguageExceptionErrorInfo *)pLanguageExceptionErrorInfo; int result = CalliIntrinsics.StdCall <int>(pComLanguageExceptionErrorInfo->pVtable->pfnGetLanguageException, pLanguageExceptionErrorInfo, out pUnk); McgMarshal.ComSafeRelease(pLanguageExceptionErrorInfo); if (result >= 0 && pUnk != IntPtr.Zero) { try { // Check whether the given pUnk is a managed exception. ComCallableObject ccw; if (ComCallableObject.TryGetCCW(pUnk, out ccw)) { return(ccw.TargetObject as Exception); } } finally { McgMarshal.ComSafeRelease(pUnk); } } } } String message = null, errorInfoReference = null; string errMsg, errCapSid, resErrMsg; int errHr; object restrictedErrorInfo = null; bool hasErrorInfo = false; if (RestrictedErrorInfoHelper.GetErrorDetails(pRestrictedErrorInfo, out errMsg, out errHr, out resErrMsg, out errCapSid) && errHr == hr) { // RestrictedErrorInfo details can be used since the pRestrictedErrorInfo has the same hr value as the hr returned by the native code. // We are in windows blue or above and hence the exceptionMsg is errMsg + "\r\n" + resErrMsg message = String.IsNullOrEmpty(resErrMsg) ? errMsg : errMsg + "\r\n" + resErrMsg; RestrictedErrorInfoHelper.GetReference(pRestrictedErrorInfo, out errorInfoReference); restrictedErrorInfo = McgMarshal.ComInterfaceToObject(pRestrictedErrorInfo, InternalTypes.IRestrictedErrorInfo); hasErrorInfo = true; } if (hr == Interop.COM.RO_E_CLOSED && isWinRTScenario) { hr = Interop.COM.COR_E_OBJECTDISPOSED; } // Now we simply need to set the description and the resDescription by adding an internal method. ex = GetMappingExceptionForHR(hr, message, isClassicCOM, hasErrorInfo); if (restrictedErrorInfo != null) { InteropExtensions.AddExceptionDataForRestrictedErrorInfo(ex, resErrMsg, errorInfoReference, errCapSid, restrictedErrorInfo); } return(ex); } } catch (Exception) { // We can't do any thing here and hence we swallow the exception and get the corresponding hr. } finally { McgMarshal.ComSafeRelease(pRestrictedErrorInfo); } // We could not find any restrictedErrorInfo associated with this object and hence we simply use the hr to create the exception. return(GetMappingExceptionForHR(hr, null, isClassicCOM, hasErrorInfo: false)); }
public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld) { if (structure == null) { throw new ArgumentNullException(nameof(structure)); } if (ptr == IntPtr.Zero) { throw new ArgumentNullException(nameof(ptr)); } if (fDeleteOld) { DestroyStructure(ptr, structure.GetType()); } RuntimeTypeHandle structureTypeHandle = structure.GetType().TypeHandle; if (structureTypeHandle.IsGenericType() || structureTypeHandle.IsGenericTypeDefinition()) { throw new ArgumentException(nameof(structure), SR.Argument_NeedNonGenericObject); } // Boxed struct start at offset 1 (EEType* at offset 0) while class start at offset 0 int offset = structureTypeHandle.IsValueType() ? 1 : 0; IntPtr marshalStub; if (structureTypeHandle.IsBlittable()) { if (!RuntimeAugments.InteropCallbacks.TryGetStructMarshalStub(structureTypeHandle, out marshalStub)) { marshalStub = IntPtr.Zero; } } else { marshalStub = RuntimeAugments.InteropCallbacks.GetStructMarshalStub(structureTypeHandle); } if (marshalStub != IntPtr.Zero) { InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { CalliIntrinsics.Call <int>( marshalStub, ((void *)((IntPtr *)unboxedStructPtr + offset)), // safe (need to adjust offset as it could be class) (void *)ptr // unsafe (no need to adjust as it is always struct) ); }); } else { int structSize = Marshal.SizeOf(structure); InteropExtensions.PinObjectAndCall(structure, unboxedStructPtr => { InteropExtensions.Memcpy( ptr, // unsafe (no need to adjust as it is always struct) (IntPtr)((IntPtr *)unboxedStructPtr + offset), // safe (need to adjust offset as it could be class) structSize ); }); } }