1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Microsoft Public License, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 #if !SILVERLIGHT // ComObject
20 using System.Diagnostics;
21 using System.Diagnostics.CodeAnalysis;
22 using System.Reflection;
23 using System.Reflection.Emit;
24 using System.Runtime.InteropServices;
25 using System.Security;
26 using System.Security.Permissions;
27 using ComTypes = System.Runtime.InteropServices.ComTypes;
30 namespace System.Dynamic {
32 namespace Microsoft.Scripting {
35 internal static class ComRuntimeHelpers {
38 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
41 [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
42 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")]
43 public static void CheckThrowException(int hresult, ref ExcepInfo excepInfo, uint argErr, string message) {
44 if (ComHresults.IsSuccess(hresult)) {
49 case ComHresults.DISP_E_BADPARAMCOUNT:
50 // The number of elements provided to DISPPARAMS is different from the number of arguments
51 // accepted by the method or property.
52 throw Error.DispBadParamCount(message);
54 case ComHresults.DISP_E_BADVARTYPE:
55 //One of the arguments in rgvarg is not a valid variant type.
58 case ComHresults.DISP_E_EXCEPTION:
59 // The application needs to raise an exception. In this case, the structure passed in pExcepInfo
60 // should be filled in.
61 throw excepInfo.GetException();
63 case ComHresults.DISP_E_MEMBERNOTFOUND:
64 // The requested member does not exist, or the call to Invoke tried to set the value of a
65 // read-only property.
66 throw Error.DispMemberNotFound(message);
68 case ComHresults.DISP_E_NONAMEDARGS:
69 // This implementation of IDispatch does not support named arguments.
70 throw Error.DispNoNamedArgs(message);
72 case ComHresults.DISP_E_OVERFLOW:
73 // One of the arguments in rgvarg could not be coerced to the specified type.
74 throw Error.DispOverflow(message);
76 case ComHresults.DISP_E_PARAMNOTFOUND:
77 // One of the parameter DISPIDs does not correspond to a parameter on the method. In this case,
78 // puArgErr should be set to the first argument that contains the error.
81 case ComHresults.DISP_E_TYPEMISMATCH:
82 // One or more of the arguments could not be coerced. The index within rgvarg of the first
83 // parameter with the incorrect type is returned in the puArgErr parameter.
84 throw Error.DispTypeMismatch(argErr, message);
86 case ComHresults.DISP_E_UNKNOWNINTERFACE:
87 // The interface identifier passed in riid is not IID_NULL.
90 case ComHresults.DISP_E_UNKNOWNLCID:
91 // The member being invoked interprets string arguments according to the LCID, and the
92 // LCID is not recognized.
95 case ComHresults.DISP_E_PARAMNOTOPTIONAL:
96 // A required parameter was omitted.
97 throw Error.DispParamNotOptional(message);
100 Marshal.ThrowExceptionForHR(hresult);
103 internal static void GetInfoFromType(ComTypes.ITypeInfo typeInfo, out string name, out string documentation) {
107 typeInfo.GetDocumentation(-1, out name, out documentation, out dwHelpContext, out strHelpFile);
110 internal static string GetNameOfMethod(ComTypes.ITypeInfo typeInfo, int memid) {
112 string[] rgNames = new string[1];
113 typeInfo.GetNames(memid, rgNames, 1, out cNames);
117 internal static string GetNameOfLib(ComTypes.ITypeLib typeLib) {
123 typeLib.GetDocumentation(-1, out name, out strDocString, out dwHelpContext, out strHelpFile);
127 internal static string GetNameOfType(ComTypes.ITypeInfo typeInfo) {
129 string documentation;
130 GetInfoFromType(typeInfo, out name, out documentation);
136 /// Look for typeinfo using IDispatch.GetTypeInfo
138 /// <param name="dispatch"></param>
139 /// <param name="throwIfMissingExpectedTypeInfo">
140 /// Some COM objects just dont expose typeinfo. In these cases, this method will return null.
141 /// Some COM objects do intend to expose typeinfo, but may not be able to do so if the type-library is not properly
142 /// registered. This will be considered as acceptable or as an error condition depending on throwIfMissingExpectedTypeInfo</param>
143 /// <returns></returns>
145 internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch, bool throwIfMissingExpectedTypeInfo) {
147 int hresult = dispatch.TryGetTypeInfoCount(out typeCount);
148 Marshal.ThrowExceptionForHR(hresult);
149 Debug.Assert(typeCount <= 1);
150 if (typeCount == 0) {
154 IntPtr typeInfoPtr = IntPtr.Zero;
156 hresult = dispatch.TryGetTypeInfo(0, 0, out typeInfoPtr);
157 if (!ComHresults.IsSuccess(hresult)) {
158 CheckIfMissingTypeInfoIsExpected(hresult, throwIfMissingExpectedTypeInfo);
161 if (typeInfoPtr == IntPtr.Zero) { // be defensive against components that return IntPtr.Zero
162 if (throwIfMissingExpectedTypeInfo) {
163 Marshal.ThrowExceptionForHR(ComHresults.E_FAIL);
168 ComTypes.ITypeInfo typeInfo = null;
170 typeInfo = Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo;
172 Marshal.Release(typeInfoPtr);
179 /// This method should be called when typeinfo is not available for an object. The function
180 /// will check if the typeinfo is expected to be missing. This can include error cases where
181 /// the same error is guaranteed to happen all the time, on all machines, under all circumstances.
182 /// In such cases, we just have to operate without the typeinfo.
184 /// However, if accessing the typeinfo is failing in a transient way, we might want to throw
185 /// an exception so that we will eagerly predictably indicate the problem.
188 private static void CheckIfMissingTypeInfoIsExpected(int hresult, bool throwIfMissingExpectedTypeInfo) {
189 Debug.Assert(!ComHresults.IsSuccess(hresult));
191 // Word.Basic always returns this because of an incorrect implementation of IDispatch.GetTypeInfo
192 // Any implementation that returns E_NOINTERFACE is likely to do so in all environments
193 if (hresult == ComHresults.E_NOINTERFACE) {
197 // This assert is potentially over-restrictive since COM components can behave in quite unexpected ways.
198 // However, asserting the common expected cases ensures that we find out about the unexpected scenarios, and
199 // can investigate the scenarios to ensure that there is no bug in our own code.
200 Debug.Assert(hresult == ComHresults.TYPE_E_LIBNOTREGISTERED);
202 if (throwIfMissingExpectedTypeInfo) {
203 Marshal.ThrowExceptionForHR(hresult);
207 [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
209 internal static ComTypes.TYPEATTR GetTypeAttrForTypeInfo(ComTypes.ITypeInfo typeInfo) {
210 IntPtr pAttrs = IntPtr.Zero;
211 typeInfo.GetTypeAttr(out pAttrs);
213 // GetTypeAttr should never return null, this is just to be safe
214 if (pAttrs == IntPtr.Zero) {
215 throw Error.CannotRetrieveTypeInformation();
219 return (ComTypes.TYPEATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPEATTR));
221 typeInfo.ReleaseTypeAttr(pAttrs);
225 [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
227 internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typeLib) {
228 IntPtr pAttrs = IntPtr.Zero;
229 typeLib.GetLibAttr(out pAttrs);
231 // GetTypeAttr should never return null, this is just to be safe
232 if (pAttrs == IntPtr.Zero) {
233 throw Error.CannotRetrieveTypeInformation();
237 return (ComTypes.TYPELIBATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPELIBATTR));
239 typeLib.ReleaseTLibAttr(pAttrs);
243 public static BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid) {
244 return new BoundDispEvent(rcw, sourceIid, dispid);
247 public static DispCallable CreateDispCallable(IDispatchComObject dispatch, ComMethodDesc method) {
248 return new DispCallable(dispatch, method.Name, method.DispId);
253 /// This class contains methods that either cannot be expressed in C#, or which require writing unsafe code.
254 /// Callers of these methods need to use them extremely carefully as incorrect use could cause GC-holes
255 /// and other problems.
258 internal static class UnsafeMethods {
259 #region public members
261 #region Generated ConvertByrefToPtr
263 // *** BEGIN GENERATED CODE ***
264 // generated by function: gen_ConvertByrefToPtr from: generate_comdispatch.py
266 #if MICROSOFT_DYNAMIC
267 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
270 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
271 internal static unsafe IntPtr ConvertSByteByrefToPtr(ref SByte value) {
272 fixed (SByte *x = &value) {
273 AssertByrefPointsToStack(new IntPtr(x));
274 return new IntPtr(x);
278 #if MICROSOFT_DYNAMIC
279 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
282 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
283 internal static unsafe IntPtr ConvertInt16ByrefToPtr(ref Int16 value) {
284 fixed (Int16 *x = &value) {
285 AssertByrefPointsToStack(new IntPtr(x));
286 return new IntPtr(x);
290 #if MICROSOFT_DYNAMIC
291 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
294 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
295 public static unsafe IntPtr ConvertInt32ByrefToPtr(ref Int32 value) {
296 fixed (Int32 *x = &value) {
297 AssertByrefPointsToStack(new IntPtr(x));
298 return new IntPtr(x);
302 #if MICROSOFT_DYNAMIC
303 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
306 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
307 internal static unsafe IntPtr ConvertInt64ByrefToPtr(ref Int64 value) {
308 fixed (Int64 *x = &value) {
309 AssertByrefPointsToStack(new IntPtr(x));
310 return new IntPtr(x);
314 #if MICROSOFT_DYNAMIC
315 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
318 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
319 internal static unsafe IntPtr ConvertByteByrefToPtr(ref Byte value) {
320 fixed (Byte *x = &value) {
321 AssertByrefPointsToStack(new IntPtr(x));
322 return new IntPtr(x);
326 #if MICROSOFT_DYNAMIC
327 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
330 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
331 internal static unsafe IntPtr ConvertUInt16ByrefToPtr(ref UInt16 value) {
332 fixed (UInt16 *x = &value) {
333 AssertByrefPointsToStack(new IntPtr(x));
334 return new IntPtr(x);
338 #if MICROSOFT_DYNAMIC
339 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
342 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
343 internal static unsafe IntPtr ConvertUInt32ByrefToPtr(ref UInt32 value) {
344 fixed (UInt32 *x = &value) {
345 AssertByrefPointsToStack(new IntPtr(x));
346 return new IntPtr(x);
350 #if MICROSOFT_DYNAMIC
351 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
354 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
355 internal static unsafe IntPtr ConvertUInt64ByrefToPtr(ref UInt64 value) {
356 fixed (UInt64 *x = &value) {
357 AssertByrefPointsToStack(new IntPtr(x));
358 return new IntPtr(x);
362 #if MICROSOFT_DYNAMIC
363 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
366 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
367 internal static unsafe IntPtr ConvertIntPtrByrefToPtr(ref IntPtr value) {
368 fixed (IntPtr *x = &value) {
369 AssertByrefPointsToStack(new IntPtr(x));
370 return new IntPtr(x);
374 #if MICROSOFT_DYNAMIC
375 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
378 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
379 internal static unsafe IntPtr ConvertUIntPtrByrefToPtr(ref UIntPtr value) {
380 fixed (UIntPtr *x = &value) {
381 AssertByrefPointsToStack(new IntPtr(x));
382 return new IntPtr(x);
386 #if MICROSOFT_DYNAMIC
387 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
390 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
391 internal static unsafe IntPtr ConvertSingleByrefToPtr(ref Single value) {
392 fixed (Single *x = &value) {
393 AssertByrefPointsToStack(new IntPtr(x));
394 return new IntPtr(x);
398 #if MICROSOFT_DYNAMIC
399 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
402 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
403 internal static unsafe IntPtr ConvertDoubleByrefToPtr(ref Double value) {
404 fixed (Double *x = &value) {
405 AssertByrefPointsToStack(new IntPtr(x));
406 return new IntPtr(x);
410 #if MICROSOFT_DYNAMIC
411 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
414 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
415 internal static unsafe IntPtr ConvertDecimalByrefToPtr(ref Decimal value) {
416 fixed (Decimal *x = &value) {
417 AssertByrefPointsToStack(new IntPtr(x));
418 return new IntPtr(x);
423 // *** END GENERATED CODE ***
427 #if MICROSOFT_DYNAMIC
428 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
431 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
432 public static unsafe IntPtr ConvertVariantByrefToPtr(ref Variant value) {
433 fixed (Variant* x = &value) {
434 AssertByrefPointsToStack(new IntPtr(x));
435 return new IntPtr(x);
439 #if MICROSOFT_DYNAMIC
440 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
443 internal static Variant GetVariantForObject(object obj) {
444 Variant variant = default(Variant);
448 InitVariantForObject(obj, ref variant);
452 #if MICROSOFT_DYNAMIC
453 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
456 internal static void InitVariantForObject(object obj, ref Variant variant) {
457 Debug.Assert(obj != null);
459 // GetNativeVariantForObject is very expensive for values that marshal as VT_DISPATCH
460 // also is is extremely common scenario when object at hand is an RCW.
461 // Therefore we are going to test for IDispatch before defaulting to GetNativeVariantForObject.
462 IDispatch disp = obj as IDispatch;
464 variant.AsDispatch = obj;
468 System.Runtime.InteropServices.Marshal.GetNativeVariantForObject(obj, UnsafeMethods.ConvertVariantByrefToPtr(ref variant));
471 #if MICROSOFT_DYNAMIC
472 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
475 [Obsolete("do not use this method", true)]
476 public static object GetObjectForVariant(Variant variant) {
477 IntPtr ptr = UnsafeMethods.ConvertVariantByrefToPtr(ref variant);
478 return System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant(ptr);
481 [Obsolete("do not use this method", true)]
482 public static int IUnknownRelease(IntPtr interfacePointer) {
483 return _IUnknownRelease(interfacePointer);
486 [Obsolete("do not use this method", true)]
487 public static void IUnknownReleaseNotZero(IntPtr interfacePointer) {
488 if (interfacePointer != IntPtr.Zero) {
489 IUnknownRelease(interfacePointer);
493 #if MICROSOFT_DYNAMIC
494 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
497 [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
498 [Obsolete("do not use this method", true)]
499 public static int IDispatchInvoke(
500 IntPtr dispatchPointer,
502 ComTypes.INVOKEKIND flags,
503 ref ComTypes.DISPPARAMS dispParams,
505 out ExcepInfo excepInfo,
509 int hresult = _IDispatchInvoke(
519 if (hresult == ComHresults.DISP_E_MEMBERNOTFOUND
520 && (flags & ComTypes.INVOKEKIND.INVOKE_FUNC) != 0
521 && (flags & (ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT | ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF)) == 0) {
523 // Re-invoke with no result argument to accomodate Word
524 hresult = _IDispatchInvokeNoResult(
527 ComTypes.INVOKEKIND.INVOKE_FUNC,
536 [Obsolete("do not use this method", true)]
537 #if MICROSOFT_DYNAMIC
538 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
541 public static IntPtr GetIdsOfNamedParameters(IDispatch dispatch, string[] names, int methodDispId, out GCHandle pinningHandle) {
542 pinningHandle = GCHandle.Alloc(null, GCHandleType.Pinned);
543 int[] dispIds = new int[names.Length];
544 Guid empty = Guid.Empty;
545 int hresult = dispatch.TryGetIDsOfNames(ref empty, names, (uint)names.Length, 0, dispIds);
547 Marshal.ThrowExceptionForHR(hresult);
550 if (methodDispId != dispIds[0]) {
551 throw Error.GetIDsOfNamesInvalid(names[0]);
554 int[] keywordArgDispIds = dispIds.RemoveFirst(); // Remove the dispId of the method name
556 pinningHandle.Target = keywordArgDispIds;
557 return Marshal.UnsafeAddrOfPinnedArrayElement(keywordArgDispIds, 0);
562 #region non-public members
564 [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
566 static UnsafeMethods() {
569 private static void EmitLoadArg(ILGenerator il, int index) {
570 ContractUtils.Requires(index >= 0, "index");
574 il.Emit(OpCodes.Ldarg_0);
577 il.Emit(OpCodes.Ldarg_1);
580 il.Emit(OpCodes.Ldarg_2);
583 il.Emit(OpCodes.Ldarg_3);
586 if (index <= Byte.MaxValue) {
587 il.Emit(OpCodes.Ldarg_S, (byte)index);
589 il.Emit(OpCodes.Ldarg, index);
596 /// Ensure that "value" is a local variable in some caller's frame. So converting
597 /// the byref to an IntPtr is a safe operation. Alternatively, we could also allow
598 /// allowed "value" to be a pinned object.
600 [Conditional("DEBUG")]
602 private static void AssertByrefPointsToStack(IntPtr ptr) {
603 if (Marshal.ReadInt32(ptr) == _dummyMarker) {
607 int dummy = _dummyMarker;
608 IntPtr ptrToLocal = ConvertInt32ByrefToPtr(ref dummy);
609 Debug.Assert(ptrToLocal.ToInt64() < ptr.ToInt64());
610 Debug.Assert((ptr.ToInt64() - ptrToLocal.ToInt64()) < (16 * 1024));
613 private static readonly object _lock = new object();
614 private static ModuleBuilder _dynamicModule;
616 internal static ModuleBuilder DynamicModule {
618 if (_dynamicModule != null) {
619 return _dynamicModule;
622 if (_dynamicModule == null) {
623 var attributes = new[] {
624 new CustomAttributeBuilder(typeof(UnverifiableCodeAttribute).GetConstructor(Type.EmptyTypes), new object[0]),
625 //PermissionSet(SecurityAction.Demand, Unrestricted = true)
626 new CustomAttributeBuilder(typeof(PermissionSetAttribute).GetConstructor(new Type[]{typeof(SecurityAction)}),
627 new object[]{SecurityAction.Demand},
628 new PropertyInfo[]{typeof(PermissionSetAttribute).GetProperty("Unrestricted")},
632 string name = typeof(VariantArray).Namespace + ".DynamicAssembly";
633 var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.Run, attributes);
634 assembly.DefineVersionInfoResource();
635 _dynamicModule = assembly.DefineDynamicModule(name);
637 return _dynamicModule;
642 private const int _dummyMarker = 0x10101010;
645 /// We will emit an indirect call to an unmanaged function pointer from the vtable of the given interface pointer.
646 /// This approach can take only ~300 instructions on x86 compared with ~900 for Marshal.Release. We are relying on
647 /// the JIT-compiler to do pinvoke-stub-inlining and calling the pinvoke target directly.
649 private delegate int IUnknownReleaseDelegate(IntPtr interfacePointer);
650 private static readonly IUnknownReleaseDelegate _IUnknownRelease = Create_IUnknownRelease();
652 private static IUnknownReleaseDelegate Create_IUnknownRelease() {
653 DynamicMethod dm = new DynamicMethod("IUnknownRelease", typeof(int), new Type[] { typeof(IntPtr) }, DynamicModule);
655 ILGenerator method = dm.GetILGenerator();
657 // return functionPtr(...)
659 method.Emit(OpCodes.Ldarg_0);
661 // functionPtr = *(IntPtr*)(*(interfacePointer) + VTABLE_OFFSET)
662 int iunknownReleaseOffset = ((int)IDispatchMethodIndices.IUnknown_Release) * Marshal.SizeOf(typeof(IntPtr));
663 method.Emit(OpCodes.Ldarg_0);
664 method.Emit(OpCodes.Ldind_I);
665 method.Emit(OpCodes.Ldc_I4, iunknownReleaseOffset);
666 method.Emit(OpCodes.Add);
667 method.Emit(OpCodes.Ldind_I);
669 SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConvention.Winapi, typeof(int));
670 signature.AddArgument(typeof(IntPtr));
671 method.Emit(OpCodes.Calli, signature);
673 method.Emit(OpCodes.Ret);
675 return (IUnknownReleaseDelegate)dm.CreateDelegate(typeof(IUnknownReleaseDelegate));
678 internal static readonly IntPtr NullInterfaceId = GetNullInterfaceId();
681 private static IntPtr GetNullInterfaceId() {
682 int size = Marshal.SizeOf(Guid.Empty);
683 IntPtr ptr = Marshal.AllocHGlobal(size);
684 for (int i = 0; i < size; i++) {
685 Marshal.WriteByte(ptr, i, 0);
691 /// We will emit an indirect call to an unmanaged function pointer from the vtable of the given IDispatch interface pointer.
692 /// It is not possible to express this in C#. Using an indirect pinvoke call allows us to do our own marshalling.
693 /// We can allocate the Variant arguments cheaply on the stack. We are relying on the JIT-compiler to do
694 /// pinvoke-stub-inlining and calling the pinvoke target directly.
695 /// The alternative of calling via a managed interface declaration of IDispatch would have a performance
696 /// penalty of going through a CLR stub that would have to re-push the arguments on the stack, etc.
697 /// Marshal.GetDelegateForFunctionPointer could be used here, but its too expensive (~2000 instructions on x86).
699 private delegate int IDispatchInvokeDelegate(
700 IntPtr dispatchPointer,
702 ComTypes.INVOKEKIND flags,
703 ref ComTypes.DISPPARAMS dispParams,
705 out ExcepInfo excepInfo,
709 private static readonly IDispatchInvokeDelegate _IDispatchInvoke = Create_IDispatchInvoke(true);
710 private static IDispatchInvokeDelegate _IDispatchInvokeNoResultImpl;
712 private static IDispatchInvokeDelegate _IDispatchInvokeNoResult {
714 if (_IDispatchInvokeNoResultImpl == null) {
715 lock (_IDispatchInvoke) {
716 if (_IDispatchInvokeNoResultImpl == null) {
717 _IDispatchInvokeNoResultImpl = Create_IDispatchInvoke(false);
721 return _IDispatchInvokeNoResultImpl;
725 private static IDispatchInvokeDelegate Create_IDispatchInvoke(bool returnResult) {
726 const int dispatchPointerIndex = 0;
727 const int memberDispIdIndex = 1;
728 const int flagsIndex = 2;
729 const int dispParamsIndex = 3;
730 const int resultIndex = 4;
731 const int exceptInfoIndex = 5;
732 const int argErrIndex = 6;
733 Debug.Assert(argErrIndex + 1 == typeof(IDispatchInvokeDelegate).GetMethod("Invoke").GetParameters().Length);
735 Type[] paramTypes = new Type[argErrIndex + 1];
736 paramTypes[dispatchPointerIndex] = typeof(IntPtr);
737 paramTypes[memberDispIdIndex] = typeof(int);
738 paramTypes[flagsIndex] = typeof(ComTypes.INVOKEKIND);
739 paramTypes[dispParamsIndex] = typeof(ComTypes.DISPPARAMS).MakeByRefType();
740 paramTypes[resultIndex] = typeof(Variant).MakeByRefType();
741 paramTypes[exceptInfoIndex] = typeof(ExcepInfo).MakeByRefType();
742 paramTypes[argErrIndex] = typeof(uint).MakeByRefType();
744 // Define the dynamic method in our assembly so we skip verification
745 DynamicMethod dm = new DynamicMethod("IDispatchInvoke", typeof(int), paramTypes, DynamicModule);
746 ILGenerator method = dm.GetILGenerator();
748 // return functionPtr(...)
750 EmitLoadArg(method, dispatchPointerIndex);
751 EmitLoadArg(method, memberDispIdIndex);
753 // burn the address of our empty IID in directly. This is never freed, relocated, etc...
754 // Note passing this as a Guid directly results in a ~30% perf hit for IDispatch invokes so
755 // we also pass it directly as an IntPtr instead.
756 if (IntPtr.Size == 4) {
757 method.Emit(OpCodes.Ldc_I4, UnsafeMethods.NullInterfaceId.ToInt32()); // riid
759 method.Emit(OpCodes.Ldc_I8, UnsafeMethods.NullInterfaceId.ToInt64()); // riid
761 method.Emit(OpCodes.Conv_I);
763 method.Emit(OpCodes.Ldc_I4_0); // lcid
764 EmitLoadArg(method, flagsIndex);
766 EmitLoadArg(method, dispParamsIndex);
769 EmitLoadArg(method, resultIndex);
771 method.Emit(OpCodes.Ldsfld, typeof(IntPtr).GetField("Zero"));
773 EmitLoadArg(method, exceptInfoIndex);
774 EmitLoadArg(method, argErrIndex);
776 // functionPtr = *(IntPtr*)(*(dispatchPointer) + VTABLE_OFFSET)
777 int idispatchInvokeOffset = ((int)IDispatchMethodIndices.IDispatch_Invoke) * Marshal.SizeOf(typeof(IntPtr));
778 EmitLoadArg(method, dispatchPointerIndex);
779 method.Emit(OpCodes.Ldind_I);
780 method.Emit(OpCodes.Ldc_I4, idispatchInvokeOffset);
781 method.Emit(OpCodes.Add);
782 method.Emit(OpCodes.Ldind_I);
784 SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConvention.Winapi, typeof(int));
785 Type[] invokeParamTypes = new Type[] {
786 typeof(IntPtr), // dispatchPointer
787 typeof(int), // memberDispId
788 typeof(IntPtr), // riid
790 typeof(ushort), // flags
791 typeof(IntPtr), // dispParams
792 typeof(IntPtr), // result
793 typeof(IntPtr), // excepInfo
794 typeof(IntPtr), // argErr
796 signature.AddArguments(invokeParamTypes, null, null);
797 method.Emit(OpCodes.Calli, signature);
799 method.Emit(OpCodes.Ret);
800 return (IDispatchInvokeDelegate)dm.CreateDelegate(typeof(IDispatchInvokeDelegate));
806 internal static class NativeMethods {
807 [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.None)]
808 [System.Runtime.Versioning.ResourceConsumption(System.Runtime.Versioning.ResourceScope.Process, System.Runtime.Versioning.ResourceScope.Process)]
809 [DllImport("oleaut32.dll", PreserveSig = false)]
810 internal static extern void VariantClear(IntPtr variant);