2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / ComRuntimeHelpers.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 #if !SILVERLIGHT // ComObject
19
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;
28
29 #if CODEPLEX_40
30 namespace System.Dynamic {
31 #else
32 namespace Microsoft.Scripting {
33 #endif
34
35     internal static class ComRuntimeHelpers {
36
37 #if MICROSOFT_DYNAMIC
38         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
39 #endif
40         [SecurityCritical]
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)) {
45                 return;
46             }
47
48             switch (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);
53
54                 case ComHresults.DISP_E_BADVARTYPE:
55                     //One of the arguments in rgvarg is not a valid variant type.
56                     break;
57
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();
62
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);
67
68                 case ComHresults.DISP_E_NONAMEDARGS:
69                     // This implementation of IDispatch does not support named arguments.
70                     throw Error.DispNoNamedArgs(message);
71
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);
75
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. 
79                     break;
80
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);
85
86                 case ComHresults.DISP_E_UNKNOWNINTERFACE:
87                     // The interface identifier passed in riid is not IID_NULL.
88                     break;
89
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.
93                     break;
94
95                 case ComHresults.DISP_E_PARAMNOTOPTIONAL:
96                     // A required parameter was omitted.
97                     throw Error.DispParamNotOptional(message);
98             }
99
100             Marshal.ThrowExceptionForHR(hresult);
101         }
102
103         internal static void GetInfoFromType(ComTypes.ITypeInfo typeInfo, out string name, out string documentation) {
104             int dwHelpContext;
105             string strHelpFile;
106
107             typeInfo.GetDocumentation(-1, out name, out documentation, out dwHelpContext, out strHelpFile);
108         }
109
110         internal static string GetNameOfMethod(ComTypes.ITypeInfo typeInfo, int memid) {
111             int cNames;
112             string[] rgNames = new string[1];
113             typeInfo.GetNames(memid, rgNames, 1, out cNames);
114             return rgNames[0];
115         }
116
117         internal static string GetNameOfLib(ComTypes.ITypeLib typeLib) {
118             string name;
119             string strDocString;
120             int dwHelpContext;
121             string strHelpFile;
122
123             typeLib.GetDocumentation(-1, out name, out strDocString, out dwHelpContext, out strHelpFile);
124             return name;
125         }
126
127         internal static string GetNameOfType(ComTypes.ITypeInfo typeInfo) {
128             string name;
129             string documentation;
130             GetInfoFromType(typeInfo, out name, out documentation);
131
132             return name;
133         }
134
135         /// <summary>
136         /// Look for typeinfo using IDispatch.GetTypeInfo
137         /// </summary>
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>
144         [SecurityCritical]
145         internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch, bool throwIfMissingExpectedTypeInfo) {
146             uint typeCount;
147             int hresult = dispatch.TryGetTypeInfoCount(out typeCount);
148             Marshal.ThrowExceptionForHR(hresult);
149             Debug.Assert(typeCount <= 1);
150             if (typeCount == 0) {
151                 return null;
152             }
153
154             IntPtr typeInfoPtr = IntPtr.Zero;
155
156             hresult = dispatch.TryGetTypeInfo(0, 0, out typeInfoPtr);
157             if (!ComHresults.IsSuccess(hresult)) {
158                 CheckIfMissingTypeInfoIsExpected(hresult, throwIfMissingExpectedTypeInfo);
159                 return null;
160             }
161             if (typeInfoPtr == IntPtr.Zero) { // be defensive against components that return IntPtr.Zero
162                 if (throwIfMissingExpectedTypeInfo) {
163                     Marshal.ThrowExceptionForHR(ComHresults.E_FAIL);
164                 }
165                 return null;
166             }
167
168             ComTypes.ITypeInfo typeInfo = null;
169             try {
170                 typeInfo = Marshal.GetObjectForIUnknown(typeInfoPtr) as ComTypes.ITypeInfo;
171             } finally {
172                 Marshal.Release(typeInfoPtr);
173             }
174
175             return typeInfo;
176         }
177
178         /// <summary>
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.
183         /// 
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.
186         /// </summary>
187         [SecurityCritical]
188         private static void CheckIfMissingTypeInfoIsExpected(int hresult, bool throwIfMissingExpectedTypeInfo) {
189             Debug.Assert(!ComHresults.IsSuccess(hresult));
190
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) {
194                 return;
195             }
196
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);
201
202             if (throwIfMissingExpectedTypeInfo) {
203                 Marshal.ThrowExceptionForHR(hresult);
204             }
205         }
206
207         [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
208         [SecurityCritical]
209         internal static ComTypes.TYPEATTR GetTypeAttrForTypeInfo(ComTypes.ITypeInfo typeInfo) {
210             IntPtr pAttrs = IntPtr.Zero;
211             typeInfo.GetTypeAttr(out pAttrs);
212
213             // GetTypeAttr should never return null, this is just to be safe
214             if (pAttrs == IntPtr.Zero) {
215                 throw Error.CannotRetrieveTypeInformation();
216             }
217
218             try {
219                 return (ComTypes.TYPEATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPEATTR));
220             } finally {
221                 typeInfo.ReleaseTypeAttr(pAttrs);
222             }
223         }
224
225         [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
226         [SecurityCritical]
227         internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typeLib) {
228             IntPtr pAttrs = IntPtr.Zero;
229             typeLib.GetLibAttr(out pAttrs);
230
231             // GetTypeAttr should never return null, this is just to be safe
232             if (pAttrs == IntPtr.Zero) {
233                 throw Error.CannotRetrieveTypeInformation();
234             }
235
236             try {
237                 return (ComTypes.TYPELIBATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPELIBATTR));
238             } finally {
239                 typeLib.ReleaseTLibAttr(pAttrs);
240             }
241         }
242
243         public static BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid) {
244             return new BoundDispEvent(rcw, sourceIid, dispid);
245         }
246
247         public static DispCallable CreateDispCallable(IDispatchComObject dispatch, ComMethodDesc method) {
248             return new DispCallable(dispatch, method.Name, method.DispId);
249         }
250     }
251
252     /// <summary>
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.
256     /// </summary>
257     /// 
258     internal static class UnsafeMethods {
259         #region public members
260
261         #region Generated ConvertByrefToPtr
262
263         // *** BEGIN GENERATED CODE ***
264         // generated by function: gen_ConvertByrefToPtr from: generate_comdispatch.py
265
266 #if MICROSOFT_DYNAMIC
267         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
268 #endif
269         [SecurityCritical]
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);
275             }
276         }
277
278 #if MICROSOFT_DYNAMIC
279         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
280 #endif
281         [SecurityCritical]
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);
287             }
288         }
289
290 #if MICROSOFT_DYNAMIC
291         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
292 #endif
293         [SecurityCritical]
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);
299             }
300         }
301
302 #if MICROSOFT_DYNAMIC
303         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
304 #endif
305         [SecurityCritical]
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);
311             }
312         }
313
314 #if MICROSOFT_DYNAMIC
315         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
316 #endif
317         [SecurityCritical]
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);
323             }
324         }
325
326 #if MICROSOFT_DYNAMIC
327         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
328 #endif
329         [SecurityCritical]
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);
335             }
336         }
337
338 #if MICROSOFT_DYNAMIC
339         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
340 #endif
341         [SecurityCritical]
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);
347             }
348         }
349
350 #if MICROSOFT_DYNAMIC
351         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
352 #endif
353         [SecurityCritical]
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);
359             }
360         }
361
362 #if MICROSOFT_DYNAMIC
363         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
364 #endif
365         [SecurityCritical]
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);
371             }
372         }
373
374 #if MICROSOFT_DYNAMIC
375         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
376 #endif
377         [SecurityCritical]
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);
383             }
384         }
385
386 #if MICROSOFT_DYNAMIC
387         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
388 #endif
389         [SecurityCritical]
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);
395             }
396         }
397
398 #if MICROSOFT_DYNAMIC
399         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
400 #endif
401         [SecurityCritical]
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);
407             }
408         }
409
410 #if MICROSOFT_DYNAMIC
411         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
412 #endif
413         [SecurityCritical]
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);
419             }
420         }
421
422
423         // *** END GENERATED CODE ***
424
425         #endregion
426
427 #if MICROSOFT_DYNAMIC
428         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
429 #endif
430         [SecurityCritical]
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);
436             }
437         }
438
439 #if MICROSOFT_DYNAMIC
440         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
441 #endif
442         [SecurityCritical]
443         internal static Variant GetVariantForObject(object obj) {
444             Variant variant = default(Variant);
445             if (obj == null) {
446                 return variant;
447             }
448             InitVariantForObject(obj, ref variant);
449             return variant;
450         }
451
452 #if MICROSOFT_DYNAMIC
453         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
454 #endif
455         [SecurityCritical]
456         internal static void InitVariantForObject(object obj, ref Variant variant) {
457             Debug.Assert(obj != null);
458
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;
463             if (disp != null) {
464                 variant.AsDispatch = obj;
465                 return;
466             }
467
468             System.Runtime.InteropServices.Marshal.GetNativeVariantForObject(obj, UnsafeMethods.ConvertVariantByrefToPtr(ref variant));
469         }
470
471 #if MICROSOFT_DYNAMIC
472         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
473 #endif
474         [SecurityCritical]
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);
479         }
480
481         [Obsolete("do not use this method", true)]
482         public static int IUnknownRelease(IntPtr interfacePointer) {
483             return _IUnknownRelease(interfacePointer);
484         }
485
486         [Obsolete("do not use this method", true)]
487         public static void IUnknownReleaseNotZero(IntPtr interfacePointer) {
488             if (interfacePointer != IntPtr.Zero) {
489                 IUnknownRelease(interfacePointer);
490             }
491         }
492
493 #if MICROSOFT_DYNAMIC
494         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
495 #endif
496         [SecurityCritical]
497         [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
498         [Obsolete("do not use this method", true)]
499         public static int IDispatchInvoke(
500             IntPtr dispatchPointer,
501             int memberDispId,
502             ComTypes.INVOKEKIND flags,
503             ref ComTypes.DISPPARAMS dispParams,
504             out Variant result,
505             out ExcepInfo excepInfo,
506             out uint argErr
507         ) {
508
509             int hresult = _IDispatchInvoke(
510                 dispatchPointer,
511                 memberDispId,
512                 flags,
513                 ref dispParams,
514                 out result,
515                 out excepInfo,
516                 out argErr
517             );
518
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) {
522
523                 // Re-invoke with no result argument to accomodate Word
524                 hresult = _IDispatchInvokeNoResult(
525                     dispatchPointer,
526                     memberDispId,
527                     ComTypes.INVOKEKIND.INVOKE_FUNC,
528                     ref dispParams,
529                     out result,
530                     out excepInfo,
531                     out argErr);
532             }
533             return hresult;
534         }
535
536         [Obsolete("do not use this method", true)]
537 #if MICROSOFT_DYNAMIC
538         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
539 #endif
540         [SecurityCritical]
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);
546             if (hresult < 0) {
547                 Marshal.ThrowExceptionForHR(hresult);
548             }
549
550             if (methodDispId != dispIds[0]) {
551                 throw Error.GetIDsOfNamesInvalid(names[0]);
552             }
553
554             int[] keywordArgDispIds = dispIds.RemoveFirst(); // Remove the dispId of the method name
555
556             pinningHandle.Target = keywordArgDispIds;
557             return Marshal.UnsafeAddrOfPinnedArrayElement(keywordArgDispIds, 0);
558         }
559
560         #endregion
561
562         #region non-public members
563
564         [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
565         [SecurityCritical]
566         static UnsafeMethods() {
567         }
568
569         private static void EmitLoadArg(ILGenerator il, int index) {
570             ContractUtils.Requires(index >= 0, "index");
571
572             switch (index) {
573                 case 0:
574                     il.Emit(OpCodes.Ldarg_0);
575                     break;
576                 case 1:
577                     il.Emit(OpCodes.Ldarg_1);
578                     break;
579                 case 2:
580                     il.Emit(OpCodes.Ldarg_2);
581                     break;
582                 case 3:
583                     il.Emit(OpCodes.Ldarg_3);
584                     break;
585                 default:
586                     if (index <= Byte.MaxValue) {
587                         il.Emit(OpCodes.Ldarg_S, (byte)index);
588                     } else {
589                         il.Emit(OpCodes.Ldarg, index);
590                     }
591                     break;
592             }
593         }
594
595         /// <summary>
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.
599         /// </summary>
600         [Conditional("DEBUG")]
601         [SecurityCritical]
602         private static void AssertByrefPointsToStack(IntPtr ptr) {
603             if (Marshal.ReadInt32(ptr) == _dummyMarker) {
604                 // Prevent recursion
605                 return;
606             }
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));
611         }
612
613         private static readonly object _lock = new object();
614         private static ModuleBuilder _dynamicModule;
615
616         internal static ModuleBuilder DynamicModule {
617             get {
618                 if (_dynamicModule != null) {
619                     return _dynamicModule;
620                 }
621                 lock (_lock) {
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")}, 
629                                 new object[] {true})
630                         };
631
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);
636                     }
637                     return _dynamicModule;
638                 }
639             }
640         }
641
642         private const int _dummyMarker = 0x10101010;
643
644         /// <summary>
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.
648         /// </summary>
649         private delegate int IUnknownReleaseDelegate(IntPtr interfacePointer);
650         private static readonly IUnknownReleaseDelegate _IUnknownRelease = Create_IUnknownRelease();
651
652         private static IUnknownReleaseDelegate Create_IUnknownRelease() {
653             DynamicMethod dm = new DynamicMethod("IUnknownRelease", typeof(int), new Type[] { typeof(IntPtr) }, DynamicModule);
654
655             ILGenerator method = dm.GetILGenerator();
656
657             // return functionPtr(...)
658
659             method.Emit(OpCodes.Ldarg_0);
660
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);
668
669             SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConvention.Winapi, typeof(int));
670             signature.AddArgument(typeof(IntPtr));
671             method.Emit(OpCodes.Calli, signature);
672
673             method.Emit(OpCodes.Ret);
674
675             return (IUnknownReleaseDelegate)dm.CreateDelegate(typeof(IUnknownReleaseDelegate));
676         }
677
678         internal static readonly IntPtr NullInterfaceId = GetNullInterfaceId();
679
680         [SecurityCritical]
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);
686             }
687             return ptr;
688         }
689
690         /// <summary>
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).
698         /// </summary>
699         private delegate int IDispatchInvokeDelegate(
700             IntPtr dispatchPointer,
701             int memberDispId,
702             ComTypes.INVOKEKIND flags,
703             ref ComTypes.DISPPARAMS dispParams,
704             out Variant result,
705             out ExcepInfo excepInfo,
706             out uint argErr
707         );
708
709         private static readonly IDispatchInvokeDelegate _IDispatchInvoke = Create_IDispatchInvoke(true);
710         private static IDispatchInvokeDelegate _IDispatchInvokeNoResultImpl;
711
712         private static IDispatchInvokeDelegate _IDispatchInvokeNoResult {
713             get {
714                 if (_IDispatchInvokeNoResultImpl == null) {
715                     lock (_IDispatchInvoke) {
716                         if (_IDispatchInvokeNoResultImpl == null) {
717                             _IDispatchInvokeNoResultImpl = Create_IDispatchInvoke(false);
718                         }
719                     }
720                 }
721                 return _IDispatchInvokeNoResultImpl;
722             }
723         }
724
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);
734
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();
743
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();
747
748             // return functionPtr(...)
749
750             EmitLoadArg(method, dispatchPointerIndex);
751             EmitLoadArg(method, memberDispIdIndex);
752
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
758             } else {
759                 method.Emit(OpCodes.Ldc_I8, UnsafeMethods.NullInterfaceId.ToInt64()); // riid
760             }
761             method.Emit(OpCodes.Conv_I);
762
763             method.Emit(OpCodes.Ldc_I4_0); // lcid
764             EmitLoadArg(method, flagsIndex);
765
766             EmitLoadArg(method, dispParamsIndex);
767
768             if (returnResult) {
769                 EmitLoadArg(method, resultIndex);
770             } else {
771                 method.Emit(OpCodes.Ldsfld, typeof(IntPtr).GetField("Zero"));
772             }
773             EmitLoadArg(method, exceptInfoIndex);
774             EmitLoadArg(method, argErrIndex);
775
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);
783
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
789                     typeof(int),    // lcid
790                     typeof(ushort), // flags
791                     typeof(IntPtr), // dispParams
792                     typeof(IntPtr), // result
793                     typeof(IntPtr), // excepInfo
794                     typeof(IntPtr), // argErr
795                 };
796             signature.AddArguments(invokeParamTypes, null, null);
797             method.Emit(OpCodes.Calli, signature);
798
799             method.Emit(OpCodes.Ret);
800             return (IDispatchInvokeDelegate)dm.CreateDelegate(typeof(IDispatchInvokeDelegate));
801         }
802
803         #endregion
804     }
805
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);
811     }
812 }
813
814 #endif