diff options
Diffstat (limited to 'src/mscorlib/src/System/Runtime')
242 files changed, 32556 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs new file mode 100644 index 0000000000..b0010fd7bd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace System.Runtime.CompilerServices +{ + using System; + + [AttributeUsage(AttributeTargets.Field)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class AccessedThroughPropertyAttribute : Attribute + { + private readonly string propertyName; + + public AccessedThroughPropertyAttribute(string propertyName) + { + this.propertyName = propertyName; + } + + public string PropertyName + { + get + { + return propertyName; + } + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AssemblyAttributesGoHere.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AssemblyAttributesGoHere.cs new file mode 100644 index 0000000000..c021353475 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AssemblyAttributesGoHere.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.CompilerServices { + + using System; + + // NOTE TO DEVELOPERS: These classes are used by ALink (the assembly linker). + // They're used for metadata tokens for making multi-module assemblies. + // Do not randomly touch these classes. + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal sealed class AssemblyAttributesGoHere + { + + internal AssemblyAttributesGoHere() + { + } + } + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal sealed class AssemblyAttributesGoHereS + { + internal AssemblyAttributesGoHereS() + { + } + } + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal sealed class AssemblyAttributesGoHereM + { + internal AssemblyAttributesGoHereM() + { + } + } + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal sealed class AssemblyAttributesGoHereSM + { + internal AssemblyAttributesGoHereSM() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AssemblySettingAttributes.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AssemblySettingAttributes.cs new file mode 100644 index 0000000000..5251122629 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AssemblySettingAttributes.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace System.Runtime.CompilerServices +{ + + using System; + using System.Runtime.InteropServices; + + /* + NGenHint is not supported in Whidbey + + [Serializable] + public enum NGenHint + { + Default = 0x0000, // No preference specified + + Eager = 0x0001, // NGen at install time + Lazy = 0x0002, // NGen after install time + Never = 0x0003, // Assembly should not be ngened + } + */ + + [Serializable] + public enum LoadHint + { + Default = 0x0000, // No preference specified + + Always = 0x0001, // Dependency is always loaded + Sometimes = 0x0002, // Dependency is sometimes loaded + //Never = 0x0003, // Dependency is never loaded + } + + [Serializable] + [AttributeUsage(AttributeTargets.Assembly)] + public sealed class DefaultDependencyAttribute : Attribute + { + private LoadHint loadHint; + + public DefaultDependencyAttribute ( + LoadHint loadHintArgument + ) + { + loadHint = loadHintArgument; + } + + public LoadHint LoadHint + { + get + { + return loadHint; + } + } + } + + +[Serializable] +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class DependencyAttribute : Attribute + { + private String dependentAssembly; + private LoadHint loadHint; + + public DependencyAttribute ( + String dependentAssemblyArgument, + LoadHint loadHintArgument + ) + { + dependentAssembly = dependentAssemblyArgument; + loadHint = loadHintArgument; + } + + public String DependentAssembly + { + get + { + return dependentAssembly; + } + } + + public LoadHint LoadHint + { + get + { + return loadHint; + } + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs new file mode 100644 index 0000000000..05850605b8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs @@ -0,0 +1,1187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// Compiler-targeted types that build tasks for use as the return types of asynchronous methods. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Runtime.ExceptionServices; +using System.Security; +using System.Security.Permissions; +using System.Threading; +using System.Threading.Tasks; + +#if FEATURE_COMINTEROP +using System.Runtime.InteropServices.WindowsRuntime; +#endif // FEATURE_COMINTEROP + +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// Provides a builder for asynchronous methods that return void. + /// This type is intended for compiler use only. + /// </summary> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct AsyncVoidMethodBuilder + { + /// <summary>The synchronization context associated with this operation.</summary> + private SynchronizationContext m_synchronizationContext; + /// <summary>State related to the IAsyncStateMachine.</summary> + private AsyncMethodBuilderCore m_coreState; // mutable struct: must not be readonly + /// <summary>Task used for debugging and logging purposes only. Lazily initialized.</summary> + private Task m_task; + + /// <summary>Initializes a new <see cref="AsyncVoidMethodBuilder"/>.</summary> + /// <returns>The initialized <see cref="AsyncVoidMethodBuilder"/>.</returns> + public static AsyncVoidMethodBuilder Create() + { + // Capture the current sync context. If there isn't one, use the dummy s_noContextCaptured + // instance; this allows us to tell the state of no captured context apart from the state + // of an improperly constructed builder instance. + SynchronizationContext sc = SynchronizationContext.CurrentNoFlow; + if (sc != null) + sc.OperationStarted(); + return new AsyncVoidMethodBuilder() { m_synchronizationContext = sc }; + } + + /// <summary>Initiates the builder's execution with the associated state machine.</summary> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="stateMachine">The state machine instance, passed by reference.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception> + [SecuritySafeCritical] + [DebuggerStepThrough] + public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + // See comment on AsyncMethodBuilderCore.Start + // AsyncMethodBuilderCore.Start(ref stateMachine); + + if (stateMachine == null) throw new ArgumentNullException("stateMachine"); + Contract.EndContractBlock(); + + // Run the MoveNext method within a copy-on-write ExecutionContext scope. + // This allows us to undo any ExecutionContext changes made in MoveNext, + // so that they won't "leak" out of the first await. + + Thread currentThread = Thread.CurrentThread; + ExecutionContextSwitcher ecs = default(ExecutionContextSwitcher); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + ExecutionContext.EstablishCopyOnWriteScope(currentThread, ref ecs); + stateMachine.MoveNext(); + } + finally + { + ecs.Undo(currentThread); + } + } + + /// <summary>Associates the builder with the state machine it represents.</summary> + /// <param name="stateMachine">The heap-allocated state machine object.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception> + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + m_coreState.SetStateMachine(stateMachine); // argument validation handled by AsyncMethodBuilderCore + } + + /// <summary> + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// </summary> + /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="awaiter">The awaiter.</param> + /// <param name="stateMachine">The state machine.</param> + public void AwaitOnCompleted<TAwaiter, TStateMachine>( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + try + { + AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize = null; + var continuation = m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runnerToInitialize); + Contract.Assert(continuation != null, "GetCompletionAction should always return a valid action."); + + // If this is our first await, such that we've not yet boxed the state machine, do so now. + if (m_coreState.m_stateMachine == null) + { + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Task.Id, "Async: " + stateMachine.GetType().Name, 0); + + // Box the state machine, then tell the boxed instance to call back into its own builder, + // so we can cache the boxed reference. NOTE: The language compiler may choose to use + // a class instead of a struct for the state machine for debugging purposes; in such cases, + // the stateMachine will already be an object. + m_coreState.PostBoxInitialization(stateMachine, runnerToInitialize, null); + } + + awaiter.OnCompleted(continuation); + } + catch (Exception exc) + { + // Prevent exceptions from leaking to the call site, which could + // then allow multiple flows of execution through the same async method + // if the awaiter had already scheduled the continuation by the time + // the exception was thrown. We propagate the exception on the + // ThreadPool because we can trust it to not throw, unlike + // if we were to go to a user-supplied SynchronizationContext, + // whose Post method could easily throw. + AsyncMethodBuilderCore.ThrowAsync(exc, targetContext: null); + } + } + + /// <summary> + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// </summary> + /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="awaiter">The awaiter.</param> + /// <param name="stateMachine">The state machine.</param> + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + try + { + AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize = null; + var continuation = m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runnerToInitialize); + Contract.Assert(continuation != null, "GetCompletionAction should always return a valid action."); + + // If this is our first await, such that we've not yet boxed the state machine, do so now. + if (m_coreState.m_stateMachine == null) + { + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Task.Id, "Async: " + stateMachine.GetType().Name, 0); + + // Box the state machine, then tell the boxed instance to call back into its own builder, + // so we can cache the boxed reference. NOTE: The language compiler may choose to use + // a class instead of a struct for the state machine for debugging purposes; in such cases, + // the stateMachine will already be an object. + m_coreState.PostBoxInitialization(stateMachine, runnerToInitialize, null); + } + + awaiter.UnsafeOnCompleted(continuation); + } + catch (Exception e) + { + AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null); + } + } + + /// <summary>Completes the method builder successfully.</summary> + public void SetResult() + { + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Task.Id, AsyncCausalityStatus.Completed); + + if (m_synchronizationContext != null) + { + NotifySynchronizationContextOfCompletion(); + } + } + + /// <summary>Faults the method builder with an exception.</summary> + /// <param name="exception">The exception that is the cause of this fault.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception> + public void SetException(Exception exception) + { + if (exception == null) throw new ArgumentNullException("exception"); + Contract.EndContractBlock(); + + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Task.Id, AsyncCausalityStatus.Error); + + if (m_synchronizationContext != null) + { + // If we captured a synchronization context, Post the throwing of the exception to it + // and decrement its outstanding operation count. + try + { + AsyncMethodBuilderCore.ThrowAsync(exception, targetContext: m_synchronizationContext); + } + finally + { + NotifySynchronizationContextOfCompletion(); + } + } + else + { + // Otherwise, queue the exception to be thrown on the ThreadPool. This will + // result in a crash unless legacy exception behavior is enabled by a config + // file or a CLR host. + AsyncMethodBuilderCore.ThrowAsync(exception, targetContext: null); + } + } + + /// <summary>Notifies the current synchronization context that the operation completed.</summary> + private void NotifySynchronizationContextOfCompletion() + { + Contract.Assert(m_synchronizationContext != null, "Must only be used with a non-null context."); + try + { + m_synchronizationContext.OperationCompleted(); + } + catch (Exception exc) + { + // If the interaction with the SynchronizationContext goes awry, + // fall back to propagating on the ThreadPool. + AsyncMethodBuilderCore.ThrowAsync(exc, targetContext: null); + } + } + + // This property lazily instantiates the Task in a non-thread-safe manner. + private Task Task + { + get + { + if (m_task == null) m_task = new Task(); + return m_task; + } + } + + /// <summary> + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// </summary> + /// <remarks> + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and AsyncCausalityTracer in a single-threaded manner. + /// </remarks> + private object ObjectIdForDebugger { get { return this.Task; } } + } + + /// <summary> + /// Provides a builder for asynchronous methods that return <see cref="System.Threading.Tasks.Task"/>. + /// This type is intended for compiler use only. + /// </summary> + /// <remarks> + /// AsyncTaskMethodBuilder is a value type, and thus it is copied by value. + /// Prior to being copied, one of its Task, SetResult, or SetException members must be accessed, + /// or else the copies may end up building distinct Task instances. + /// </remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct AsyncTaskMethodBuilder + { + /// <summary>A cached VoidTaskResult task used for builders that complete synchronously.</summary> + private readonly static Task<VoidTaskResult> s_cachedCompleted = AsyncTaskMethodBuilder<VoidTaskResult>.s_defaultResultTask; + + /// <summary>The generic builder object to which this non-generic instance delegates.</summary> + private AsyncTaskMethodBuilder<VoidTaskResult> m_builder; // mutable struct: must not be readonly + + /// <summary>Initializes a new <see cref="AsyncTaskMethodBuilder"/>.</summary> + /// <returns>The initialized <see cref="AsyncTaskMethodBuilder"/>.</returns> + public static AsyncTaskMethodBuilder Create() + { + return default(AsyncTaskMethodBuilder); + // Note: If ATMB<T>.Create is modified to do any initialization, this + // method needs to be updated to do m_builder = ATMB<T>.Create(). + } + + /// <summary>Initiates the builder's execution with the associated state machine.</summary> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="stateMachine">The state machine instance, passed by reference.</param> + [SecuritySafeCritical] + [DebuggerStepThrough] + public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + // See comment on AsyncMethodBuilderCore.Start + // AsyncMethodBuilderCore.Start(ref stateMachine); + + if (stateMachine == null) throw new ArgumentNullException("stateMachine"); + Contract.EndContractBlock(); + + // Run the MoveNext method within a copy-on-write ExecutionContext scope. + // This allows us to undo any ExecutionContext changes made in MoveNext, + // so that they won't "leak" out of the first await. + + Thread currentThread = Thread.CurrentThread; + ExecutionContextSwitcher ecs = default(ExecutionContextSwitcher); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + ExecutionContext.EstablishCopyOnWriteScope(currentThread, ref ecs); + stateMachine.MoveNext(); + } + finally + { + ecs.Undo(currentThread); + } + } + + /// <summary>Associates the builder with the state machine it represents.</summary> + /// <param name="stateMachine">The heap-allocated state machine object.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception> + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + m_builder.SetStateMachine(stateMachine); // argument validation handled by AsyncMethodBuilderCore + } + + /// <summary> + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// </summary> + /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="awaiter">The awaiter.</param> + /// <param name="stateMachine">The state machine.</param> + public void AwaitOnCompleted<TAwaiter, TStateMachine>( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + m_builder.AwaitOnCompleted<TAwaiter, TStateMachine>(ref awaiter, ref stateMachine); + } + + /// <summary> + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// </summary> + /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="awaiter">The awaiter.</param> + /// <param name="stateMachine">The state machine.</param> + public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + m_builder.AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref awaiter, ref stateMachine); + } + + /// <summary>Gets the <see cref="System.Threading.Tasks.Task"/> for this builder.</summary> + /// <returns>The <see cref="System.Threading.Tasks.Task"/> representing the builder's asynchronous operation.</returns> + /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception> + public Task Task { get { return m_builder.Task; } } + + /// <summary> + /// Completes the <see cref="System.Threading.Tasks.Task"/> in the + /// <see cref="System.Threading.Tasks.TaskStatus">RanToCompletion</see> state. + /// </summary> + /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception> + /// <exception cref="System.InvalidOperationException">The task has already completed.</exception> + public void SetResult() + { + // Accessing AsyncTaskMethodBuilder.s_cachedCompleted is faster than + // accessing AsyncTaskMethodBuilder<T>.s_defaultResultTask. + m_builder.SetResult(s_cachedCompleted); + } + + /// <summary> + /// Completes the <see cref="System.Threading.Tasks.Task"/> in the + /// <see cref="System.Threading.Tasks.TaskStatus">Faulted</see> state with the specified exception. + /// </summary> + /// <param name="exception">The <see cref="System.Exception"/> to use to fault the task.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception> + /// <exception cref="System.InvalidOperationException">The task has already completed.</exception> + public void SetException(Exception exception) { m_builder.SetException(exception); } + + /// <summary> + /// Called by the debugger to request notification when the first wait operation + /// (await, Wait, Result, etc.) on this builder's task completes. + /// </summary> + /// <param name="enabled"> + /// true to enable notification; false to disable a previously set notification. + /// </param> + internal void SetNotificationForWaitCompletion(bool enabled) + { + m_builder.SetNotificationForWaitCompletion(enabled); + } + + /// <summary> + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// </summary> + /// <remarks> + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and tracing pruposes, and only in a single-threaded manner + /// when no other threads are in the middle of accessing this property or this.Task. + /// </remarks> + private object ObjectIdForDebugger { get { return this.Task; } } + } + + /// <summary> + /// Provides a builder for asynchronous methods that return <see cref="System.Threading.Tasks.Task{TResult}"/>. + /// This type is intended for compiler use only. + /// </summary> + /// <remarks> + /// AsyncTaskMethodBuilder{TResult} is a value type, and thus it is copied by value. + /// Prior to being copied, one of its Task, SetResult, or SetException members must be accessed, + /// or else the copies may end up building distinct Task instances. + /// </remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct AsyncTaskMethodBuilder<TResult> + { + /// <summary>A cached task for default(TResult).</summary> + internal readonly static Task<TResult> s_defaultResultTask = AsyncTaskCache.CreateCacheableTask(default(TResult)); + + // WARNING: For performance reasons, the m_task field is lazily initialized. + // For correct results, the struct AsyncTaskMethodBuilder<TResult> must + // always be used from the same location/copy, at least until m_task is + // initialized. If that guarantee is broken, the field could end up being + // initialized on the wrong copy. + + /// <summary>State related to the IAsyncStateMachine.</summary> + private AsyncMethodBuilderCore m_coreState; // mutable struct: must not be readonly + /// <summary>The lazily-initialized built task.</summary> + private Task<TResult> m_task; // lazily-initialized: must not be readonly + + /// <summary>Initializes a new <see cref="AsyncTaskMethodBuilder"/>.</summary> + /// <returns>The initialized <see cref="AsyncTaskMethodBuilder"/>.</returns> + public static AsyncTaskMethodBuilder<TResult> Create() + { + return default(AsyncTaskMethodBuilder<TResult>); + // NOTE: If this method is ever updated to perform more initialization, + // ATMB.Create must also be updated to call this Create method. + } + + /// <summary>Initiates the builder's execution with the associated state machine.</summary> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="stateMachine">The state machine instance, passed by reference.</param> + [SecuritySafeCritical] + [DebuggerStepThrough] + public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + // See comment on AsyncMethodBuilderCore.Start + // AsyncMethodBuilderCore.Start(ref stateMachine); + + if (stateMachine == null) throw new ArgumentNullException("stateMachine"); + Contract.EndContractBlock(); + + // Run the MoveNext method within a copy-on-write ExecutionContext scope. + // This allows us to undo any ExecutionContext changes made in MoveNext, + // so that they won't "leak" out of the first await. + + Thread currentThread = Thread.CurrentThread; + ExecutionContextSwitcher ecs = default(ExecutionContextSwitcher); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + ExecutionContext.EstablishCopyOnWriteScope(currentThread, ref ecs); + stateMachine.MoveNext(); + } + finally + { + ecs.Undo(currentThread); + } + } + + /// <summary>Associates the builder with the state machine it represents.</summary> + /// <param name="stateMachine">The heap-allocated state machine object.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception> + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + m_coreState.SetStateMachine(stateMachine); // argument validation handled by AsyncMethodBuilderCore + } + + /// <summary> + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// </summary> + /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="awaiter">The awaiter.</param> + /// <param name="stateMachine">The state machine.</param> + public void AwaitOnCompleted<TAwaiter, TStateMachine>( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + try + { + AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize = null; + var continuation = m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runnerToInitialize); + Contract.Assert(continuation != null, "GetCompletionAction should always return a valid action."); + + // If this is our first await, such that we've not yet boxed the state machine, do so now. + if (m_coreState.m_stateMachine == null) + { + // Force the Task to be initialized prior to the first suspending await so + // that the original stack-based builder has a reference to the right Task. + var builtTask = this.Task; + + // Box the state machine, then tell the boxed instance to call back into its own builder, + // so we can cache the boxed reference. NOTE: The language compiler may choose to use + // a class instead of a struct for the state machine for debugging purposes; in such cases, + // the stateMachine will already be an object. + m_coreState.PostBoxInitialization(stateMachine, runnerToInitialize, builtTask); + } + + awaiter.OnCompleted(continuation); + } + catch (Exception e) + { + AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null); + } + } + + /// <summary> + /// Schedules the specified state machine to be pushed forward when the specified awaiter completes. + /// </summary> + /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="awaiter">The awaiter.</param> + /// <param name="stateMachine">The state machine.</param> + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + try + { + AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize = null; + var continuation = m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runnerToInitialize); + Contract.Assert(continuation != null, "GetCompletionAction should always return a valid action."); + + // If this is our first await, such that we've not yet boxed the state machine, do so now. + if (m_coreState.m_stateMachine == null) + { + // Force the Task to be initialized prior to the first suspending await so + // that the original stack-based builder has a reference to the right Task. + var builtTask = this.Task; + + // Box the state machine, then tell the boxed instance to call back into its own builder, + // so we can cache the boxed reference. NOTE: The language compiler may choose to use + // a class instead of a struct for the state machine for debugging purposes; in such cases, + // the stateMachine will already be an object. + m_coreState.PostBoxInitialization(stateMachine, runnerToInitialize, builtTask); + } + + awaiter.UnsafeOnCompleted(continuation); + } + catch (Exception e) + { + AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null); + } + } + + /// <summary>Gets the <see cref="System.Threading.Tasks.Task{TResult}"/> for this builder.</summary> + /// <returns>The <see cref="System.Threading.Tasks.Task{TResult}"/> representing the builder's asynchronous operation.</returns> + public Task<TResult> Task + { + get + { + // Get and return the task. If there isn't one, first create one and store it. + var task = m_task; + if (task == null) { m_task = task = new Task<TResult>(); } + return task; + } + } + + /// <summary> + /// Completes the <see cref="System.Threading.Tasks.Task{TResult}"/> in the + /// <see cref="System.Threading.Tasks.TaskStatus">RanToCompletion</see> state with the specified result. + /// </summary> + /// <param name="result">The result to use to complete the task.</param> + /// <exception cref="System.InvalidOperationException">The task has already completed.</exception> + public void SetResult(TResult result) + { + // Get the currently stored task, which will be non-null if get_Task has already been accessed. + // If there isn't one, get a task and store it. + var task = m_task; + if (task == null) + { + m_task = GetTaskForResult(result); + Contract.Assert(m_task != null, "GetTaskForResult should never return null"); + } + // Slow path: complete the existing task. + else + { + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, task.Id, AsyncCausalityStatus.Completed); + + //only log if we have a real task that was previously created + if (System.Threading.Tasks.Task.s_asyncDebuggingEnabled) + { + System.Threading.Tasks.Task.RemoveFromActiveTasks(task.Id); + } + + if (!task.TrySetResult(result)) + { + throw new InvalidOperationException(Environment.GetResourceString("TaskT_TransitionToFinal_AlreadyCompleted")); + } + } + } + + /// <summary> + /// Completes the builder by using either the supplied completed task, or by completing + /// the builder's previously accessed task using default(TResult). + /// </summary> + /// <param name="completedTask">A task already completed with the value default(TResult).</param> + /// <exception cref="System.InvalidOperationException">The task has already completed.</exception> + internal void SetResult(Task<TResult> completedTask) + { + Contract.Requires(completedTask != null, "Expected non-null task"); + Contract.Requires(completedTask.Status == TaskStatus.RanToCompletion, "Expected a successfully completed task"); + + // Get the currently stored task, which will be non-null if get_Task has already been accessed. + // If there isn't one, store the supplied completed task. + var task = m_task; + if (task == null) + { + m_task = completedTask; + } + else + { + // Otherwise, complete the task that's there. + SetResult(default(TResult)); + } + } + + /// <summary> + /// Completes the <see cref="System.Threading.Tasks.Task{TResult}"/> in the + /// <see cref="System.Threading.Tasks.TaskStatus">Faulted</see> state with the specified exception. + /// </summary> + /// <param name="exception">The <see cref="System.Exception"/> to use to fault the task.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The task has already completed.</exception> + public void SetException(Exception exception) + { + if (exception == null) throw new ArgumentNullException("exception"); + Contract.EndContractBlock(); + + + var task = m_task; + if (task == null) + { + // Get the task, forcing initialization if it hasn't already been initialized. + task = this.Task; + } + + // If the exception represents cancellation, cancel the task. Otherwise, fault the task. + var oce = exception as OperationCanceledException; + bool successfullySet = oce != null ? + task.TrySetCanceled(oce.CancellationToken, oce) : + task.TrySetException(exception); + + // Unlike with TaskCompletionSource, we do not need to spin here until m_task is completed, + // since AsyncTaskMethodBuilder.SetException should not be immediately followed by any code + // that depends on the task having completely completed. Moreover, with correct usage, + // SetResult or SetException should only be called once, so the Try* methods should always + // return true, so no spinning would be necessary anyway (the spinning in TCS is only relevant + // if another thread completes the task first). + + if (!successfullySet) + { + throw new InvalidOperationException(Environment.GetResourceString("TaskT_TransitionToFinal_AlreadyCompleted")); + } + } + + /// <summary> + /// Called by the debugger to request notification when the first wait operation + /// (await, Wait, Result, etc.) on this builder's task completes. + /// </summary> + /// <param name="enabled"> + /// true to enable notification; false to disable a previously set notification. + /// </param> + /// <remarks> + /// This should only be invoked from within an asynchronous method, + /// and only by the debugger. + /// </remarks> + internal void SetNotificationForWaitCompletion(bool enabled) + { + // Get the task (forcing initialization if not already initialized), and set debug notification + this.Task.SetNotificationForWaitCompletion(enabled); + } + + /// <summary> + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// </summary> + /// <remarks> + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner + /// when no other threads are in the middle of accessing this property or this.Task. + /// </remarks> + private object ObjectIdForDebugger { get { return this.Task; } } + + /// <summary> + /// Gets a task for the specified result. This will either + /// be a cached or new task, never null. + /// </summary> + /// <param name="result">The result for which we need a task.</param> + /// <returns>The completed task containing the result.</returns> + [SecuritySafeCritical] // for JitHelpers.UnsafeCast + private Task<TResult> GetTaskForResult(TResult result) + { + Contract.Ensures( + EqualityComparer<TResult>.Default.Equals(result, Contract.Result<Task<TResult>>().Result), + "The returned task's Result must return the same value as the specified result value."); + + // The goal of this function is to be give back a cached task if possible, + // or to otherwise give back a new task. To give back a cached task, + // we need to be able to evaluate the incoming result value, and we need + // to avoid as much overhead as possible when doing so, as this function + // is invoked as part of the return path from every async method. + // Most tasks won't be cached, and thus we need the checks for those that are + // to be as close to free as possible. This requires some trickiness given the + // lack of generic specialization in .NET. + // + // Be very careful when modifying this code. It has been tuned + // to comply with patterns recognized by both 32-bit and 64-bit JITs. + // If changes are made here, be sure to look at the generated assembly, as + // small tweaks can have big consequences for what does and doesn't get optimized away. + // + // Note that this code only ever accesses a static field when it knows it'll + // find a cached value, since static fields (even if readonly and integral types) + // require special access helpers in this NGEN'd and domain-neutral. + + if (null != (object)default(TResult)) // help the JIT avoid the value type branches for ref types + { + // Special case simple value types: + // - Boolean + // - Byte, SByte + // - Char + // - Decimal + // - Int32, UInt32 + // - Int64, UInt64 + // - Int16, UInt16 + // - IntPtr, UIntPtr + // As of .NET 4.5, the (Type)(object)result pattern used below + // is recognized and optimized by both 32-bit and 64-bit JITs. + + // For Boolean, we cache all possible values. + if (typeof(TResult) == typeof(Boolean)) // only the relevant branches are kept for each value-type generic instantiation + { + Boolean value = (Boolean)(object)result; + Task<Boolean> task = value ? AsyncTaskCache.TrueTask : AsyncTaskCache.FalseTask; + return JitHelpers.UnsafeCast<Task<TResult>>(task); // UnsafeCast avoids type check we know will succeed + } + // For Int32, we cache a range of common values, e.g. [-1,4). + else if (typeof(TResult) == typeof(Int32)) + { + // Compare to constants to avoid static field access if outside of cached range. + // We compare to the upper bound first, as we're more likely to cache miss on the upper side than on the + // lower side, due to positive values being more common than negative as return values. + Int32 value = (Int32)(object)result; + if (value < AsyncTaskCache.EXCLUSIVE_INT32_MAX && + value >= AsyncTaskCache.INCLUSIVE_INT32_MIN) + { + Task<Int32> task = AsyncTaskCache.Int32Tasks[value - AsyncTaskCache.INCLUSIVE_INT32_MIN]; + return JitHelpers.UnsafeCast<Task<TResult>>(task); // UnsafeCast avoids a type check we know will succeed + } + } + // For other known value types, we only special-case 0 / default(TResult). + else if ( + (typeof(TResult) == typeof(UInt32) && default(UInt32) == (UInt32)(object)result) || + (typeof(TResult) == typeof(Byte) && default(Byte) == (Byte)(object)result) || + (typeof(TResult) == typeof(SByte) && default(SByte) == (SByte)(object)result) || + (typeof(TResult) == typeof(Char) && default(Char) == (Char)(object)result) || + (typeof(TResult) == typeof(Decimal) && default(Decimal) == (Decimal)(object)result) || + (typeof(TResult) == typeof(Int64) && default(Int64) == (Int64)(object)result) || + (typeof(TResult) == typeof(UInt64) && default(UInt64) == (UInt64)(object)result) || + (typeof(TResult) == typeof(Int16) && default(Int16) == (Int16)(object)result) || + (typeof(TResult) == typeof(UInt16) && default(UInt16) == (UInt16)(object)result) || + (typeof(TResult) == typeof(IntPtr) && default(IntPtr) == (IntPtr)(object)result) || + (typeof(TResult) == typeof(UIntPtr) && default(UIntPtr) == (UIntPtr)(object)result)) + { + return s_defaultResultTask; + } + } + else if (result == null) // optimized away for value types + { + return s_defaultResultTask; + } + + // No cached task is available. Manufacture a new one for this result. + return new Task<TResult>(result); + } + } + + /// <summary>Provides a cache of closed generic tasks for async methods.</summary> + internal static class AsyncTaskCache + { + // All static members are initialized inline to ensure type is beforefieldinit + + /// <summary>A cached Task{Boolean}.Result == true.</summary> + internal readonly static Task<Boolean> TrueTask = CreateCacheableTask(true); + /// <summary>A cached Task{Boolean}.Result == false.</summary> + internal readonly static Task<Boolean> FalseTask = CreateCacheableTask(false); + + /// <summary>The cache of Task{Int32}.</summary> + internal readonly static Task<Int32>[] Int32Tasks = CreateInt32Tasks(); + /// <summary>The minimum value, inclusive, for which we want a cached task.</summary> + internal const Int32 INCLUSIVE_INT32_MIN = -1; + /// <summary>The maximum value, exclusive, for which we want a cached task.</summary> + internal const Int32 EXCLUSIVE_INT32_MAX = 9; + /// <summary>Creates an array of cached tasks for the values in the range [INCLUSIVE_MIN,EXCLUSIVE_MAX).</summary> + private static Task<Int32>[] CreateInt32Tasks() + { + Contract.Assert(EXCLUSIVE_INT32_MAX >= INCLUSIVE_INT32_MIN, "Expected max to be at least min"); + var tasks = new Task<Int32>[EXCLUSIVE_INT32_MAX - INCLUSIVE_INT32_MIN]; + for (int i = 0; i < tasks.Length; i++) + { + tasks[i] = CreateCacheableTask(i + INCLUSIVE_INT32_MIN); + } + return tasks; + } + + /// <summary>Creates a non-disposable task.</summary> + /// <typeparam name="TResult">Specifies the result type.</typeparam> + /// <param name="result">The result for the task.</param> + /// <returns>The cacheable task.</returns> + internal static Task<TResult> CreateCacheableTask<TResult>(TResult result) + { + return new Task<TResult>(false, result, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); + } + } + + /// <summary>Holds state related to the builder's IAsyncStateMachine.</summary> + /// <remarks>This is a mutable struct. Be very delicate with it.</remarks> + internal struct AsyncMethodBuilderCore + { + /// <summary>A reference to the heap-allocated state machine object associated with this builder.</summary> + internal IAsyncStateMachine m_stateMachine; + /// <summary>A cached Action delegate used when dealing with a default ExecutionContext.</summary> + internal Action m_defaultContextAction; + + // This method is copy&pasted into the public Start methods to avoid size overhead of valuetype generic instantiations. + // Ideally, we would build intrinsics to get the raw ref address and raw code address of MoveNext, and just use the shared implementation. +#if false + /// <summary>Initiates the builder's execution with the associated state machine.</summary> + /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam> + /// <param name="stateMachine">The state machine instance, passed by reference.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument is null (Nothing in Visual Basic).</exception> + [SecuritySafeCritical] + [DebuggerStepThrough] + internal static void Start<TStateMachine>(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine + { + if (stateMachine == null) throw new ArgumentNullException("stateMachine"); + Contract.EndContractBlock(); + + // Run the MoveNext method within a copy-on-write ExecutionContext scope. + // This allows us to undo any ExecutionContext changes made in MoveNext, + // so that they won't "leak" out of the first await. + + Thread currentThread = Thread.CurrentThread; + ExecutionContextSwitcher ecs = default(ExecutionContextSwitcher); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + ExecutionContext.EstablishCopyOnWriteScope(currentThread, ref ecs); + stateMachine.MoveNext(); + } + finally + { + ecs.Undo(currentThread); + } + } +#endif + + /// <summary>Associates the builder with the state machine it represents.</summary> + /// <param name="stateMachine">The heap-allocated state machine object.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception> + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + if (stateMachine == null) throw new ArgumentNullException("stateMachine"); + Contract.EndContractBlock(); + if (m_stateMachine != null) throw new InvalidOperationException(Environment.GetResourceString("AsyncMethodBuilder_InstanceNotInitialized")); + m_stateMachine = stateMachine; + } + + /// <summary> + /// Gets the Action to use with an awaiter's OnCompleted or UnsafeOnCompleted method. + /// On first invocation, the supplied state machine will be boxed. + /// </summary> + /// <typeparam name="TMethodBuilder">Specifies the type of the method builder used.</typeparam> + /// <typeparam name="TStateMachine">Specifies the type of the state machine used.</typeparam> + /// <param name="builder">The builder.</param> + /// <param name="stateMachine">The state machine.</param> + /// <returns>An Action to provide to the awaiter.</returns> + [SecuritySafeCritical] + internal Action GetCompletionAction(Task taskForTracing, ref MoveNextRunner runnerToInitialize) + { + Contract.Assert(m_defaultContextAction == null || m_stateMachine != null, + "Expected non-null m_stateMachine on non-null m_defaultContextAction"); + + // Alert a listening debugger that we can't make forward progress unless it slips threads. + // If we don't do this, and a method that uses "await foo;" is invoked through funceval, + // we could end up hooking up a callback to push forward the async method's state machine, + // the debugger would then abort the funceval after it takes too long, and then continuing + // execution could result in another callback being hooked up. At that point we have + // multiple callbacks registered to push the state machine, which could result in bad behavior. + Debugger.NotifyOfCrossThreadDependency(); + + // The builder needs to flow ExecutionContext, so capture it. + var capturedContext = ExecutionContext.FastCapture(); // ok to use FastCapture as we haven't made any permission demands/asserts + + // If the ExecutionContext is the default context, try to use a cached delegate, creating one if necessary. + Action action; + MoveNextRunner runner; + if (capturedContext != null && capturedContext.IsPreAllocatedDefault) + { + // Get the cached delegate, and if it's non-null, return it. + action = m_defaultContextAction; + if (action != null) + { + Contract.Assert(m_stateMachine != null, "If the delegate was set, the state machine should have been as well."); + return action; + } + + // There wasn't a cached delegate, so create one and cache it. + // The delegate won't be usable until we set the MoveNextRunner's target state machine. + runner = new MoveNextRunner(m_stateMachine); + + action = new Action(runner.RunWithDefaultContext); + if (taskForTracing != null) + { + action = OutputAsyncCausalityEvents(taskForTracing, action); + } + m_defaultContextAction = action; + } + // Otherwise, create an Action that flows this context. The context may be null. + // The delegate won't be usable until we set the MoveNextRunner's target state machine. + else + { + var runnerWithContext = new MoveNextRunnerWithContext(capturedContext, m_stateMachine); + runner = runnerWithContext; + action = new Action(runnerWithContext.RunWithCapturedContext); + + if (taskForTracing != null) + { + action = OutputAsyncCausalityEvents(taskForTracing, action); + } + + // NOTE: If capturedContext is null, we could create the Action to point directly + // to m_stateMachine.MoveNext. However, that follows a much more expensive + // delegate creation path. + } + + if (m_stateMachine == null) + runnerToInitialize = runner; + + return action; + } + + private Action OutputAsyncCausalityEvents(Task innerTask, Action continuation) + { + return CreateContinuationWrapper(continuation, () => + { + AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, innerTask.Id, CausalitySynchronousWork.Execution); + + // Invoke the original continuation + continuation.Invoke(); + + AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution); + }, innerTask); + } + + internal void PostBoxInitialization(IAsyncStateMachine stateMachine, MoveNextRunner runner, Task builtTask) + { + if (builtTask != null) + { + if (AsyncCausalityTracer.LoggingOn) + AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, builtTask.Id, "Async: " + stateMachine.GetType().Name, 0); + + if (System.Threading.Tasks.Task.s_asyncDebuggingEnabled) + System.Threading.Tasks.Task.AddToActiveTasks(builtTask); + } + + m_stateMachine = stateMachine; + m_stateMachine.SetStateMachine(m_stateMachine); + + Contract.Assert(runner.m_stateMachine == null, "The runner's state machine should not yet have been populated."); + Contract.Assert(m_stateMachine != null, "The builder's state machine field should have been initialized."); + + // Now that we have the state machine, store it into the runner that the action delegate points to. + // And return the action. + runner.m_stateMachine = m_stateMachine; // only after this line is the Action delegate usable + } + + /// <summary>Throws the exception on the ThreadPool.</summary> + /// <param name="exception">The exception to propagate.</param> + /// <param name="targetContext">The target context on which to propagate the exception. Null to use the ThreadPool.</param> + internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext) + { + // Capture the exception into an ExceptionDispatchInfo so that its + // stack trace and Watson bucket info will be preserved + var edi = ExceptionDispatchInfo.Capture(exception); + + // If the user supplied a SynchronizationContext... + if (targetContext != null) + { + try + { + // Post the throwing of the exception to that context, and return. + targetContext.Post(state => ((ExceptionDispatchInfo)state).Throw(), edi); + return; + } + catch (Exception postException) + { + // If something goes horribly wrong in the Post, we'll + // propagate both exceptions on the ThreadPool + edi = ExceptionDispatchInfo.Capture(new AggregateException(exception, postException)); + } + } + + // If we have the new error reporting APIs, report this error. Otherwise, Propagate the exception(s) on the ThreadPool +#if FEATURE_COMINTEROP + if (!WindowsRuntimeMarshal.ReportUnhandledError(edi.SourceException)) +#endif // FEATURE_COMINTEROP + { + ThreadPool.QueueUserWorkItem(state => ((ExceptionDispatchInfo)state).Throw(), edi); + } + } + + /// <summary>Provides the ability to invoke a state machine's MoveNext method under a supplied ExecutionContext.</summary> + internal sealed class MoveNextRunnerWithContext : MoveNextRunner + { + /// <summary>The context with which to run MoveNext.</summary> + private readonly ExecutionContext m_context; + + /// <summary>Initializes the runner.</summary> + /// <param name="context">The context with which to run MoveNext.</param> + [SecurityCritical] // Run needs to be SSC to map to Action delegate, so to prevent misuse, we only allow construction through SC + internal MoveNextRunnerWithContext(ExecutionContext context, IAsyncStateMachine stateMachine) : base(stateMachine) + { + m_context = context; + } + + /// <summary>Invokes MoveNext under the provided context.</summary> + [SecuritySafeCritical] + internal void RunWithCapturedContext() + { + Contract.Assert(m_stateMachine != null, "The state machine must have been set before calling Run."); + + if (m_context != null) + { + try + { + // Use the context and callback to invoke m_stateMachine.MoveNext. + ExecutionContext.Run(m_context, InvokeMoveNextCallback, m_stateMachine, preserveSyncCtx: true); + } + finally { m_context.Dispose(); } + } + else + { + m_stateMachine.MoveNext(); + } + } + } + + /// <summary>Provides the ability to invoke a state machine's MoveNext method.</summary> + internal class MoveNextRunner + { + /// <summary>The state machine whose MoveNext method should be invoked.</summary> + internal IAsyncStateMachine m_stateMachine; + + /// <summary>Initializes the runner.</summary> + [SecurityCritical] // Run needs to be SSC to map to Action delegate, so to prevent misuse, we only allow construction through SC + internal MoveNextRunner(IAsyncStateMachine stateMachine) + { + m_stateMachine = stateMachine; + } + + /// <summary>Invokes MoveNext under the default context.</summary> + [SecuritySafeCritical] + internal void RunWithDefaultContext() + { + Contract.Assert(m_stateMachine != null, "The state machine must have been set before calling Run."); + ExecutionContext.Run(ExecutionContext.PreAllocatedDefault, InvokeMoveNextCallback, m_stateMachine, preserveSyncCtx: true); + } + + /// <summary>Gets a delegate to the InvokeMoveNext method.</summary> + protected static ContextCallback InvokeMoveNextCallback + { + [SecuritySafeCritical] + get { return s_invokeMoveNext ?? (s_invokeMoveNext = InvokeMoveNext); } + } + + /// <summary>Cached delegate used with ExecutionContext.Run.</summary> + [SecurityCritical] + private static ContextCallback s_invokeMoveNext; // lazily-initialized due to SecurityCritical attribution + + /// <summary>Invokes the MoveNext method on the supplied IAsyncStateMachine.</summary> + /// <param name="stateMachine">The IAsyncStateMachine machine instance.</param> + [SecurityCritical] // necessary for ContextCallback in CoreCLR + private static void InvokeMoveNext(object stateMachine) + { + ((IAsyncStateMachine)stateMachine).MoveNext(); + } + } + + /// <summary> + /// Logically we pass just an Action (delegate) to a task for its action to 'ContinueWith' when it completes. + /// However debuggers and profilers need more information about what that action is. (In particular what + /// the action after that is and after that. To solve this problem we create a 'ContinuationWrapper + /// which when invoked just does the original action (the invoke action), but also remembers other information + /// (like the action after that (which is also a ContinuationWrapper and thus form a linked list). + // We also store that task if the action is associate with at task. + /// </summary> + private class ContinuationWrapper + { + internal readonly Action m_continuation; // This is continuation which will happen after m_invokeAction (and is probably a ContinuationWrapper) + private readonly Action m_invokeAction; // This wrapper is an action that wraps another action, this is that Action. + internal readonly Task m_innerTask; // If the continuation is logically going to invoke a task, this is that task (may be null) + + internal ContinuationWrapper(Action continuation, Action invokeAction, Task innerTask) + { + Contract.Requires(continuation != null, "Expected non-null continuation"); + + // If we don't have a task, see if our continuation is a wrapper and use that. + if (innerTask == null) + innerTask = TryGetContinuationTask(continuation); + + m_continuation = continuation; + m_innerTask = innerTask; + m_invokeAction = invokeAction; + } + + internal void Invoke() + { + m_invokeAction(); + } + } + + internal static Action CreateContinuationWrapper(Action continuation, Action invokeAction, Task innerTask = null) + { + return new ContinuationWrapper(continuation, invokeAction, innerTask).Invoke; + } + + internal static Action TryGetStateMachineForDebugger(Action action) + { + object target = action.Target; + var runner = target as AsyncMethodBuilderCore.MoveNextRunner; + if (runner != null) + { + return new Action(runner.m_stateMachine.MoveNext); + } + + var continuationWrapper = target as ContinuationWrapper; + if (continuationWrapper != null) + { + return TryGetStateMachineForDebugger(continuationWrapper.m_continuation); + } + + return action; + } + + ///<summary> + /// Given an action, see if it is a contiunation wrapper and has a Task associated with it. If so return it (null otherwise) + ///</summary> + internal static Task TryGetContinuationTask(Action action) + { + if (action != null) + { + var asWrapper = action.Target as ContinuationWrapper; + if (asWrapper != null) + return asWrapper.m_innerTask; + } + return null; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs new file mode 100644 index 0000000000..f1fc9ced82 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; + +namespace System.Runtime.CompilerServices +{ + [Serializable, AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class AsyncStateMachineAttribute : StateMachineAttribute + { + public AsyncStateMachineAttribute(Type stateMachineType) + : base(stateMachineType) + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CallerFilePathAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CallerFilePathAttribute.cs new file mode 100644 index 0000000000..330934cf95 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CallerFilePathAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class CallerFilePathAttribute : Attribute + { + public CallerFilePathAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs new file mode 100644 index 0000000000..9c87e8e25f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class CallerLineNumberAttribute : Attribute + { + public CallerLineNumberAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs new file mode 100644 index 0000000000..4fc70908fb --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class CallerMemberNameAttribute : Attribute + { + public CallerMemberNameAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CallingConvention.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CallingConvention.cs new file mode 100644 index 0000000000..f44251d480 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CallingConvention.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.CompilerServices +{ + // Types used in Custom Modifier to specify calling conventions. + [System.Runtime.InteropServices.ComVisible(true)] + public class CallConvCdecl + { + } + + [System.Runtime.InteropServices.ComVisible(true)] + public class CallConvStdcall + { + } + + [System.Runtime.InteropServices.ComVisible(true)] + public class CallConvThiscall + { + } + + [System.Runtime.InteropServices.ComVisible(true)] + public class CallConvFastcall + { + } + +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CompilationRelaxations.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CompilationRelaxations.cs new file mode 100644 index 0000000000..5e4f19410b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CompilationRelaxations.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +namespace System.Runtime.CompilerServices +{ + + using System; + + /// IMPORTANT: Keep this in sync with corhdr.h +[Serializable] +[Flags] +[System.Runtime.InteropServices.ComVisible(true)] + public enum CompilationRelaxations : int + { + NoStringInterning = 0x0008, // Start in 0x0008, we had other non public flags in this enum before, + // so we'll start here just in case somebody used them. This flag is only + // valid when set for Assemblies. + }; + +[Serializable] +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Method)] +[System.Runtime.InteropServices.ComVisible(true)] + public class CompilationRelaxationsAttribute : Attribute + { + private int m_relaxations; // The relaxations. + + public CompilationRelaxationsAttribute ( + int relaxations) + { + m_relaxations = relaxations; + } + + public CompilationRelaxationsAttribute ( + CompilationRelaxations relaxations) + { + m_relaxations = (int) relaxations; + } + + public int CompilationRelaxations + { + get + { + return m_relaxations; + } + } + } + +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs new file mode 100644 index 0000000000..1778506c7c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.CompilerServices { + +[Serializable] +[AttributeUsage(AttributeTargets.All, Inherited = true)] + public sealed class CompilerGeneratedAttribute : Attribute + { + public CompilerGeneratedAttribute () {} + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs new file mode 100644 index 0000000000..65755f6baa --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Attribute used to communicate to the VS7 debugger +** that a class should be treated as if it has +** global scope. +** +** +===========================================================*/ + + +namespace System.Runtime.CompilerServices +{ + [Serializable] + [AttributeUsage(AttributeTargets.Class)] + [System.Runtime.InteropServices.ComVisible(true)] + public class CompilerGlobalScopeAttribute : Attribute + { + public CompilerGlobalScopeAttribute () {} + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CompilerMarshalOverride.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CompilerMarshalOverride.cs new file mode 100644 index 0000000000..a7b4aca480 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CompilerMarshalOverride.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // The CLR data marshaler has some behaviors that are incompatible with + // C++. Specifically, C++ treats boolean variables as byte size, whereas + // the marshaller treats them as 4-byte size. Similarly, C++ treats + // wchar_t variables as 4-byte size, whereas the marshaller treats them + // as single byte size under certain conditions. In order to work around + // such issues, the C++ compiler will emit a type that the marshaller will + // marshal using the correct sizes. In addition, the compiler will place + // this modopt onto the variables to indicate that the specified type is + // not the true type. Any compiler that needed to deal with similar + // marshalling incompatibilities could use this attribute as well. + // + // Indicates that the modified instance differs from its true type for + // correct marshalling. + public static class CompilerMarshalOverride + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/mscorlib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs new file mode 100644 index 0000000000..21d677241d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -0,0 +1,795 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** Description: Compiler support for runtime-generated "object fields." +** +** Lets DLR and other language compilers expose the ability to +** attach arbitrary "properties" to instanced managed objects at runtime. +** +** We expose this support as a dictionary whose keys are the +** instanced objects and the values are the "properties." +** +** Unlike a regular dictionary, ConditionalWeakTables will not +** keep keys alive. +** +** +** Lifetimes of keys and values: +** +** Inserting a key and value into the dictonary will not +** prevent the key from dying, even if the key is strongly reachable +** from the value. +** +** Prior to ConditionalWeakTable, the CLR did not expose +** the functionality needed to implement this guarantee. +** +** Once the key dies, the dictionary automatically removes +** the key/value entry. +** +** +** Relationship between ConditionalWeakTable and Dictionary: +** +** ConditionalWeakTable mirrors the form and functionality +** of the IDictionary interface for the sake of api consistency. +** +** Unlike Dictionary, ConditionalWeakTable is fully thread-safe +** and requires no additional locking to be done by callers. +** +** ConditionalWeakTable defines equality as Object.ReferenceEquals(). +** ConditionalWeakTable does not invoke GetHashCode() overrides. +** +** It is not intended to be a general purpose collection +** and it does not formally implement IDictionary or +** expose the full public surface area. +** +** +** +** Thread safety guarantees: +** +** ConditionalWeakTable is fully thread-safe and requires no +** additional locking to be done by callers. +** +** +** OOM guarantees: +** +** Will not corrupt unmanaged handle table on OOM. No guarantees +** about managed weak table consistency. Native handles reclamation +** may be delayed until appdomain shutdown. +===========================================================*/ + +namespace System.Runtime.CompilerServices +{ + using System; + using System.Collections.Generic; + using System.Runtime.Versioning; + using System.Runtime.InteropServices; + + + #region ConditionalWeakTable + [System.Runtime.InteropServices.ComVisible(false)] + public sealed class ConditionalWeakTable<TKey, TValue> + where TKey : class + where TValue : class + { + + #region Constructors + [System.Security.SecuritySafeCritical] + public ConditionalWeakTable() + { + _buckets = Array.Empty<int>(); + _entries = Array.Empty<Entry>(); + _freeList = -1; + _lock = new Object(); + + Resize(); // Resize at once (so won't need "if initialized" checks all over) + } + #endregion + + #region Public Members + //-------------------------------------------------------------------------------------------- + // key: key of the value to find. Cannot be null. + // value: if the key is found, contains the value associated with the key upon method return. + // if the key is not found, contains default(TValue). + // + // Method returns "true" if key was found, "false" otherwise. + // + // Note: The key may get garbaged collected during the TryGetValue operation. If so, TryGetValue + // may at its discretion, return "false" and set "value" to the default (as if the key was not present.) + //-------------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + public bool TryGetValue(TKey key, out TValue value) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + lock(_lock) + { + VerifyIntegrity(); + return TryGetValueWorker(key, out value); + } + } + + //-------------------------------------------------------------------------------------------- + // key: key to add. May not be null. + // value: value to associate with key. + // + // If the key is already entered into the dictionary, this method throws an exception. + // + // Note: The key may get garbage collected during the Add() operation. If so, Add() + // has the right to consider any prior entries successfully removed and add a new entry without + // throwing an exception. + //-------------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + public void Add(TKey key, TValue value) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + lock(_lock) + { + VerifyIntegrity(); + _invalid = true; + + int entryIndex = FindEntry(key); + if (entryIndex != -1) + { + _invalid = false; + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate); + } + + CreateEntry(key, value); + _invalid = false; + } + + } + + //-------------------------------------------------------------------------------------------- + // key: key to remove. May not be null. + // + // Returns true if the key is found and removed. Returns false if the key was not in the dictionary. + // + // Note: The key may get garbage collected during the Remove() operation. If so, + // Remove() will not fail or throw, however, the return value can be either true or false + // depending on the race condition. + //-------------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + public bool Remove(TKey key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + lock(_lock) + { + VerifyIntegrity(); + _invalid = true; + + int hashCode = RuntimeHelpers.GetHashCode(key) & Int32.MaxValue; + int bucket = hashCode % _buckets.Length; + int last = -1; + for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) + { + if (_entries[entriesIndex].hashCode == hashCode && _entries[entriesIndex].depHnd.GetPrimary() == key) + { + if (last == -1) + { + _buckets[bucket] = _entries[entriesIndex].next; + } + else + { + _entries[last].next = _entries[entriesIndex].next; + } + + _entries[entriesIndex].depHnd.Free(); + _entries[entriesIndex].next = _freeList; + + _freeList = entriesIndex; + + _invalid = false; + return true; + + } + last = entriesIndex; + } + _invalid = false; + return false; + } + } + + + //-------------------------------------------------------------------------------------------- + // key: key of the value to find. Cannot be null. + // createValueCallback: callback that creates value for key. Cannot be null. + // + // Atomically tests if key exists in table. If so, returns corresponding value. If not, + // invokes createValueCallback() passing it the key. The returned value is bound to the key in the table + // and returned as the result of GetValue(). + // + // If multiple threads try to initialize the same key, the table may invoke createValueCallback + // multiple times with the same key. Exactly one of these calls will succeed and the returned + // value of that call will be the one added to the table and returned by all the racing GetValue() calls. + // + // This rule permits the table to invoke createValueCallback outside the internal table lock + // to prevent deadlocks. + //-------------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + public TValue GetValue(TKey key, CreateValueCallback createValueCallback) + { + // Our call to TryGetValue() validates key so no need for us to. + // + // if (key == null) + // { + // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + // } + + if (createValueCallback == null) + { + throw new ArgumentNullException("createValueCallback"); + } + + TValue existingValue; + if (TryGetValue(key, out existingValue)) + { + return existingValue; + } + + // If we got here, the key is not currently in table. Invoke the callback (outside the lock) + // to generate the new value for the key. + TValue newValue = createValueCallback(key); + + lock(_lock) + { + VerifyIntegrity(); + _invalid = true; + + // Now that we've retaken the lock, must recheck in case there was a race condition to add the key. + if (TryGetValueWorker(key, out existingValue)) + { + _invalid = false; + return existingValue; + } + else + { + CreateEntry(key, newValue); + _invalid = false; + return newValue; + } + } + } + + //-------------------------------------------------------------------------------------------- + // key: key of the value to find. Cannot be null. + // + // Helper method to call GetValue without passing a creation delegate. Uses Activator.CreateInstance + // to create new instances as needed. If TValue does not have a default constructor, this will + // throw. + //-------------------------------------------------------------------------------------------- + public TValue GetOrCreateValue(TKey key) + { + return GetValue(key, k => Activator.CreateInstance<TValue>()); + } + + public delegate TValue CreateValueCallback(TKey key); + + #endregion + + #region internal members + + //-------------------------------------------------------------------------------------------- + // Find a key that equals (value equality) with the given key - don't use in perf critical path + // Note that it calls out to Object.Equals which may calls the override version of Equals + // and that may take locks and leads to deadlock + // Currently it is only used by WinRT event code and you should only use this function + // if you know for sure that either you won't run into dead locks or you need to live with the + // possiblity + //-------------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + [FriendAccessAllowed] + internal TKey FindEquivalentKeyUnsafe(TKey key, out TValue value) + { + lock (_lock) + { + for (int bucket = 0; bucket < _buckets.Length; ++bucket) + { + for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) + { + object thisKey, thisValue; + _entries[entriesIndex].depHnd.GetPrimaryAndSecondary(out thisKey, out thisValue); + if (Object.Equals(thisKey, key)) + { + value = (TValue) thisValue; + return (TKey) thisKey; + } + } + } + } + + value = default(TValue); + return null; + } + + //-------------------------------------------------------------------------------------------- + // Returns a collection of keys - don't use in perf critical path + //-------------------------------------------------------------------------------------------- + internal ICollection<TKey> Keys + { + [System.Security.SecuritySafeCritical] + get + { + List<TKey> list = new List<TKey>(); + lock (_lock) + { + for (int bucket = 0; bucket < _buckets.Length; ++bucket) + { + for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) + { + TKey thisKey = (TKey) _entries[entriesIndex].depHnd.GetPrimary(); + if (thisKey != null) + { + list.Add(thisKey); + } + } + } + } + + return list; + } + } + + //-------------------------------------------------------------------------------------------- + // Returns a collection of values - don't use in perf critical path + //-------------------------------------------------------------------------------------------- + internal ICollection<TValue> Values + { + [System.Security.SecuritySafeCritical] + get + { + List<TValue> list = new List<TValue>(); + lock (_lock) + { + for (int bucket = 0; bucket < _buckets.Length; ++bucket) + { + for (int entriesIndex = _buckets[bucket]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) + { + Object primary = null; + Object secondary = null; + + _entries[entriesIndex].depHnd.GetPrimaryAndSecondary(out primary, out secondary); + + // Now that we've secured a strong reference to the secondary, must check the primary again + // to ensure it didn't expire (otherwise, we open a race condition where TryGetValue misreports an + // expired key as a live key with a null value.) + if (primary != null) + { + list.Add((TValue)secondary); + } + } + } + } + + return list; + } + } + + //-------------------------------------------------------------------------------------------- + // Clear all the key/value pairs + //-------------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + internal void Clear() + { + lock (_lock) + { + // Clear the buckets + for (int bucketIndex = 0; bucketIndex < _buckets.Length; bucketIndex++) + { + _buckets[bucketIndex] = -1; + } + + // Clear the entries and link them backwards together as part of free list + int entriesIndex; + for (entriesIndex = 0; entriesIndex < _entries.Length; entriesIndex++) + { + if (_entries[entriesIndex].depHnd.IsAllocated) + { + _entries[entriesIndex].depHnd.Free(); + } + + // Link back wards as free list + _entries[entriesIndex].next = entriesIndex - 1; + } + + _freeList = entriesIndex - 1; + } + } + + #endregion + + #region Private Members + [System.Security.SecurityCritical] + //---------------------------------------------------------------------------------------- + // Worker for finding a key/value pair + // + // Preconditions: + // Must hold _lock. + // Key already validated as non-null + //---------------------------------------------------------------------------------------- + private bool TryGetValueWorker(TKey key, out TValue value) + { + int entryIndex = FindEntry(key); + if (entryIndex != -1) + { + Object primary = null; + Object secondary = null; + _entries[entryIndex].depHnd.GetPrimaryAndSecondary(out primary, out secondary); + // Now that we've secured a strong reference to the secondary, must check the primary again + // to ensure it didn't expire (otherwise, we open a race condition where TryGetValue misreports an + // expired key as a live key with a null value.) + if (primary != null) + { + value = (TValue)secondary; + return true; + } + } + + value = default(TValue); + return false; + } + + //---------------------------------------------------------------------------------------- + // Worker for adding a new key/value pair. + // + // Preconditions: + // Must hold _lock. + // Key already validated as non-null and not already in table. + //---------------------------------------------------------------------------------------- + [System.Security.SecurityCritical] + private void CreateEntry(TKey key, TValue value) + { + if (_freeList == -1) + { + Resize(); + } + + int hashCode = RuntimeHelpers.GetHashCode(key) & Int32.MaxValue; + int bucket = hashCode % _buckets.Length; + + int newEntry = _freeList; + _freeList = _entries[newEntry].next; + + _entries[newEntry].hashCode = hashCode; + _entries[newEntry].depHnd = new DependentHandle(key, value); + _entries[newEntry].next = _buckets[bucket]; + + _buckets[bucket] = newEntry; + + } + + //---------------------------------------------------------------------------------------- + // This does two things: resize and scrub expired keys off bucket lists. + // + // Precondition: + // Must hold _lock. + // + // Postcondition: + // _freeList is non-empty on exit. + //---------------------------------------------------------------------------------------- + [System.Security.SecurityCritical] + private void Resize() + { + // Start by assuming we won't resize. + int newSize = _buckets.Length; + + // If any expired keys exist, we won't resize. + bool hasExpiredEntries = false; + int entriesIndex; + for (entriesIndex = 0; entriesIndex < _entries.Length; entriesIndex++) + { + if ( _entries[entriesIndex].depHnd.IsAllocated && _entries[entriesIndex].depHnd.GetPrimary() == null) + { + hasExpiredEntries = true; + break; + } + } + + if (!hasExpiredEntries) + { + newSize = System.Collections.HashHelpers.GetPrime(_buckets.Length == 0 ? _initialCapacity + 1 : _buckets.Length * 2); + } + + + // Reallocate both buckets and entries and rebuild the bucket and freelists from scratch. + // This serves both to scrub entries with expired keys and to put the new entries in the proper bucket. + int newFreeList = -1; + int[] newBuckets = new int[newSize]; + for (int bucketIndex = 0; bucketIndex < newSize; bucketIndex++) + { + newBuckets[bucketIndex] = -1; + } + Entry[] newEntries = new Entry[newSize]; + + // Migrate existing entries to the new table. + for (entriesIndex = 0; entriesIndex < _entries.Length; entriesIndex++) + { + DependentHandle depHnd = _entries[entriesIndex].depHnd; + if (depHnd.IsAllocated && depHnd.GetPrimary() != null) + { + // Entry is used and has not expired. Link it into the appropriate bucket list. + int bucket = _entries[entriesIndex].hashCode % newSize; + newEntries[entriesIndex].depHnd = depHnd; + newEntries[entriesIndex].hashCode = _entries[entriesIndex].hashCode; + newEntries[entriesIndex].next = newBuckets[bucket]; + newBuckets[bucket] = entriesIndex; + } + else + { + // Entry has either expired or was on the freelist to begin with. Either way + // insert it on the new freelist. + _entries[entriesIndex].depHnd.Free(); + newEntries[entriesIndex].depHnd = new DependentHandle(); + newEntries[entriesIndex].next = newFreeList; + newFreeList = entriesIndex; + } + } + + // Add remaining entries to freelist. + while (entriesIndex != newEntries.Length) + { + newEntries[entriesIndex].depHnd = new DependentHandle(); + newEntries[entriesIndex].next = newFreeList; + newFreeList = entriesIndex; + entriesIndex++; + } + + _buckets = newBuckets; + _entries = newEntries; + _freeList = newFreeList; + } + + //---------------------------------------------------------------------------------------- + // Returns -1 if not found (if key expires during FindEntry, this can be treated as "not found.") + // + // Preconditions: + // Must hold _lock. + // Key already validated as non-null. + //---------------------------------------------------------------------------------------- + [System.Security.SecurityCritical] + private int FindEntry(TKey key) + { + int hashCode = RuntimeHelpers.GetHashCode(key) & Int32.MaxValue; + for (int entriesIndex = _buckets[hashCode % _buckets.Length]; entriesIndex != -1; entriesIndex = _entries[entriesIndex].next) + { + if (_entries[entriesIndex].hashCode == hashCode && _entries[entriesIndex].depHnd.GetPrimary() == key) + { + return entriesIndex; + } + } + return -1; + } + + //---------------------------------------------------------------------------------------- + // Precondition: + // Must hold _lock. + //---------------------------------------------------------------------------------------- + private void VerifyIntegrity() + { + if (_invalid) + { + throw new InvalidOperationException(Environment.GetResourceString("CollectionCorrupted")); + } + } + + //---------------------------------------------------------------------------------------- + // Finalizer. + //---------------------------------------------------------------------------------------- + [System.Security.SecuritySafeCritical] + ~ConditionalWeakTable() + { + + // We're just freeing per-appdomain unmanaged handles here. If we're already shutting down the AD, + // don't bother. + // + // (Despite its name, Environment.HasShutdownStart also returns true if the current AD is finalizing.) + if (Environment.HasShutdownStarted) + { + return; + } + + if (_lock != null) + { + lock(_lock) + { + if (_invalid) + { + return; + } + Entry[] entries = _entries; + + // Make sure anyone sneaking into the table post-resurrection + // gets booted before they can damage the native handle table. + _invalid = true; + _entries = null; + _buckets = null; + + for (int entriesIndex = 0; entriesIndex < entries.Length; entriesIndex++) + { + entries[entriesIndex].depHnd.Free(); + } + } + } + } + #endregion + + #region Private Data Members + //-------------------------------------------------------------------------------------------- + // Entry can be in one of three states: + // + // - Linked into the freeList (_freeList points to first entry) + // depHnd.IsAllocated == false + // hashCode == <dontcare> + // next links to next Entry on freelist) + // + // - Used with live key (linked into a bucket list where _buckets[hashCode % _buckets.Length] points to first entry) + // depHnd.IsAllocated == true, depHnd.GetPrimary() != null + // hashCode == RuntimeHelpers.GetHashCode(depHnd.GetPrimary()) & Int32.MaxValue + // next links to next Entry in bucket. + // + // - Used with dead key (linked into a bucket list where _buckets[hashCode % _buckets.Length] points to first entry) + // depHnd.IsAllocated == true, depHnd.GetPrimary() == null + // hashCode == <notcare> + // next links to next Entry in bucket. + // + // The only difference between "used with live key" and "used with dead key" is that + // depHnd.GetPrimary() returns null. The transition from "used with live key" to "used with dead key" + // happens asynchronously as a result of normal garbage collection. The dictionary itself + // receives no notification when this happens. + // + // When the dictionary grows the _entries table, it scours it for expired keys and puts those + // entries back on the freelist. + //-------------------------------------------------------------------------------------------- + private struct Entry + { + public DependentHandle depHnd; // Holds key and value using a weak reference for the key and a strong reference + // for the value that is traversed only if the key is reachable without going through the value. + public int hashCode; // Cached copy of key's hashcode + public int next; // Index of next entry, -1 if last + } + + private int[] _buckets; // _buckets[hashcode & _buckets.Length] contains index of first entry in bucket (-1 if empty) + private Entry[] _entries; + private int _freeList; // -1 = empty, else index of first unused Entry + private const int _initialCapacity = 5; + private readonly Object _lock; // this could be a ReaderWriterLock but CoreCLR does not support RWLocks. + private bool _invalid; // flag detects if OOM or other background exception threw us out of the lock. + #endregion + } + #endregion + + + + + #region DependentHandle + //========================================================================================= + // This struct collects all operations on native DependentHandles. The DependentHandle + // merely wraps an IntPtr so this struct serves mainly as a "managed typedef." + // + // DependentHandles exist in one of two states: + // + // IsAllocated == false + // No actual handle is allocated underneath. Illegal to call GetPrimary + // or GetPrimaryAndSecondary(). Ok to call Free(). + // + // Initializing a DependentHandle using the nullary ctor creates a DependentHandle + // that's in the !IsAllocated state. + // (! Right now, we get this guarantee for free because (IntPtr)0 == NULL unmanaged handle. + // ! If that assertion ever becomes false, we'll have to add an _isAllocated field + // ! to compensate.) + // + // + // IsAllocated == true + // There's a handle allocated underneath. You must call Free() on this eventually + // or you cause a native handle table leak. + // + // This struct intentionally does no self-synchronization. It's up to the caller to + // to use DependentHandles in a thread-safe way. + //========================================================================================= + [ComVisible(false)] + struct DependentHandle + { + #region Constructors + #if FEATURE_CORECLR + [System.Security.SecuritySafeCritical] // auto-generated + #else + [System.Security.SecurityCritical] + #endif + public DependentHandle(Object primary, Object secondary) + { + IntPtr handle = (IntPtr)0; + nInitialize(primary, secondary, out handle); + // no need to check for null result: nInitialize expected to throw OOM. + _handle = handle; + } + #endregion + + #region Public Members + public bool IsAllocated + { + get + { + return _handle != (IntPtr)0; + } + } + + // Getting the secondary object is more expensive than getting the first so + // we provide a separate primary-only accessor for those times we only want the + // primary. + #if FEATURE_CORECLR + [System.Security.SecuritySafeCritical] // auto-generated + #else + [System.Security.SecurityCritical] + #endif + public Object GetPrimary() + { + Object primary; + nGetPrimary(_handle, out primary); + return primary; + } + + #if FEATURE_CORECLR + [System.Security.SecuritySafeCritical] // auto-generated + #else + [System.Security.SecurityCritical] + #endif + public void GetPrimaryAndSecondary(out Object primary, out Object secondary) + { + nGetPrimaryAndSecondary(_handle, out primary, out secondary); + } + + //---------------------------------------------------------------------- + // Forces dependentHandle back to non-allocated state (if not already there) + // and frees the handle if needed. + //---------------------------------------------------------------------- + [System.Security.SecurityCritical] + public void Free() + { + if (_handle != (IntPtr)0) + { + IntPtr handle = _handle; + _handle = (IntPtr)0; + nFree(handle); + } + } + #endregion + + #region Private Members + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void nInitialize(Object primary, Object secondary, out IntPtr dependentHandle); + + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void nGetPrimary(IntPtr dependentHandle, out Object primary); + + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void nGetPrimaryAndSecondary(IntPtr dependentHandle, out Object primary, out Object secondary); + + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void nFree(IntPtr dependentHandle); + #endregion + + #region Private Data Member + private IntPtr _handle; + #endregion + + } // struct DependentHandle + #endregion +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs new file mode 100644 index 0000000000..c912095bda --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Collections.Generic; + +namespace System.Runtime.CompilerServices +{ + [Serializable] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited=false)] + [System.Runtime.InteropServices.ComVisible(true)] + public abstract class CustomConstantAttribute : Attribute + { + public abstract Object Value { get; } + + internal static object GetRawConstant(CustomAttributeData attr) + { + foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments) + { + if (namedArgument.MemberInfo.Name.Equals("Value")) + return namedArgument.TypedValue.Value; + } + + // Return DBNull to indicate that no default value is available. + // Not to be confused with a null return which indicates a null default value. + return DBNull.Value; + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs new file mode 100644 index 0000000000..4362aa84a1 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Diagnostics.Contracts; + +namespace System.Runtime.CompilerServices +{ + [Serializable] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited=false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class DateTimeConstantAttribute : CustomConstantAttribute + { + public DateTimeConstantAttribute(long ticks) + { + date = new System.DateTime(ticks); + } + + public override Object Value + { + get + { + return date; + } + } + + internal static DateTime GetRawDateTimeConstant(CustomAttributeData attr) + { + Contract.Requires(attr.Constructor.DeclaringType == typeof(DateTimeConstantAttribute)); + Contract.Requires(attr.ConstructorArguments.Count == 1); + + foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments) + { + if (namedArgument.MemberInfo.Name.Equals("Value")) + { + return new DateTime((long)namedArgument.TypedValue.Value); + } + } + + // Look at the ctor argument if the "Value" property was not explicitly defined. + return new DateTime((long)attr.ConstructorArguments[0].Value); + } + + private System.DateTime date; + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs new file mode 100644 index 0000000000..39a4c86b72 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +// Note: If you add a new ctor overloads you need to update ParameterInfo.RawDefaultValue + +using System.Reflection; +using System.Diagnostics.Contracts; +using System.Collections.Generic; + +namespace System.Runtime.CompilerServices +{ + [Serializable] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited=false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class DecimalConstantAttribute : Attribute + { + [CLSCompliant(false)] + public DecimalConstantAttribute( + byte scale, + byte sign, + uint hi, + uint mid, + uint low + ) + { + dec = new System.Decimal((int) low, (int)mid, (int)hi, (sign != 0), scale); + } + + public DecimalConstantAttribute( + byte scale, + byte sign, + int hi, + int mid, + int low + ) + { + dec = new System.Decimal(low, mid, hi, (sign != 0), scale); + } + + + public System.Decimal Value + { + get + { + return dec; + } + } + + internal static Decimal GetRawDecimalConstant(CustomAttributeData attr) + { + Contract.Requires(attr.Constructor.DeclaringType == typeof(DecimalConstantAttribute)); + + foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments) + { + if (namedArgument.MemberInfo.Name.Equals("Value")) + { + // This is not possible because Decimal cannot be represented directly in the metadata. + Contract.Assert(false, "Decimal cannot be represented directly in the metadata."); + return (Decimal)namedArgument.TypedValue.Value; + } + } + + ParameterInfo[] parameters = attr.Constructor.GetParameters(); + Contract.Assert(parameters.Length == 5); + + System.Collections.Generic.IList<CustomAttributeTypedArgument> args = attr.ConstructorArguments; + Contract.Assert(args.Count == 5); + + if (parameters[2].ParameterType == typeof(uint)) + { + // DecimalConstantAttribute(byte scale, byte sign, uint hi, uint mid, uint low) + int low = (int)(UInt32)args[4].Value; + int mid = (int)(UInt32)args[3].Value; + int hi = (int)(UInt32)args[2].Value; + byte sign = (byte)args[1].Value; + byte scale = (byte)args[0].Value; + + return new System.Decimal(low, mid, hi, (sign != 0), scale); + } + else + { + // DecimalConstantAttribute(byte scale, byte sign, int hi, int mid, int low) + int low = (int)args[4].Value; + int mid = (int)args[3].Value; + int hi = (int)args[2].Value; + byte sign = (byte)args[1].Value; + byte scale = (byte)args[0].Value; + + return new System.Decimal(low, mid, hi, (sign != 0), scale); + } + } + + private System.Decimal dec; + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DecoratedNameAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DecoratedNameAttribute.cs new file mode 100644 index 0000000000..75558d4e7e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/DecoratedNameAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All), + ComVisible(false)] + internal sealed class DecoratedNameAttribute : Attribute + { + public DecoratedNameAttribute(string decoratedName) + {} + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs new file mode 100644 index 0000000000..46dae10fdd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace System.Runtime.CompilerServices +{ + using System; + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public sealed class DisablePrivateReflectionAttribute : Attribute + { + public DisablePrivateReflectionAttribute() {} + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DiscardableAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DiscardableAttribute.cs new file mode 100644 index 0000000000..303151f576 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/DiscardableAttribute.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.CompilerServices { + + using System; + + // Custom attribute to indicating a TypeDef is a discardable attribute +[System.Runtime.InteropServices.ComVisible(true)] + public class DiscardableAttribute : Attribute + { + public DiscardableAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/ExtensionAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/ExtensionAttribute.cs new file mode 100644 index 0000000000..6ec8fa04f5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/ExtensionAttribute.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; + +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// Indicates that a method is an extension method, or that a class or assembly contains extension methods. + /// </summary> + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] + public sealed class ExtensionAttribute : Attribute { } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs new file mode 100644 index 0000000000..679e304ad1 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + using System; + +[Serializable] +[AttributeUsage(AttributeTargets.Field)] + sealed public class FixedAddressValueTypeAttribute : Attribute + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs new file mode 100644 index 0000000000..a7d01b12c4 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** Purpose: Used by a compiler for generating value types +** in-place within other value types containing a certain +** number of elements of the given (primitive) type. Somewhat +** similar to P/Invoke's ByValTStr attribute. +** Used by C# with this syntax: "fixed int buffer[10];" +** +===========================================================*/ +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field, Inherited=false)] + public sealed class FixedBufferAttribute : Attribute + { + private Type elementType; + private int length; + + public FixedBufferAttribute(Type elementType, int length) + { + this.elementType = elementType; + this.length = length; + } + + public Type ElementType { + get { + return elementType; + } + } + + public int Length { + get { + return length; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/FormattableStringFactory.cs b/src/mscorlib/src/System/Runtime/CompilerServices/FormattableStringFactory.cs new file mode 100644 index 0000000000..aee3bc2230 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/FormattableStringFactory.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Class: FormattableStringFactory +** +** +** Purpose: implementation of the FormattableStringFactory +** class. +** +===========================================================*/ +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// A factory type used by compilers to create instances of the type <see cref="FormattableString"/>. + /// </summary> + public static class FormattableStringFactory + { + /// <summary> + /// Create a <see cref="FormattableString"/> from a composite format string and object + /// array containing zero or more objects to format. + /// </summary> + public static FormattableString Create(string format, params object[] arguments) + { + if (format == null) + { + throw new ArgumentNullException("format"); + } + + if (arguments == null) + { + throw new ArgumentNullException("arguments"); + } + + return new ConcreteFormattableString(format, arguments); + } + + private sealed class ConcreteFormattableString : FormattableString + { + private readonly string _format; + private readonly object[] _arguments; + + internal ConcreteFormattableString(string format, object[] arguments) + { + _format = format; + _arguments = arguments; + } + + public override string Format { get { return _format; } } + public override object[] GetArguments() { return _arguments; } + public override int ArgumentCount { get { return _arguments.Length; } } + public override object GetArgument(int index) { return _arguments[index]; } + public override string ToString(IFormatProvider formatProvider) { return string.Format(formatProvider, _format, _arguments); } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/HasCopySemanticsAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/HasCopySemanticsAttribute.cs new file mode 100644 index 0000000000..944a2868f2 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/HasCopySemanticsAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ +[Serializable] +[AttributeUsage(AttributeTargets.Struct)] + public sealed class HasCopySemanticsAttribute : Attribute + { + public HasCopySemanticsAttribute() + {} + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IAsyncStateMachine.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IAsyncStateMachine.cs new file mode 100644 index 0000000000..7fb7ea5395 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IAsyncStateMachine.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// Represents state machines generated for asynchronous methods. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// Represents state machines generated for asynchronous methods. + /// This type is intended for compiler use only. + /// </summary> + public interface IAsyncStateMachine + { + /// <summary>Moves the state machine to its next state.</summary> + void MoveNext(); + /// <summary>Configures the state machine with a heap-allocated replica.</summary> + /// <param name="stateMachine">The heap-allocated replica.</param> + void SetStateMachine(IAsyncStateMachine stateMachine); + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs b/src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs new file mode 100644 index 0000000000..7ba9434575 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/ICastable.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + + +// +// Support for dynamic interface casting. Specifically implementing this interface on a type will allow the +// type to support interfaces (for the purposes of casting and interface dispatch) that do not appear in its +// interface map. +// + +using System; + +namespace System.Runtime.CompilerServices +{ + public interface ICastable + { + // This is called if casting this object to the given interface type would otherwise fail. Casting + // here means the IL isinst and castclass instructions in the case where they are given an interface + // type as the target type. + // + // A return value of true indicates the cast is valid. + // + // If false is returned when this is called as part of a castclass then the usual InvalidCastException + // will be thrown unless an alternate exception is assigned to the castError output parameter. This + // parameter is ignored on successful casts or during the evaluation of an isinst (which returns null + // rather than throwing on error). + // + // No exception should be thrown from this method (it will cause unpredictable effects, including the + // possibility of an immediate failfast). + // + // The results of this call are not cached, so it is advisable to provide a performant implementation. + // + // The results of this call should be invariant for the same class, interface type pair. That is + // because this is the only guard placed before an interface invocation at runtime. If a type decides + // it no longer wants to implement a given interface it has no way to synchronize with callers that + // have already cached this relationship and can invoke directly via the interface pointer. + bool IsInstanceOfInterface(RuntimeTypeHandle interfaceType, out Exception castError); + + // This is called as part of the interface dispatch mechanism when the dispatcher logic cannot find + // the given interface type in the interface map of this object. + // + // It allows the implementor to return an alternate class type which does implement the interface. The + // interface lookup shall be performed again on this type (failure to find the interface this time + // resulting in a fail fast) and the corresponding implemented method on that class called instead. + // + // Naturally, since the call is dispatched to a method on a class which does not match the type of the + // this pointer, extreme care must be taken in the implementation of the interface methods of this + // surrogate type. + // + // No exception should be thrown from this method (it will cause unpredictable effects, including the + // possibility of an immediate failfast). + // + // There is no error path defined here. By construction all interface dispatches will already have + // been verified via the castclass/isinst mechanism (and thus a call to IsInstanceOfInterface above) + // so this method is expected to succeed in all cases. The contract for interface dispatch does not + // include any errors from the infrastructure, of which this is a part. + // + // The results of this lookup are cached so computation of the result is not as perf-sensitive as + // IsInstanceOfInterface. + RuntimeTypeHandle GetImplType(RuntimeTypeHandle interfaceType); + } + + /// <summary> + /// Helpers that allows VM to call into ICastable methods without having to deal with RuntimeTypeHandle. + /// RuntimeTypeHandle is a struct and is always passed in stack in x86, which our VM call helpers don't + /// particularly like. + /// </summary> + class ICastableHelpers + { + internal static bool IsInstanceOfInterface(ICastable castable, RuntimeType type, out Exception castError) + { + return castable.IsInstanceOfInterface(new RuntimeTypeHandle(type), out castError); + } + + internal static RuntimeType GetImplType(ICastable castable, RuntimeType interfaceType) + { + return castable.GetImplType(new RuntimeTypeHandle(interfaceType)).GetRuntimeType(); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IDispatchConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IDispatchConstantAttribute.cs new file mode 100644 index 0000000000..d6dfcbbbb9 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IDispatchConstantAttribute.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ +[Serializable] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class IDispatchConstantAttribute : CustomConstantAttribute + { + public IDispatchConstantAttribute() + { + } + + public override Object Value + { + get + { + return new DispatchWrapper(null); + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/INotifyCompletion.cs b/src/mscorlib/src/System/Runtime/CompilerServices/INotifyCompletion.cs new file mode 100644 index 0000000000..872a79b72b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/INotifyCompletion.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= +// +// +// +// Interfaces used to represent instances that notify listeners of their completion via continuations. +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +using System; +using System.Security; + +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// Represents an operation that will schedule continuations when the operation completes. + /// </summary> + public interface INotifyCompletion + { + /// <summary>Schedules the continuation action to be invoked when the instance completes.</summary> + /// <param name="continuation">The action to invoke when the operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + void OnCompleted(Action continuation); + } + + /// <summary> + /// Represents an awaiter used to schedule continuations when an await operation completes. + /// </summary> + public interface ICriticalNotifyCompletion : INotifyCompletion + { + /// <summary>Schedules the continuation action to be invoked when the instance completes.</summary> + /// <param name="continuation">The action to invoke when the operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <remarks>Unlike OnCompleted, UnsafeOnCompleted need not propagate ExecutionContext information.</remarks> + [SecurityCritical] + void UnsafeOnCompleted(Action continuation); + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IUnknownConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IUnknownConstantAttribute.cs new file mode 100644 index 0000000000..f8717cff52 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IUnknownConstantAttribute.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ +[Serializable] +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class IUnknownConstantAttribute : CustomConstantAttribute + { + public IUnknownConstantAttribute() + { + } + + public override Object Value + { + get + { + return new UnknownWrapper(null); + } + } + + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IndexerNameAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IndexerNameAttribute.cs new file mode 100644 index 0000000000..0323fe0cf6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IndexerNameAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + using System; + +[Serializable] +[AttributeUsage(AttributeTargets.Property, Inherited = true)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class IndexerNameAttribute: Attribute + { + public IndexerNameAttribute(String indexerName) + {} + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs new file mode 100644 index 0000000000..ee7807a5dd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +namespace System.Runtime.CompilerServices +{ + using System; + + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true, Inherited=false)] + public sealed class InternalsVisibleToAttribute : Attribute + { + private string _assemblyName; + private bool _allInternalsVisible = true; + + public InternalsVisibleToAttribute(string assemblyName) + { + this._assemblyName = assemblyName; + } + + public string AssemblyName + { + get + { + return _assemblyName; + } + } + + public bool AllInternalsVisible + { + get { return _allInternalsVisible; } + set { _allInternalsVisible = value; } + } + } + + /// <summary> + /// If AllInternalsVisible is not true for a friend assembly, the FriendAccessAllowed attribute + /// indicates which internals are shared with that friend assembly. + /// </summary> + [AttributeUsage(AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Enum | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = false, + Inherited = false)] + [FriendAccessAllowed] + internal sealed class FriendAccessAllowedAttribute : Attribute { + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsBoxed.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsBoxed.cs new file mode 100644 index 0000000000..8b6691c09d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsBoxed.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Indicates that the modified reference type is a boxed valuetype + public static class IsBoxed + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsByValue.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsByValue.cs new file mode 100644 index 0000000000..d16a853597 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsByValue.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Indicates that the modified method argument is passed by value + public static class IsByValue + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsConst.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsConst.cs new file mode 100644 index 0000000000..210e5997a7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsConst.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Indicates that the modified type is const (i.e. has a const modifier) + public static class IsConst + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsCopyConstructed.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsCopyConstructed.cs new file mode 100644 index 0000000000..ee40ee7b02 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsCopyConstructed.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + [System.Runtime.InteropServices.ComVisible(true)] + public static class IsCopyConstructed + {} +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsExplicitlyDereferenced.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsExplicitlyDereferenced.cs new file mode 100644 index 0000000000..480a62175d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsExplicitlyDereferenced.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Consider the following C++ method prototypes: + // 1) int foo(int ^arg); + // 2) int foo(int &arg); + // + // Both of these methods will have a .NET type signature that looks the + // same, but when importing a method from a metadata scope, the compiler + // needs to know what the calling syntax should be. This modopt and its + // partner "IsImplicitlyDereferenced" disambiguate reference versus + // pointer arguments. + // + // Indicates that the modified GC reference represents a pointer in a + // method signature. + public static class IsExplicitlyDereferenced + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsImplicitlyDereferenced.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsImplicitlyDereferenced.cs new file mode 100644 index 0000000000..ea81cb8ec5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsImplicitlyDereferenced.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Consider the following C++ method prototypes: + // 1) int foo(int ^arg); + // 2) int foo(int &arg); + // + // Both of these methods will have a .NET type signature that looks the + // same, but when importing a method from a metadata scope, the compiler + // needs to know what the calling syntax should be. This modopt and its + // partner "IsExplicitlyDereferenced" disambiguate reference versus + // pointer arguments. + // + // Indicates that the modified GC reference represents a reference in a + // method signature. + public static class IsImplicitlyDereferenced + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsJitIntrinsic.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsJitIntrinsic.cs new file mode 100644 index 0000000000..013e50f3ea --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsJitIntrinsic.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Indicates that the modified method is an intrinsic for which the JIT + // can perform special code generation. + public static class IsJitIntrinsic + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsLong.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsLong.cs new file mode 100644 index 0000000000..e8bebfb2d3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsLong.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // The C++ standard indicates that a long is always 4-bytes, whereas the + // size of an integer is system dependent (not exceedign sizeof(long)). + // The CLR does not offer a mechanism for encoding this distinction, + // but it is critically important for maintaining language level type + // safety. + // + // Indicates that the modified integer is a standard C++ long. + // Could also be called IsAlternateIntegerType or something else. + public static class IsLong + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsPinned.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsPinned.cs new file mode 100644 index 0000000000..e796d1a1e7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsPinned.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Indicates that the modified instance is pinned in memory. + public static class IsPinned + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsSignUnspecifiedByte.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsSignUnspecifiedByte.cs new file mode 100644 index 0000000000..e68f4d7751 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsSignUnspecifiedByte.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // C++ recognizes three char types: signed char, unsigned char, and char. + // When a char is neither signed nor unsigned, it is a char. + // This modopt indicates that the modified instance is a char. + // + // Any compiler could use this to indicate that the user has not specified + // Sign behavior for the given byte. + public static class IsSignUnspecifiedByte + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsUdtReturn.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsUdtReturn.cs new file mode 100644 index 0000000000..dd85914b53 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsUdtReturn.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Indicates that the return type is a user defined type + public static class IsUdtReturn + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IsVolatile.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IsVolatile.cs new file mode 100644 index 0000000000..ea2fe032c6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IsVolatile.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ +[System.Runtime.InteropServices.ComVisible(true)] + public static class IsVolatile + { + // no instantiation, please! + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs new file mode 100644 index 0000000000..4bb9b4eb8f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; + +namespace System.Runtime.CompilerServices +{ + [Serializable, AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class IteratorStateMachineAttribute : StateMachineAttribute + { + public IteratorStateMachineAttribute(Type stateMachineType) + : base(stateMachineType) + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs new file mode 100644 index 0000000000..d081d70070 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices { + + using System; + using System.Reflection; + + // This Enum matchs the miImpl flags defined in corhdr.h. It is used to specify + // certain method properties. + + [Serializable] + [Flags] +[System.Runtime.InteropServices.ComVisible(true)] + public enum MethodImplOptions + { + Unmanaged = System.Reflection.MethodImplAttributes.Unmanaged, + ForwardRef = System.Reflection.MethodImplAttributes.ForwardRef, + PreserveSig = System.Reflection.MethodImplAttributes.PreserveSig, + InternalCall = System.Reflection.MethodImplAttributes.InternalCall, + Synchronized = System.Reflection.MethodImplAttributes.Synchronized, + NoInlining = System.Reflection.MethodImplAttributes.NoInlining, + [System.Runtime.InteropServices.ComVisible(false)] + AggressiveInlining = System.Reflection.MethodImplAttributes.AggressiveInlining, + NoOptimization = System.Reflection.MethodImplAttributes.NoOptimization, + // **** If you add something, update internal MethodImplAttribute(MethodImplAttributes methodImplAttributes)! **** + } + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public enum MethodCodeType + { + IL = System.Reflection.MethodImplAttributes.IL, + Native = System.Reflection.MethodImplAttributes.Native, + /// <internalonly/> + OPTIL = System.Reflection.MethodImplAttributes.OPTIL, + Runtime = System.Reflection.MethodImplAttributes.Runtime + } + + // Custom attribute to specify additional method properties. +[Serializable] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] +[System.Runtime.InteropServices.ComVisible(true)] + sealed public class MethodImplAttribute : Attribute + { + internal MethodImplOptions _val; + public MethodCodeType MethodCodeType; + + internal MethodImplAttribute(MethodImplAttributes methodImplAttributes) + { + MethodImplOptions all = + MethodImplOptions.Unmanaged | MethodImplOptions.ForwardRef | MethodImplOptions.PreserveSig | + MethodImplOptions.InternalCall | MethodImplOptions.Synchronized | + MethodImplOptions.NoInlining | MethodImplOptions.AggressiveInlining | + MethodImplOptions.NoOptimization; + _val = ((MethodImplOptions)methodImplAttributes) & all; + } + + public MethodImplAttribute(MethodImplOptions methodImplOptions) + { + _val = methodImplOptions; + } + + public MethodImplAttribute(short value) + { + _val = (MethodImplOptions)value; + } + + public MethodImplAttribute() + { + } + + public MethodImplOptions Value { get {return _val;} } + } + +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/NativeCppClassAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/NativeCppClassAttribute.cs new file mode 100644 index 0000000000..0d6c759d76 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/NativeCppClassAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices { +[Serializable] +[AttributeUsage(AttributeTargets.Struct, Inherited = true), + System.Runtime.InteropServices.ComVisible(true)] + public sealed class NativeCppClassAttribute : Attribute + { + public NativeCppClassAttribute () {} + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs new file mode 100644 index 0000000000..d5e64a1177 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Attribute: ReferenceAssemblyAttribute +** +** Purpose: Identifies an assembly as being a "reference +** assembly", meaning it contains public surface area but +** no usable implementation. Reference assemblies +** should be loadable for introspection, but not execution. +** +============================================================*/ +namespace System.Runtime.CompilerServices +{ + using System; + + [Serializable] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)] + public sealed class ReferenceAssemblyAttribute : Attribute + { + private String _description; // Maybe ".NET FX v4.0 SP1, partial trust"? + + public ReferenceAssemblyAttribute() + { + } + + public ReferenceAssemblyAttribute(String description) + { + _description = description; + } + + public String Description + { + get { return _description; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/RequiredAttributeAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/RequiredAttributeAttribute.cs new file mode 100644 index 0000000000..f363696ebd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/RequiredAttributeAttribute.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.CompilerServices +{ +[Serializable] +[AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, + AllowMultiple=true, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class RequiredAttributeAttribute : Attribute + { + private Type requiredContract; + + public RequiredAttributeAttribute (Type requiredContract) + { + this.requiredContract= requiredContract; + } + public Type RequiredContract + { + get { return this.requiredContract; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs new file mode 100644 index 0000000000..40a9b7c568 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/*============================================================================= +** +** +** +** Purpose: Mark up the program to indicate various legacy or new opt-in behaviors. +** +** +=============================================================================*/ + +namespace System.Runtime.CompilerServices +{ + + using System; + +[Serializable] +[AttributeUsage(AttributeTargets.Assembly, Inherited=false, AllowMultiple=false)] + public sealed class RuntimeCompatibilityAttribute : Attribute + { + // fields + private bool m_wrapNonExceptionThrows; + + // constructors + public RuntimeCompatibilityAttribute() { + // legacy behavior is the default, and m_wrapNonExceptionThrows is implicitly + // false thanks to the CLR's guarantee of zeroed memory. + } + + // properties + + // If a non-CLSCompliant exception (i.e. one that doesn't derive from System.Exception) is + // thrown, should it be wrapped up in a System.Runtime.CompilerServices.RuntimeWrappedException + // instance when presented to catch handlers? + public bool WrapNonExceptionThrows { + get { + return m_wrapNonExceptionThrows; + } + set { + m_wrapNonExceptionThrows = value; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs new file mode 100644 index 0000000000..d20fe0bffd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -0,0 +1,248 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// RuntimeHelpers +// This class defines a set of static methods that provide support for compilers. +// +// +namespace System.Runtime.CompilerServices { + + using System; + using System.Security; + using System.Runtime; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Runtime.ConstrainedExecution; + using System.Runtime.Serialization; + using System.Security.Permissions; + using System.Threading; + using System.Runtime.Versioning; + using System.Diagnostics.Contracts; + + public static class RuntimeHelpers + { +#if FEATURE_CORECLR + // Exposed here as a more appropriate place than on FormatterServices itself, + // which is a high level reflection heavy type. + public static Object GetUninitializedObject(Type type) + { + return FormatterServices.GetUninitializedObject(type); + } +#endif // FEATURE_CORECLR + + [System.Security.SecuritySafeCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void InitializeArray(Array array,RuntimeFieldHandle fldHandle); + + // GetObjectValue is intended to allow value classes to be manipulated as 'Object' + // but have aliasing behavior of a value class. The intent is that you would use + // this function just before an assignment to a variable of type 'Object'. If the + // value being assigned is a mutable value class, then a shallow copy is returned + // (because value classes have copy semantics), but otherwise the object itself + // is returned. + // + // Note: VB calls this method when they're about to assign to an Object + // or pass it as a parameter. The goal is to make sure that boxed + // value types work identical to unboxed value types - ie, they get + // cloned when you pass them around, and are always passed by value. + // Of course, reference types are not cloned. + // + [System.Security.SecuritySafeCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern Object GetObjectValue(Object obj); + + // RunClassConstructor causes the class constructor for the given type to be triggered + // in the current domain. After this call returns, the class constructor is guaranteed to + // have at least been started by some thread. In the absence of class constructor + // deadlock conditions, the call is further guaranteed to have completed. + // + // This call will generate an exception if the specified class constructor threw an + // exception when it ran. + + [System.Security.SecuritySafeCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void _RunClassConstructor(RuntimeType type); + + public static void RunClassConstructor(RuntimeTypeHandle type) + { + _RunClassConstructor(type.GetRuntimeType()); + } + + // RunModuleConstructor causes the module constructor for the given type to be triggered + // in the current domain. After this call returns, the module constructor is guaranteed to + // have at least been started by some thread. In the absence of module constructor + // deadlock conditions, the call is further guaranteed to have completed. + // + // This call will generate an exception if the specified module constructor threw an + // exception when it ran. + + [System.Security.SecuritySafeCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void _RunModuleConstructor(System.Reflection.RuntimeModule module); + + public static void RunModuleConstructor(ModuleHandle module) + { + _RunModuleConstructor(module.GetRuntimeModule()); + } + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static unsafe extern void _PrepareMethod(IRuntimeMethodInfo method, IntPtr* pInstantiation, int cInstantiation); + + [System.Security.SecurityCritical] // auto-generated + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + internal static extern void _CompileMethod(IRuntimeMethodInfo method); + + // Simple (instantiation not required) method. + [System.Security.SecurityCritical] // auto-generated_required + public static void PrepareMethod(RuntimeMethodHandle method) + { + unsafe + { + _PrepareMethod(method.GetMethodInfo(), null, 0); + } + } + + // Generic method or method with generic class with specific instantiation. + [System.Security.SecurityCritical] // auto-generated_required + public static void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeHandle[] instantiation) + { + unsafe + { + int length; + IntPtr[] instantiationHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(instantiation, out length); + fixed (IntPtr* pInstantiation = instantiationHandles) + { + _PrepareMethod(method.GetMethodInfo(), pInstantiation, length); + GC.KeepAlive(instantiation); + } + } + } + + // This method triggers a given delegate to be prepared. This involves preparing the + // delegate's Invoke method and preparing the target of that Invoke. In the case of + // a multi-cast delegate, we rely on the fact that each individual component was prepared + // prior to the Combine. In other words, this service does not navigate through the + // entire multicasting list. + // If our own reliable event sinks perform the Combine (for example AppDomain.DomainUnload), + // then the result is fully prepared. But if a client calls Combine himself and then + // then adds that combination to e.g. AppDomain.DomainUnload, then the client is responsible + // for his own preparation. + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void PrepareDelegate(Delegate d); + + // See comment above for PrepareDelegate + // + // PrepareContractedDelegate weakens this a bit by only assuring that we prepare + // delegates which also have a ReliabilityContract. This is useful for services that + // want to provide opt-in reliability, generally some random event sink providing + // always reliable semantics to random event handlers that are likely to have not + // been written with relability in mind is a lost cause anyway. + // + // NOTE: that for the NGen case you can sidestep the required ReliabilityContract + // by using the [PrePrepareMethod] attribute. + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void PrepareContractedDelegate(Delegate d); + + [System.Security.SecuritySafeCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int GetHashCode(Object o); + + [System.Security.SecuritySafeCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public new static extern bool Equals(Object o1, Object o2); + + public static int OffsetToStringData + { + // This offset is baked in by string indexer intrinsic, so there is no harm + // in getting it baked in here as well. + [System.Runtime.Versioning.NonVersionable] + get { + // Number of bytes from the address pointed to by a reference to + // a String to the first 16-bit character in the String. Skip + // over the MethodTable pointer, & String + // length. Of course, the String reference points to the memory + // after the sync block, so don't count that. + // This property allows C#'s fixed statement to work on Strings. + // On 64 bit platforms, this should be 12 (8+4) and on 32 bit 8 (4+4). +#if BIT64 + return 12; +#else // 32 + return 8; +#endif // BIT64 + } + } + + // This method ensures that there is sufficient stack to execute the average Framework function. + // If there is not enough stack, then it throws System.InsufficientExecutionStackException. + // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack + // below. + [System.Security.SecuritySafeCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static extern void EnsureSufficientExecutionStack(); + +#if FEATURE_CORECLR + // This method ensures that there is sufficient stack to execute the average Framework function. + // If there is not enough stack, then it return false. + // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack + // below. + [System.Security.SecuritySafeCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal static extern bool TryEnsureSufficientExecutionStack(); +#endif + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public static extern void ProbeForSufficientStack(); + + // This method is a marker placed immediately before a try clause to mark the corresponding catch and finally blocks as + // constrained. There's no code here other than the probe because most of the work is done at JIT time when we spot a call to this routine. + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public static void PrepareConstrainedRegions() + { + ProbeForSufficientStack(); + } + + // When we detect a CER with no calls, we can point the JIT to this non-probing version instead + // as we don't need to probe. + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public static void PrepareConstrainedRegionsNoOP() + { + } + + #if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + #endif + public delegate void TryCode(Object userData); + + #if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + #endif + public delegate void CleanupCode(Object userData, bool exceptionThrown); + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData); + +#if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated +#endif + [PrePrepareMethod] + internal static void ExecuteBackoutCodeHelper(Object backoutCode, Object userData, bool exceptionThrown) + { + ((CleanupCode)backoutCode)(userData, exceptionThrown); + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs new file mode 100644 index 0000000000..2751d61db7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: The exception class uses to wrap all non-CLS compliant exceptions. +** +** +=============================================================================*/ + +namespace System.Runtime.CompilerServices { + using System; + using System.Runtime.Serialization; + using System.Runtime.Remoting; + using System.Security.Permissions; + using System.Diagnostics.Contracts; + + [Serializable] + public sealed class RuntimeWrappedException : Exception + { + private RuntimeWrappedException(Object thrownObject) + : base(Environment.GetResourceString("RuntimeWrappedException")) { + SetErrorCode(System.__HResults.COR_E_RUNTIMEWRAPPED); + m_wrappedException = thrownObject; + } + + public Object WrappedException { + get { return m_wrappedException; } + } + + private Object m_wrappedException; + + [System.Security.SecurityCritical] // auto-generated_required + public override void GetObjectData(SerializationInfo info, StreamingContext context) { + if (info==null) { + throw new ArgumentNullException("info"); + } + Contract.EndContractBlock(); + base.GetObjectData(info, context); + info.AddValue("WrappedException", m_wrappedException, typeof(Object)); + } + + internal RuntimeWrappedException(SerializationInfo info, StreamingContext context) + : base(info, context) { + m_wrappedException = info.GetValue("WrappedException", typeof(Object)); + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/ScopelessEnumAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/ScopelessEnumAttribute.cs new file mode 100644 index 0000000000..91769187cc --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/ScopelessEnumAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ +[Serializable] +[AttributeUsage(AttributeTargets.Enum)] + public sealed class ScopelessEnumAttribute : Attribute + { + public ScopelessEnumAttribute() + {} + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/SpecialNameAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/SpecialNameAttribute.cs new file mode 100644 index 0000000000..38e5538b44 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/SpecialNameAttribute.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Field | + AttributeTargets.Event | + AttributeTargets.Struct)] + + + public sealed class SpecialNameAttribute : Attribute + { + public SpecialNameAttribute() { } + } +} + + + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/StateMachineAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/StateMachineAttribute.cs new file mode 100644 index 0000000000..7c84009e1f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/StateMachineAttribute.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; + +namespace System.Runtime.CompilerServices +{ + [Serializable, AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public class StateMachineAttribute : Attribute + { + public Type StateMachineType { get; private set; } + + public StateMachineAttribute(Type stateMachineType) + { + this.StateMachineType = stateMachineType; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs new file mode 100644 index 0000000000..eb019eecbf --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace System.Runtime.CompilerServices +{ + using System; + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module)] + public sealed class SuppressIldasmAttribute : Attribute + { + public SuppressIldasmAttribute() + { + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/SuppressMergeCheckAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/SuppressMergeCheckAttribute.cs new file mode 100644 index 0000000000..6bb36c4bf5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/SuppressMergeCheckAttribute.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System.Runtime.InteropServices; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Method | + AttributeTargets.Field | + AttributeTargets.Event | + AttributeTargets.Property)] + + internal sealed class SuppressMergeCheckAttribute : Attribute + { + public SuppressMergeCheckAttribute() + {} + } +} + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs b/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs new file mode 100644 index 0000000000..ea6bb96e16 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs @@ -0,0 +1,531 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// Types for awaiting Task and Task<T>. These types are emitted from Task{<T>}.GetAwaiter +// and Task{<T>}.ConfigureAwait. They are meant to be used only by the compiler, e.g. +// +// await nonGenericTask; +// ===================== +// var $awaiter = nonGenericTask.GetAwaiter(); +// if (!$awaiter.IsCompleted) +// { +// SPILL: +// $builder.AwaitUnsafeOnCompleted(ref $awaiter, ref this); +// return; +// Label: +// UNSPILL; +// } +// $awaiter.GetResult(); +// +// result += await genericTask.ConfigureAwait(false); +// =================================================================================== +// var $awaiter = genericTask.ConfigureAwait(false).GetAwaiter(); +// if (!$awaiter.IsCompleted) +// { +// SPILL; +// $builder.AwaitUnsafeOnCompleted(ref $awaiter, ref this); +// return; +// Label: +// UNSPILL; +// } +// result += $awaiter.GetResult(); +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using System.Security.Permissions; +using System.Diagnostics.Tracing; + +// NOTE: For performance reasons, initialization is not verified. If a developer +// incorrectly initializes a task awaiter, which should only be done by the compiler, +// NullReferenceExceptions may be generated (the alternative would be for us to detect +// this case and then throw a different exception instead). This is the same tradeoff +// that's made with other compiler-focused value types like List<T>.Enumerator. + +namespace System.Runtime.CompilerServices +{ + /// <summary>Provides an awaiter for awaiting a <see cref="System.Threading.Tasks.Task"/>.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct TaskAwaiter : ICriticalNotifyCompletion + { + /// <summary>The task being awaited.</summary> + private readonly Task m_task; + + /// <summary>Initializes the <see cref="TaskAwaiter"/>.</summary> + /// <param name="task">The <see cref="System.Threading.Tasks.Task"/> to be awaited.</param> + internal TaskAwaiter(Task task) + { + Contract.Requires(task != null, "Constructing an awaiter requires a task to await."); + m_task = task; + } + + /// <summary>Gets whether the task being awaited is completed.</summary> + /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + public bool IsCompleted + { + get { return m_task.IsCompleted; } + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecuritySafeCritical] + public void OnCompleted(Action continuation) + { + OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:true); + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecurityCritical] + public void UnsafeOnCompleted(Action continuation) + { + OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:false); + } + + /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task"/>.</summary> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception> + /// <exception cref="System.Exception">The task completed in a Faulted state.</exception> + public void GetResult() + { + ValidateEnd(m_task); + } + + /// <summary> + /// Fast checks for the end of an await operation to determine whether more needs to be done + /// prior to completing the await. + /// </summary> + /// <param name="task">The awaited task.</param> + internal static void ValidateEnd(Task task) + { + // Fast checks that can be inlined. + if (task.IsWaitNotificationEnabledOrNotRanToCompletion) + { + // If either the end await bit is set or we're not completed successfully, + // fall back to the slower path. + HandleNonSuccessAndDebuggerNotification(task); + } + } + + /// <summary> + /// Ensures the task is completed, triggers any necessary debugger breakpoints for completing + /// the await on the task, and throws an exception if the task did not complete successfully. + /// </summary> + /// <param name="task">The awaited task.</param> + private static void HandleNonSuccessAndDebuggerNotification(Task task) + { + // NOTE: The JIT refuses to inline ValidateEnd when it contains the contents + // of HandleNonSuccessAndDebuggerNotification, hence the separation. + + // Synchronously wait for the task to complete. When used by the compiler, + // the task will already be complete. This code exists only for direct GetResult use, + // for cases where the same exception propagation semantics used by "await" are desired, + // but where for one reason or another synchronous rather than asynchronous waiting is needed. + if (!task.IsCompleted) + { + bool taskCompleted = task.InternalWait(Timeout.Infinite, default(CancellationToken)); + Contract.Assert(taskCompleted, "With an infinite timeout, the task should have always completed."); + } + + // Now that we're done, alert the debugger if so requested + task.NotifyDebuggerOfWaitCompletionIfNecessary(); + + // And throw an exception if the task is faulted or canceled. + if (!task.IsRanToCompletion) ThrowForNonSuccess(task); + } + + /// <summary>Throws an exception to handle a task that completed in a state other than RanToCompletion.</summary> + private static void ThrowForNonSuccess(Task task) + { + Contract.Requires(task.IsCompleted, "Task must have been completed by now."); + Contract.Requires(task.Status != TaskStatus.RanToCompletion, "Task should not be completed successfully."); + + // Handle whether the task has been canceled or faulted + switch (task.Status) + { + // If the task completed in a canceled state, throw an OperationCanceledException. + // This will either be the OCE that actually caused the task to cancel, or it will be a new + // TaskCanceledException. TCE derives from OCE, and by throwing it we automatically pick up the + // completed task's CancellationToken if it has one, including that CT in the OCE. + case TaskStatus.Canceled: + var oceEdi = task.GetCancellationExceptionDispatchInfo(); + if (oceEdi != null) + { + oceEdi.Throw(); + Contract.Assert(false, "Throw() should have thrown"); + } + throw new TaskCanceledException(task); + + // If the task faulted, throw its first exception, + // even if it contained more than one. + case TaskStatus.Faulted: + var edis = task.GetExceptionDispatchInfos(); + if (edis.Count > 0) + { + edis[0].Throw(); + Contract.Assert(false, "Throw() should have thrown"); + break; // Necessary to compile: non-reachable, but compiler can't determine that + } + else + { + Contract.Assert(false, "There should be exceptions if we're Faulted."); + throw task.Exception; + } + } + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="task">The task being awaited.</param> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <param name="continueOnCapturedContext">Whether to capture and marshal back to the current context.</param> + /// <param name="flowExecutionContext">Whether to flow ExecutionContext across the await.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + [SecurityCritical] + internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext) + { + if (continuation == null) throw new ArgumentNullException("continuation"); + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + + // If TaskWait* ETW events are enabled, trace a beginning event for this await + // and set up an ending event to be traced when the asynchronous await completes. + if ( TplEtwProvider.Log.IsEnabled() || Task.s_asyncDebuggingEnabled) + { + continuation = OutputWaitEtwEvents(task, continuation); + } + + // Set the continuation onto the awaited task. + task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref stackMark); + } + + /// <summary> + /// Outputs a WaitBegin ETW event, and augments the continuation action to output a WaitEnd ETW event. + /// </summary> + /// <param name="task">The task being awaited.</param> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <returns>The action to use as the actual continuation.</returns> + private static Action OutputWaitEtwEvents(Task task, Action continuation) + { + Contract.Requires(task != null, "Need a task to wait on"); + Contract.Requires(continuation != null, "Need a continuation to invoke when the wait completes"); + + if (Task.s_asyncDebuggingEnabled) + { + Task.AddToActiveTasks(task); + } + + var etwLog = TplEtwProvider.Log; + + if (etwLog.IsEnabled()) + { + // ETW event for Task Wait Begin + var currentTaskAtBegin = Task.InternalCurrent; + + // If this task's continuation is another task, get it. + var continuationTask = AsyncMethodBuilderCore.TryGetContinuationTask(continuation); + etwLog.TaskWaitBegin( + (currentTaskAtBegin != null ? currentTaskAtBegin.m_taskScheduler.Id : TaskScheduler.Default.Id), + (currentTaskAtBegin != null ? currentTaskAtBegin.Id : 0), + task.Id, TplEtwProvider.TaskWaitBehavior.Asynchronous, + (continuationTask != null ? continuationTask.Id : 0), System.Threading.Thread.GetDomainID()); + } + + // Create a continuation action that outputs the end event and then invokes the user + // provided delegate. This incurs the allocations for the closure/delegate, but only if the event + // is enabled, and in doing so it allows us to pass the awaited task's information into the end event + // in a purely pay-for-play manner (the alternatively would be to increase the size of TaskAwaiter + // just for this ETW purpose, not pay-for-play, since GetResult would need to know whether a real yield occurred). + return AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, () => + { + if (Task.s_asyncDebuggingEnabled) + { + Task.RemoveFromActiveTasks(task.Id); + } + + // ETW event for Task Wait End. + Guid prevActivityId = new Guid(); + bool bEtwLogEnabled = etwLog.IsEnabled(); + if (bEtwLogEnabled) + { + var currentTaskAtEnd = Task.InternalCurrent; + etwLog.TaskWaitEnd( + (currentTaskAtEnd != null ? currentTaskAtEnd.m_taskScheduler.Id : TaskScheduler.Default.Id), + (currentTaskAtEnd != null ? currentTaskAtEnd.Id : 0), + task.Id); + + // Ensure the continuation runs under the activity ID of the task that completed for the + // case the antecendent is a promise (in the other cases this is already the case). + if (etwLog.TasksSetActivityIds && (task.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0) + EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(task.Id), out prevActivityId); + } + // Invoke the original continuation provided to OnCompleted. + continuation(); + + if (bEtwLogEnabled) + { + etwLog.TaskWaitContinuationComplete(task.Id); + if (etwLog.TasksSetActivityIds && (task.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0) + EventSource.SetCurrentThreadActivityId(prevActivityId); + } + }); + } + } + + /// <summary>Provides an awaiter for awaiting a <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion + { + /// <summary>The task being awaited.</summary> + private readonly Task<TResult> m_task; + + /// <summary>Initializes the <see cref="TaskAwaiter{TResult}"/>.</summary> + /// <param name="task">The <see cref="System.Threading.Tasks.Task{TResult}"/> to be awaited.</param> + internal TaskAwaiter(Task<TResult> task) + { + Contract.Requires(task != null, "Constructing an awaiter requires a task to await."); + m_task = task; + } + + /// <summary>Gets whether the task being awaited is completed.</summary> + /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + public bool IsCompleted + { + get { return m_task.IsCompleted; } + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecuritySafeCritical] + public void OnCompleted(Action continuation) + { + TaskAwaiter.OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:true); + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecurityCritical] + public void UnsafeOnCompleted(Action continuation) + { + TaskAwaiter.OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:false); + } + + /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary> + /// <returns>The result of the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception> + /// <exception cref="System.Exception">The task completed in a Faulted state.</exception> + public TResult GetResult() + { + TaskAwaiter.ValidateEnd(m_task); + return m_task.ResultOnSuccess; + } + } + + /// <summary>Provides an awaitable object that allows for configured awaits on <see cref="System.Threading.Tasks.Task"/>.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + public struct ConfiguredTaskAwaitable + { + /// <summary>The task being awaited.</summary> + private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter m_configuredTaskAwaiter; + + /// <summary>Initializes the <see cref="ConfiguredTaskAwaitable"/>.</summary> + /// <param name="task">The awaitable <see cref="System.Threading.Tasks.Task"/>.</param> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. + /// </param> + internal ConfiguredTaskAwaitable(Task task, bool continueOnCapturedContext) + { + Contract.Requires(task != null, "Constructing an awaitable requires a task to await."); + m_configuredTaskAwaiter = new ConfiguredTaskAwaitable.ConfiguredTaskAwaiter(task, continueOnCapturedContext); + } + + /// <summary>Gets an awaiter for this awaitable.</summary> + /// <returns>The awaiter.</returns> + public ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter() + { + return m_configuredTaskAwaiter; + } + + /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable"/>.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion + { + /// <summary>The task being awaited.</summary> + private readonly Task m_task; + /// <summary>Whether to attempt marshaling back to the original context.</summary> + private readonly bool m_continueOnCapturedContext; + + /// <summary>Initializes the <see cref="ConfiguredTaskAwaiter"/>.</summary> + /// <param name="task">The <see cref="System.Threading.Tasks.Task"/> to await.</param> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the original context captured + /// when BeginAwait is called; otherwise, false. + /// </param> + internal ConfiguredTaskAwaiter(Task task, bool continueOnCapturedContext) + { + Contract.Requires(task != null, "Constructing an awaiter requires a task to await."); + m_task = task; + m_continueOnCapturedContext = continueOnCapturedContext; + } + + /// <summary>Gets whether the task being awaited is completed.</summary> + /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + public bool IsCompleted + { + get { return m_task.IsCompleted; } + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecuritySafeCritical] + public void OnCompleted(Action continuation) + { + TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:true); + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecurityCritical] + public void UnsafeOnCompleted(Action continuation) + { + TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:false); + } + + /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task"/>.</summary> + /// <returns>The result of the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception> + /// <exception cref="System.Exception">The task completed in a Faulted state.</exception> + public void GetResult() + { + TaskAwaiter.ValidateEnd(m_task); + } + } + } + + /// <summary>Provides an awaitable object that allows for configured awaits on <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + public struct ConfiguredTaskAwaitable<TResult> + { + /// <summary>The underlying awaitable on whose logic this awaitable relies.</summary> + private readonly ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter m_configuredTaskAwaiter; + + /// <summary>Initializes the <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary> + /// <param name="task">The awaitable <see cref="System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. + /// </param> + internal ConfiguredTaskAwaitable(Task<TResult> task, bool continueOnCapturedContext) + { + m_configuredTaskAwaiter = new ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter(task, continueOnCapturedContext); + } + + /// <summary>Gets an awaiter for this awaitable.</summary> + /// <returns>The awaiter.</returns> + public ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter GetAwaiter() + { + return m_configuredTaskAwaiter; + } + + /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion + { + /// <summary>The task being awaited.</summary> + private readonly Task<TResult> m_task; + /// <summary>Whether to attempt marshaling back to the original context.</summary> + private readonly bool m_continueOnCapturedContext; + + /// <summary>Initializes the <see cref="ConfiguredTaskAwaiter"/>.</summary> + /// <param name="task">The awaitable <see cref="System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. + /// </param> + internal ConfiguredTaskAwaiter(Task<TResult> task, bool continueOnCapturedContext) + { + Contract.Requires(task != null, "Constructing an awaiter requires a task to await."); + m_task = task; + m_continueOnCapturedContext = continueOnCapturedContext; + } + + /// <summary>Gets whether the task being awaited is completed.</summary> + /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + public bool IsCompleted + { + get { return m_task.IsCompleted; } + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecuritySafeCritical] + public void OnCompleted(Action continuation) + { + TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:true); + } + + /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary> + /// <param name="continuation">The action to invoke when the await operation completes.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + [SecurityCritical] + public void UnsafeOnCompleted(Action continuation) + { + TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:false); + } + + /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary> + /// <returns>The result of the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception> + /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception> + /// <exception cref="System.Exception">The task completed in a Faulted state.</exception> + public TResult GetResult() + { + TaskAwaiter.ValidateEnd(m_task); + return m_task.ResultOnSuccess; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/TypeDependencyAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/TypeDependencyAttribute.cs new file mode 100644 index 0000000000..db04eb9348 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/TypeDependencyAttribute.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + using System; + using System.Diagnostics.Contracts; + + // We might want to make this inherited someday. But I suspect it shouldn't + // be necessary. + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] + internal sealed class TypeDependencyAttribute: Attribute + { + + private string typeName; + + public TypeDependencyAttribute (string typeName) + { + if(typeName == null) throw new ArgumentNullException("typeName"); + Contract.EndContractBlock(); + this.typeName = typeName; + } + } + +} + + + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs new file mode 100644 index 0000000000..c1656dcf99 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] + public sealed class TypeForwardedFromAttribute : Attribute + { + string assemblyFullName; + + private TypeForwardedFromAttribute() + { + // Disallow default constructor + } + + + public TypeForwardedFromAttribute(string assemblyFullName) + { + if (String.IsNullOrEmpty(assemblyFullName)) + { + throw new ArgumentNullException("assemblyFullName"); + } + this.assemblyFullName = assemblyFullName; + } + + public string AssemblyFullName + { + get { + return assemblyFullName; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs new file mode 100644 index 0000000000..034dad1afe --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using System.Reflection; + +namespace System.Runtime.CompilerServices +{ + using System; + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true, Inherited=false)] + public sealed class TypeForwardedToAttribute : Attribute + { + private Type _destination; + + public TypeForwardedToAttribute(Type destination) + { + _destination = destination; + } + + public Type Destination + { + get { + return _destination; + } + } + + [System.Security.SecurityCritical] + internal static TypeForwardedToAttribute[] GetCustomAttribute(RuntimeAssembly assembly) + { + Type[] types = null; + RuntimeAssembly.GetForwardedTypes(assembly.GetNativeHandle(), JitHelpers.GetObjectHandleOnStack(ref types)); + + TypeForwardedToAttribute[] attributes = new TypeForwardedToAttribute[types.Length]; + for (int i = 0; i < types.Length; ++i) + attributes[i] = new TypeForwardedToAttribute(types[i]); + + return attributes; + } + + } +} + + + + diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs new file mode 100644 index 0000000000..bc210ccb71 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + using System; + +[Serializable] +[AttributeUsage(AttributeTargets.Struct)] + sealed public class UnsafeValueTypeAttribute : Attribute + { + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/YieldAwaitable.cs b/src/mscorlib/src/System/Runtime/CompilerServices/YieldAwaitable.cs new file mode 100644 index 0000000000..b29b39c5bf --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/YieldAwaitable.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// Compiler-targeted type for switching back into the current execution context, e.g. +// +// await Task.Yield(); +// ===================== +// var $awaiter = Task.Yield().GetAwaiter(); +// if (!$awaiter.IsCompleted) +// { +// $builder.AwaitUnsafeOnCompleted(ref $awaiter, ref this); +// return; +// Label: +// } +// $awaiter.GetResult(); +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Security; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Diagnostics.Tracing; +using System.Threading; +using System.Threading.Tasks; +using System.Security.Permissions; + +namespace System.Runtime.CompilerServices +{ + // NOTE: YieldAwaitable currently has no state; while developers are encouraged to use Task.Yield() to produce one, + // no validation is performed to ensure that the developer isn't doing "await new YieldAwaitable()". Such validation + // would require additional, useless state to be stored, and as this is a type in the CompilerServices namespace, and + // as the above example isn't harmful, we take the cheaper approach of not validating anything. + + /// <summary>Provides an awaitable context for switching into a target environment.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + public struct YieldAwaitable + { + /// <summary>Gets an awaiter for this <see cref="YieldAwaitable"/>.</summary> + /// <returns>An awaiter for this awaitable.</returns> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + public YieldAwaiter GetAwaiter() { return new YieldAwaiter(); } + + /// <summary>Provides an awaiter that switches into a target environment.</summary> + /// <remarks>This type is intended for compiler use only.</remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public struct YieldAwaiter : ICriticalNotifyCompletion + { + /// <summary>Gets whether a yield is not required.</summary> + /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks> + public bool IsCompleted { get { return false; } } // yielding is always required for YieldAwaiter, hence false + + /// <summary>Posts the <paramref name="continuation"/> back to the current context.</summary> + /// <param name="continuation">The action to invoke asynchronously.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + [SecuritySafeCritical] + public void OnCompleted(Action continuation) + { + QueueContinuation(continuation, flowContext: true); + } + + /// <summary>Posts the <paramref name="continuation"/> back to the current context.</summary> + /// <param name="continuation">The action to invoke asynchronously.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + [SecurityCritical] + public void UnsafeOnCompleted(Action continuation) + { + QueueContinuation(continuation, flowContext: false); + } + + /// <summary>Posts the <paramref name="continuation"/> back to the current context.</summary> + /// <param name="continuation">The action to invoke asynchronously.</param> + /// <param name="flowContext">true to flow ExecutionContext; false if flowing is not required.</param> + /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception> + [SecurityCritical] + private static void QueueContinuation(Action continuation, bool flowContext) + { + // Validate arguments + if (continuation == null) throw new ArgumentNullException("continuation"); + Contract.EndContractBlock(); + + if (TplEtwProvider.Log.IsEnabled()) + { + continuation = OutputCorrelationEtwEvent(continuation); + } + // Get the current SynchronizationContext, and if there is one, + // post the continuation to it. However, treat the base type + // as if there wasn't a SynchronizationContext, since that's what it + // logically represents. + var syncCtx = SynchronizationContext.CurrentNoFlow; + if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) + { + syncCtx.Post(s_sendOrPostCallbackRunAction, continuation); + } + else + { + // If we're targeting the default scheduler, queue to the thread pool, so that we go into the global + // queue. As we're going into the global queue, we might as well use QUWI, which for the global queue is + // just a tad faster than task, due to a smaller object getting allocated and less work on the execution path. + TaskScheduler scheduler = TaskScheduler.Current; + if (scheduler == TaskScheduler.Default) + { + if (flowContext) + { + ThreadPool.QueueUserWorkItem(s_waitCallbackRunAction, continuation); + } + else + { + ThreadPool.UnsafeQueueUserWorkItem(s_waitCallbackRunAction, continuation); + } + } + // We're targeting a custom scheduler, so queue a task. + else + { + Task.Factory.StartNew(continuation, default(CancellationToken), TaskCreationOptions.PreferFairness, scheduler); + } + } + } + + private static Action OutputCorrelationEtwEvent(Action continuation) + { + int continuationId = Task.NewId(); + Task currentTask = Task.InternalCurrent; + // fire the correlation ETW event + TplEtwProvider.Log.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, (currentTask != null) ? currentTask.Id : 0, continuationId); + + return AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, () => + { + var etwLog = TplEtwProvider.Log; + etwLog.TaskWaitContinuationStarted(continuationId); + + // ETW event for Task Wait End. + Guid prevActivityId = new Guid(); + // Ensure the continuation runs under the correlated activity ID generated above + if (etwLog.TasksSetActivityIds) + EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(continuationId), out prevActivityId); + + // Invoke the original continuation provided to OnCompleted. + continuation(); + // Restore activity ID + + if (etwLog.TasksSetActivityIds) + EventSource.SetCurrentThreadActivityId(prevActivityId); + + etwLog.TaskWaitContinuationComplete(continuationId); + }); + + } + + /// <summary>WaitCallback that invokes the Action supplied as object state.</summary> + private static readonly WaitCallback s_waitCallbackRunAction = RunAction; + /// <summary>SendOrPostCallback that invokes the Action supplied as object state.</summary> + private static readonly SendOrPostCallback s_sendOrPostCallbackRunAction = RunAction; + + /// <summary>Runs an Action delegate provided as state.</summary> + /// <param name="state">The Action delegate to invoke.</param> + private static void RunAction(object state) { ((Action)state)(); } + + /// <summary>Ends the await operation.</summary> + public void GetResult() {} // Nop. It exists purely because the compiler pattern demands it. + } + } +} diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs b/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs new file mode 100644 index 0000000000..8ee50da290 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +// JitHelpers +// Low-level Jit Helpers +//////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Threading; +using System.Runtime; +using System.Runtime.Versioning; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Runtime.CompilerServices { + + // Wrapper for address of a string variable on stack + internal struct StringHandleOnStack + { + private IntPtr m_ptr; + + internal StringHandleOnStack(IntPtr pString) + { + m_ptr = pString; + } + } + + // Wrapper for address of a object variable on stack + internal struct ObjectHandleOnStack + { + private IntPtr m_ptr; + + internal ObjectHandleOnStack(IntPtr pObject) + { + m_ptr = pObject; + } + } + + // Wrapper for StackCrawlMark + internal struct StackCrawlMarkHandle + { + private IntPtr m_ptr; + + internal StackCrawlMarkHandle(IntPtr stackMark) + { + m_ptr = stackMark; + } + } + + // Helper class to assist with unsafe pinning of arbitrary objects. The typical usage pattern is: + // fixed (byte * pData = &JitHelpers.GetPinningHelper(value).m_data) + // { + // ... pData is what Object::GetData() returns in VM ... + // } + internal class PinningHelper + { + public byte m_data; + } + + [FriendAccessAllowed] + internal static class JitHelpers + { + // The special dll name to be used for DllImport of QCalls + internal const string QCall = "QCall"; + + // Wraps object variable into a handle. Used to return managed strings from QCalls. + // s has to be a local variable on the stack. + [SecurityCritical] + static internal StringHandleOnStack GetStringHandleOnStack(ref string s) + { + return new StringHandleOnStack(UnsafeCastToStackPointer(ref s)); + } + + // Wraps object variable into a handle. Used to pass managed object references in and out of QCalls. + // o has to be a local variable on the stack. + [SecurityCritical] + static internal ObjectHandleOnStack GetObjectHandleOnStack<T>(ref T o) where T : class + { + return new ObjectHandleOnStack(UnsafeCastToStackPointer(ref o)); + } + + // Wraps StackCrawlMark into a handle. Used to pass StackCrawlMark to QCalls. + // stackMark has to be a local variable on the stack. + [SecurityCritical] + static internal StackCrawlMarkHandle GetStackCrawlMarkHandle(ref StackCrawlMark stackMark) + { + return new StackCrawlMarkHandle(UnsafeCastToStackPointer(ref stackMark)); + } + +#if _DEBUG + [SecurityCritical] + [FriendAccessAllowed] + static internal T UnsafeCast<T>(Object o) where T : class + { + T ret = UnsafeCastInternal<T>(o); + Contract.Assert(ret == (o as T), "Invalid use of JitHelpers.UnsafeCast!"); + return ret; + } + + // The IL body of this method is not critical, but its body will be replaced with unsafe code, so + // this method is effectively critical + [SecurityCritical] + static private T UnsafeCastInternal<T>(Object o) where T : class + { + // The body of this function will be replaced by the EE with unsafe code that just returns o!!! + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } + + static internal int UnsafeEnumCast<T>(T val) where T : struct // Actually T must be 4 byte (or less) enum + { + Contract.Assert(typeof(T).IsEnum + && (Enum.GetUnderlyingType(typeof(T)) == typeof(int) + || Enum.GetUnderlyingType(typeof(T)) == typeof(uint) + || Enum.GetUnderlyingType(typeof(T)) == typeof(short) + || Enum.GetUnderlyingType(typeof(T)) == typeof(ushort) + || Enum.GetUnderlyingType(typeof(T)) == typeof(byte) + || Enum.GetUnderlyingType(typeof(T)) == typeof(sbyte)), + "Error, T must be an 4 byte (or less) enum JitHelpers.UnsafeEnumCast!"); + return UnsafeEnumCastInternal<T>(val); + } + + static private int UnsafeEnumCastInternal<T>(T val) where T : struct // Actually T must be 4 (or less) byte enum + { + // should be return (int) val; but C# does not allow, runtime does this magically + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } + + static internal long UnsafeEnumCastLong<T>(T val) where T : struct // Actually T must be 8 byte enum + { + Contract.Assert(typeof(T).IsEnum + && (Enum.GetUnderlyingType(typeof(T)) == typeof(long) + || Enum.GetUnderlyingType(typeof(T)) == typeof(ulong)), + "Error, T must be an 8 byte enum JitHelpers.UnsafeEnumCastLong!"); + return UnsafeEnumCastLongInternal<T>(val); + } + + static private long UnsafeEnumCastLongInternal<T>(T val) where T : struct // Actually T must be 8 byte enum + { + // should be return (int) val; but C# does not allow, runtime does this magically + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } + + // Internal method for getting a raw pointer for handles in JitHelpers. + // The reference has to point into a local stack variable in order so it can not be moved by the GC. + [SecurityCritical] + static internal IntPtr UnsafeCastToStackPointer<T>(ref T val) + { + IntPtr p = UnsafeCastToStackPointerInternal<T>(ref val); + Contract.Assert(IsAddressInStack(p), "Pointer not in the stack!"); + return p; + } + + [SecurityCritical] + static private IntPtr UnsafeCastToStackPointerInternal<T>(ref T val) + { + // The body of this function will be replaced by the EE with unsafe code that just returns val!!! + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } +#else // _DEBUG + // The IL body of this method is not critical, but its body will be replaced with unsafe code, so + // this method is effectively critical + [SecurityCritical] + [FriendAccessAllowed] + static internal T UnsafeCast<T>(Object o) where T : class + { + // The body of this function will be replaced by the EE with unsafe code that just returns o!!! + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } + + static internal int UnsafeEnumCast<T>(T val) where T : struct // Actually T must be 4 byte (or less) enum + { + // should be return (int) val; but C# does not allow, runtime does this magically + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } + + static internal long UnsafeEnumCastLong<T>(T val) where T : struct // Actually T must be 8 byte enum + { + // should be return (long) val; but C# does not allow, runtime does this magically + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } + + [SecurityCritical] + static internal IntPtr UnsafeCastToStackPointer<T>(ref T val) + { + // The body of this function will be replaced by the EE with unsafe code that just returns o!!! + // See getILIntrinsicImplementation for how this happens. + throw new InvalidOperationException(); + } +#endif // _DEBUG + + // Set the given element in the array without any type or range checks + [SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern static internal void UnsafeSetArrayElement(Object[] target, int index, Object element); + + // Used for unsafe pinning of arbitrary objects. + [System.Security.SecurityCritical] // auto-generated + static internal PinningHelper GetPinningHelper(Object o) + { + // This cast is really unsafe - call the private version that does not assert in debug +#if _DEBUG + return UnsafeCastInternal<PinningHelper>(o); +#else + return UnsafeCast<PinningHelper>(o); +#endif + } + +#if _DEBUG + [SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern static bool IsAddressInStack(IntPtr ptr); +#endif + } +} diff --git a/src/mscorlib/src/System/Runtime/ExceptionServices/CorruptingExceptionCommon.cs b/src/mscorlib/src/System/Runtime/ExceptionServices/CorruptingExceptionCommon.cs new file mode 100644 index 0000000000..e12df10462 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/ExceptionServices/CorruptingExceptionCommon.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#if FEATURE_CORRUPTING_EXCEPTIONS + +/*============================================================================= +** +** +** +** Purpose: Contains common usage support entities for Corrupting Exceptions +** +** Created: 06/20/2008 +** +** +** +=============================================================================*/ + +namespace System.Runtime.ExceptionServices { + using System; + + // This attribute can be applied to methods to indicate that ProcessCorruptedState + // Exceptions should be delivered to them. + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class HandleProcessCorruptedStateExceptionsAttribute : Attribute + { + public HandleProcessCorruptedStateExceptionsAttribute() + { + } + } +} +#endif // FEATURE_CORRUPTING_EXCEPTIONS diff --git a/src/mscorlib/src/System/Runtime/ExceptionServices/ExceptionServicesCommon.cs b/src/mscorlib/src/System/Runtime/ExceptionServices/ExceptionServicesCommon.cs new file mode 100644 index 0000000000..7251d901cd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/ExceptionServices/ExceptionServicesCommon.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Contains common usage support entities for advanced exception +** handling/processing scenarios. +** +** Created: 11/2/2010 +** +** +** +=============================================================================*/ + +#if FEATURE_EXCEPTIONDISPATCHINFO +namespace System.Runtime.ExceptionServices { + using System; + + // This class defines support for seperating the exception dispatch details + // (like stack trace, watson buckets, etc) from the actual managed exception + // object. This allows us to track error (via the exception object) independent + // of the path the error takes. + // + // This is particularly useful for frameworks like PFX, APM, etc that wish to + // propagate exceptions (i.e. errors to be precise) across threads. + public sealed class ExceptionDispatchInfo + { + // Private members that will hold the relevant details. + private Exception m_Exception; + private string m_remoteStackTrace; + private object m_stackTrace; + private object m_dynamicMethods; + private UIntPtr m_IPForWatsonBuckets; + private Object m_WatsonBuckets; + + private ExceptionDispatchInfo(Exception exception) + { + // Copy over the details we need to save. + m_Exception = exception; + m_remoteStackTrace = exception.RemoteStackTrace; + + // NOTE: don't be tempted to pass the fields for the out params; the containing object + // might be relocated during the call so the pointers will no longer be valid. + object stackTrace; + object dynamicMethods; + m_Exception.GetStackTracesDeepCopy(out stackTrace, out dynamicMethods); + m_stackTrace = stackTrace; + m_dynamicMethods = dynamicMethods; + + m_IPForWatsonBuckets = exception.IPForWatsonBuckets; + m_WatsonBuckets = exception.WatsonBuckets; + } + + internal UIntPtr IPForWatsonBuckets + { + get + { + return m_IPForWatsonBuckets; + } + } + + internal object WatsonBuckets + { + get + { + return m_WatsonBuckets; + } + } + + internal object BinaryStackTraceArray + { + get + { + return m_stackTrace; + } + } + + internal object DynamicMethodArray + { + get + { + return m_dynamicMethods; + } + } + + internal string RemoteStackTrace + { + get + { + return m_remoteStackTrace; + } + } + + // This static method is used to create an instance of ExceptionDispatchInfo for + // the specified exception object and save all the required details that maybe + // needed to be propagated when the exception is "rethrown" on a different thread. + public static ExceptionDispatchInfo Capture(Exception source) + { + if (source == null) + { + throw new ArgumentNullException("source", Environment.GetResourceString("ArgumentNull_Obj")); + } + + return new ExceptionDispatchInfo(source); + } + + // Return the exception object represented by this ExceptionDispatchInfo instance + public Exception SourceException + { + + get + { + return m_Exception; + } + } + + // When a framework needs to "Rethrow" an exception on a thread different (but not necessarily so) from + // where it was thrown, it should invoke this method against the ExceptionDispatchInfo (EDI) + // created for the exception in question. + // + // This method will restore the original stack trace and bucketing details before throwing + // the exception so that it is easy, from debugging standpoint, to understand what really went wrong on + // the original thread. + public void Throw() + { + // Restore the exception dispatch details before throwing the exception. + m_Exception.RestoreExceptionDispatchInfo(this); + throw m_Exception; + } + } +} +#endif // FEATURE_EXCEPTIONDISPATCHINFO diff --git a/src/mscorlib/src/System/Runtime/GcSettings.cs b/src/mscorlib/src/System/Runtime/GcSettings.cs new file mode 100644 index 0000000000..5b4be27757 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/GcSettings.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace System.Runtime { + using System; + using System.Runtime.CompilerServices; + using System.Runtime.ConstrainedExecution; + using System.Security.Permissions; + using System.Diagnostics.Contracts; + + // These settings are the same format as in clr\src\vm\gcpriv.h + // make sure you change that file if you change this file! + + [Serializable] + public enum GCLargeObjectHeapCompactionMode + { + Default = 1, + CompactOnce = 2 + } + + [Serializable] + public enum GCLatencyMode + { + Batch = 0, + Interactive = 1, + LowLatency = 2, + SustainedLowLatency = 3, + NoGCRegion = 4 + } + + public static class GCSettings + { + enum SetLatencyModeStatus + { + Succeeded = 0, + NoGCInProgress = 1 // NoGCRegion is in progress, can't change pause mode. + }; + + public static GCLatencyMode LatencyMode + { + [System.Security.SecuritySafeCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get + { + return (GCLatencyMode)(GC.GetGCLatencyMode()); + } + + // We don't want to allow this API when hosted. + [System.Security.SecurityCritical] // auto-generated_required + [HostProtection(MayLeakOnAbort = true)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + set + { + if ((value < GCLatencyMode.Batch) || (value > GCLatencyMode.SustainedLowLatency)) + { + throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_Enum")); + } + Contract.EndContractBlock(); + + if (GC.SetGCLatencyMode((int)value) == (int)SetLatencyModeStatus.NoGCInProgress) + throw new InvalidOperationException("The NoGCRegion mode is in progress. End it and then set a different mode."); + } + } + + public static GCLargeObjectHeapCompactionMode LargeObjectHeapCompactionMode + { + [System.Security.SecuritySafeCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get + { + return (GCLargeObjectHeapCompactionMode)(GC.GetLOHCompactionMode()); + } + + // We don't want to allow this API when hosted. + [System.Security.SecurityCritical] // auto-generated_required + [HostProtection(MayLeakOnAbort = true)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + set + { + if ((value < GCLargeObjectHeapCompactionMode.Default) || + (value > GCLargeObjectHeapCompactionMode.CompactOnce)) + { + throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_Enum")); + } + Contract.EndContractBlock(); + + GC.SetLOHCompactionMode((int)value); + } + } + + public static bool IsServerGC + { + [System.Security.SecuritySafeCritical] // auto-generated + get { + return GC.IsServerGC(); + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ArrayWithOffset.cs b/src/mscorlib/src/System/Runtime/InteropServices/ArrayWithOffset.cs new file mode 100644 index 0000000000..83eae1c59c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ArrayWithOffset.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices { + + using System; + using System.Runtime.CompilerServices; + using System.Runtime.Versioning; + + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + public struct ArrayWithOffset + { + //private ArrayWithOffset() + //{ + // throw new Exception(); + //} + + [System.Security.SecuritySafeCritical] // auto-generated + public ArrayWithOffset(Object array, int offset) + { + m_array = array; + m_offset = offset; + m_count = 0; + m_count = CalculateCount(); + } + + public Object GetArray() + { + return m_array; + } + + public int GetOffset() + { + return m_offset; + } + + public override int GetHashCode() + { + return m_count + m_offset; + } + + public override bool Equals(Object obj) + { + if (obj is ArrayWithOffset) + return Equals((ArrayWithOffset)obj); + else + return false; + } + + public bool Equals(ArrayWithOffset obj) + { + return obj.m_array == m_array && obj.m_offset == m_offset && obj.m_count == m_count; + } + + public static bool operator ==(ArrayWithOffset a, ArrayWithOffset b) + { + return a.Equals(b); + } + + public static bool operator !=(ArrayWithOffset a, ArrayWithOffset b) + { + return !(a == b); + } + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private extern int CalculateCount(); + + private Object m_array; + private int m_offset; + private int m_count; + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs b/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs new file mode 100644 index 0000000000..06c963a555 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs @@ -0,0 +1,1138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices{ + + using System; + using System.Reflection; + using System.Diagnostics.Contracts; + + [AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class UnmanagedFunctionPointerAttribute : Attribute + { + CallingConvention m_callingConvention; + + public UnmanagedFunctionPointerAttribute(CallingConvention callingConvention) { m_callingConvention = callingConvention; } + + public CallingConvention CallingConvention { get { return m_callingConvention; } } + + public CharSet CharSet; + public bool BestFitMapping; + public bool ThrowOnUnmappableChar; + + // This field is ignored and marshaling behaves as if it was true (for historical reasons). + public bool SetLastError; + + // P/Invoke via delegate always preserves signature, HRESULT swapping is not supported. + //public bool PreserveSig; + } + + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(false)] + public sealed class TypeIdentifierAttribute : Attribute + { + public TypeIdentifierAttribute() { } + public TypeIdentifierAttribute(string scope, string identifier) { Scope_ = scope; Identifier_ = identifier; } + + public String Scope { get { return Scope_; } } + public String Identifier { get { return Identifier_; } } + + internal String Scope_; + internal String Identifier_; + } + + // To be used on methods that sink reverse P/Invoke calls. + // This attribute is a CoreCLR-only security measure, currently ignored by the desktop CLR. + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class AllowReversePInvokeCallsAttribute : Attribute + { + public AllowReversePInvokeCallsAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class DispIdAttribute : Attribute + { + internal int _val; + public DispIdAttribute(int dispId) + { + _val = dispId; + } + public int Value { get { return _val; } } + } + + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + public enum ComInterfaceType + { + InterfaceIsDual = 0, + InterfaceIsIUnknown = 1, + InterfaceIsIDispatch = 2, + + [System.Runtime.InteropServices.ComVisible(false)] + InterfaceIsIInspectable = 3, + } + + [AttributeUsage(AttributeTargets.Interface, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class InterfaceTypeAttribute : Attribute + { + internal ComInterfaceType _val; + public InterfaceTypeAttribute(ComInterfaceType interfaceType) + { + _val = interfaceType; + } + public InterfaceTypeAttribute(short interfaceType) + { + _val = (ComInterfaceType)interfaceType; + } + public ComInterfaceType Value { get { return _val; } } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComDefaultInterfaceAttribute : Attribute + { + internal Type _val; + + public ComDefaultInterfaceAttribute(Type defaultInterface) + { + _val = defaultInterface; + } + + public Type Value { get { return _val; } } + } + + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + public enum ClassInterfaceType + { + None = 0, + AutoDispatch = 1, + AutoDual = 2 + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ClassInterfaceAttribute : Attribute + { + internal ClassInterfaceType _val; + public ClassInterfaceAttribute(ClassInterfaceType classInterfaceType) + { + _val = classInterfaceType; + + } + public ClassInterfaceAttribute(short classInterfaceType) + { + _val = (ClassInterfaceType)classInterfaceType; + } + public ClassInterfaceType Value { get { return _val; } } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComVisibleAttribute : Attribute + { + internal bool _val; + public ComVisibleAttribute(bool visibility) + { + _val = visibility; + } + public bool Value { get { return _val; } } + } + + [AttributeUsage(AttributeTargets.Interface, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibImportClassAttribute : Attribute + { + internal String _importClassName; + public TypeLibImportClassAttribute(Type importClass) + { + _importClassName = importClass.ToString(); + } + public String Value { get { return _importClassName; } } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class LCIDConversionAttribute : Attribute + { + internal int _val; + public LCIDConversionAttribute(int lcid) + { + _val = lcid; + } + public int Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComRegisterFunctionAttribute : Attribute + { + public ComRegisterFunctionAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComUnregisterFunctionAttribute : Attribute + { + public ComUnregisterFunctionAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ProgIdAttribute : Attribute + { + internal String _val; + public ProgIdAttribute(String progId) + { + _val = progId; + } + public String Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ImportedFromTypeLibAttribute : Attribute + { + internal String _val; + public ImportedFromTypeLibAttribute(String tlbFile) + { + _val = tlbFile; + } + public String Value { get {return _val;} } + } + + [Obsolete("The IDispatchImplAttribute is deprecated.", false)] + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + public enum IDispatchImplType + { + SystemDefinedImpl = 0, + InternalImpl = 1, + CompatibleImpl = 2, + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly, Inherited = false)] + [Obsolete("This attribute is deprecated and will be removed in a future version.", false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class IDispatchImplAttribute : Attribute + { + internal IDispatchImplType _val; + public IDispatchImplAttribute(IDispatchImplType implType) + { + _val = implType; + } + public IDispatchImplAttribute(short implType) + { + _val = (IDispatchImplType)implType; + } + public IDispatchImplType Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComSourceInterfacesAttribute : Attribute + { + internal String _val; + public ComSourceInterfacesAttribute(String sourceInterfaces) + { + _val = sourceInterfaces; + } + public ComSourceInterfacesAttribute(Type sourceInterface) + { + _val = sourceInterface.FullName; + } + public ComSourceInterfacesAttribute(Type sourceInterface1, Type sourceInterface2) + { + _val = sourceInterface1.FullName + "\0" + sourceInterface2.FullName; + } + public ComSourceInterfacesAttribute(Type sourceInterface1, Type sourceInterface2, Type sourceInterface3) + { + _val = sourceInterface1.FullName + "\0" + sourceInterface2.FullName + "\0" + sourceInterface3.FullName; + } + public ComSourceInterfacesAttribute(Type sourceInterface1, Type sourceInterface2, Type sourceInterface3, Type sourceInterface4) + { + _val = sourceInterface1.FullName + "\0" + sourceInterface2.FullName + "\0" + sourceInterface3.FullName + "\0" + sourceInterface4.FullName; + } + public String Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.All, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComConversionLossAttribute : Attribute + { + public ComConversionLossAttribute() + { + } + } + +[Serializable] +[Flags()] + [System.Runtime.InteropServices.ComVisible(true)] + public enum TypeLibTypeFlags + { + FAppObject = 0x0001, + FCanCreate = 0x0002, + FLicensed = 0x0004, + FPreDeclId = 0x0008, + FHidden = 0x0010, + FControl = 0x0020, + FDual = 0x0040, + FNonExtensible = 0x0080, + FOleAutomation = 0x0100, + FRestricted = 0x0200, + FAggregatable = 0x0400, + FReplaceable = 0x0800, + FDispatchable = 0x1000, + FReverseBind = 0x2000, + } + +[Serializable] +[Flags()] + [System.Runtime.InteropServices.ComVisible(true)] + public enum TypeLibFuncFlags + { + FRestricted = 0x0001, + FSource = 0x0002, + FBindable = 0x0004, + FRequestEdit = 0x0008, + FDisplayBind = 0x0010, + FDefaultBind = 0x0020, + FHidden = 0x0040, + FUsesGetLastError = 0x0080, + FDefaultCollelem = 0x0100, + FUiDefault = 0x0200, + FNonBrowsable = 0x0400, + FReplaceable = 0x0800, + FImmediateBind = 0x1000, + } + +[Serializable] +[Flags()] + [System.Runtime.InteropServices.ComVisible(true)] + public enum TypeLibVarFlags + { + FReadOnly = 0x0001, + FSource = 0x0002, + FBindable = 0x0004, + FRequestEdit = 0x0008, + FDisplayBind = 0x0010, + FDefaultBind = 0x0020, + FHidden = 0x0040, + FRestricted = 0x0080, + FDefaultCollelem = 0x0100, + FUiDefault = 0x0200, + FNonBrowsable = 0x0400, + FReplaceable = 0x0800, + FImmediateBind = 0x1000, + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Struct, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibTypeAttribute : Attribute + { + internal TypeLibTypeFlags _val; + public TypeLibTypeAttribute(TypeLibTypeFlags flags) + { + _val = flags; + } + public TypeLibTypeAttribute(short flags) + { + _val = (TypeLibTypeFlags)flags; + } + public TypeLibTypeFlags Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibFuncAttribute : Attribute + { + internal TypeLibFuncFlags _val; + public TypeLibFuncAttribute(TypeLibFuncFlags flags) + { + _val = flags; + } + public TypeLibFuncAttribute(short flags) + { + _val = (TypeLibFuncFlags)flags; + } + public TypeLibFuncFlags Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibVarAttribute : Attribute + { + internal TypeLibVarFlags _val; + public TypeLibVarAttribute(TypeLibVarFlags flags) + { + _val = flags; + } + public TypeLibVarAttribute(short flags) + { + _val = (TypeLibVarFlags)flags; + } + public TypeLibVarFlags Value { get {return _val;} } + } + + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + public enum VarEnum + { + VT_EMPTY = 0, + VT_NULL = 1, + VT_I2 = 2, + VT_I4 = 3, + VT_R4 = 4, + VT_R8 = 5, + VT_CY = 6, + VT_DATE = 7, + VT_BSTR = 8, + VT_DISPATCH = 9, + VT_ERROR = 10, + VT_BOOL = 11, + VT_VARIANT = 12, + VT_UNKNOWN = 13, + VT_DECIMAL = 14, + VT_I1 = 16, + VT_UI1 = 17, + VT_UI2 = 18, + VT_UI4 = 19, + VT_I8 = 20, + VT_UI8 = 21, + VT_INT = 22, + VT_UINT = 23, + VT_VOID = 24, + VT_HRESULT = 25, + VT_PTR = 26, + VT_SAFEARRAY = 27, + VT_CARRAY = 28, + VT_USERDEFINED = 29, + VT_LPSTR = 30, + VT_LPWSTR = 31, + VT_RECORD = 36, + VT_FILETIME = 64, + VT_BLOB = 65, + VT_STREAM = 66, + VT_STORAGE = 67, + VT_STREAMED_OBJECT = 68, + VT_STORED_OBJECT = 69, + VT_BLOB_OBJECT = 70, + VT_CF = 71, + VT_CLSID = 72, + VT_VECTOR = 0x1000, + VT_ARRAY = 0x2000, + VT_BYREF = 0x4000 + } + + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + // Note that this enum should remain in-sync with the CorNativeType enum in corhdr.h + public enum UnmanagedType + { + Bool = 0x2, // 4 byte boolean value (true != 0, false == 0) + + I1 = 0x3, // 1 byte signed value + + U1 = 0x4, // 1 byte unsigned value + + I2 = 0x5, // 2 byte signed value + + U2 = 0x6, // 2 byte unsigned value + + I4 = 0x7, // 4 byte signed value + + U4 = 0x8, // 4 byte unsigned value + + I8 = 0x9, // 8 byte signed value + + U8 = 0xa, // 8 byte unsigned value + + R4 = 0xb, // 4 byte floating point + + R8 = 0xc, // 8 byte floating point + + Currency = 0xf, // A currency + + BStr = 0x13, // OLE Unicode BSTR + + LPStr = 0x14, // Ptr to SBCS string + + LPWStr = 0x15, // Ptr to Unicode string + + LPTStr = 0x16, // Ptr to OS preferred (SBCS/Unicode) string + + ByValTStr = 0x17, // OS preferred (SBCS/Unicode) inline string (only valid in structs) + + IUnknown = 0x19, // COM IUnknown pointer. + + IDispatch = 0x1a, // COM IDispatch pointer + + Struct = 0x1b, // Structure + + Interface = 0x1c, // COM interface + + SafeArray = 0x1d, // OLE SafeArray + + ByValArray = 0x1e, // Array of fixed size (only valid in structs) + + SysInt = 0x1f, // Hardware natural sized signed integer + + SysUInt = 0x20, + + VBByRefStr = 0x22, + + AnsiBStr = 0x23, // OLE BSTR containing SBCS characters + + TBStr = 0x24, // Ptr to OS preferred (SBCS/Unicode) BSTR + + VariantBool = 0x25, // OLE defined BOOLEAN (2 bytes, true == -1, false == 0) + + FunctionPtr = 0x26, // Function pointer + + AsAny = 0x28, // Paired with Object type and does runtime marshalling determination + + LPArray = 0x2a, // C style array + + LPStruct = 0x2b, // Pointer to a structure + + CustomMarshaler = 0x2c, + + Error = 0x2d, + + [System.Runtime.InteropServices.ComVisible(false)] + IInspectable = 0x2e, + + [System.Runtime.InteropServices.ComVisible(false)] + HString = 0x2f, // Windows Runtime HSTRING + + [System.Runtime.InteropServices.ComVisible(false)] + LPUTF8Str = 0x30, // UTF8 string + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public unsafe sealed class MarshalAsAttribute : Attribute + { + [System.Security.SecurityCritical] // auto-generated + internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter) + { + return GetCustomAttribute(parameter.MetadataToken, parameter.GetRuntimeModule()); + } + + [System.Security.SecurityCritical] // auto-generated + internal static bool IsDefined(RuntimeParameterInfo parameter) + { + return GetCustomAttribute(parameter) != null; + } + + [System.Security.SecurityCritical] // auto-generated + internal static Attribute GetCustomAttribute(RuntimeFieldInfo field) + { + return GetCustomAttribute(field.MetadataToken, field.GetRuntimeModule()); ; + } + + [System.Security.SecurityCritical] // auto-generated + internal static bool IsDefined(RuntimeFieldInfo field) + { + return GetCustomAttribute(field) != null; + } + + [System.Security.SecurityCritical] // auto-generated + internal static Attribute GetCustomAttribute(int token, RuntimeModule scope) + { + UnmanagedType unmanagedType, arraySubType; + VarEnum safeArraySubType; + int sizeParamIndex = 0, sizeConst = 0; + string marshalTypeName = null, marshalCookie = null, safeArrayUserDefinedTypeName = null; + int iidParamIndex = 0; + ConstArray nativeType = ModuleHandle.GetMetadataImport(scope.GetNativeHandle()).GetFieldMarshal(token); + + if (nativeType.Length == 0) + return null; + + MetadataImport.GetMarshalAs(nativeType, + out unmanagedType, out safeArraySubType, out safeArrayUserDefinedTypeName, out arraySubType, out sizeParamIndex, + out sizeConst, out marshalTypeName, out marshalCookie, out iidParamIndex); + + RuntimeType safeArrayUserDefinedType = safeArrayUserDefinedTypeName == null || safeArrayUserDefinedTypeName.Length == 0 ? null : + RuntimeTypeHandle.GetTypeByNameUsingCARules(safeArrayUserDefinedTypeName, scope); + RuntimeType marshalTypeRef = null; + + try + { + marshalTypeRef = marshalTypeName == null ? null : RuntimeTypeHandle.GetTypeByNameUsingCARules(marshalTypeName, scope); + } + catch (System.TypeLoadException) + { + // The user may have supplied a bad type name string causing this TypeLoadException + // Regardless, we return the bad type name + Contract.Assert(marshalTypeName != null); + } + + return new MarshalAsAttribute( + unmanagedType, safeArraySubType, safeArrayUserDefinedType, arraySubType, + (short)sizeParamIndex, sizeConst, marshalTypeName, marshalTypeRef, marshalCookie, iidParamIndex); + } + + internal MarshalAsAttribute(UnmanagedType val, VarEnum safeArraySubType, RuntimeType safeArrayUserDefinedSubType, UnmanagedType arraySubType, + short sizeParamIndex, int sizeConst, string marshalType, RuntimeType marshalTypeRef, string marshalCookie, int iidParamIndex) + { + _val = val; + SafeArraySubType = safeArraySubType; + SafeArrayUserDefinedSubType = safeArrayUserDefinedSubType; + IidParameterIndex = iidParamIndex; + ArraySubType = arraySubType; + SizeParamIndex = sizeParamIndex; + SizeConst = sizeConst; + MarshalType = marshalType; + MarshalTypeRef = marshalTypeRef; + MarshalCookie = marshalCookie; + } + + internal UnmanagedType _val; + public MarshalAsAttribute(UnmanagedType unmanagedType) + { + _val = unmanagedType; + } + public MarshalAsAttribute(short unmanagedType) + { + _val = (UnmanagedType)unmanagedType; + } + public UnmanagedType Value { get { return _val; } } + + // Fields used with SubType = SafeArray. + public VarEnum SafeArraySubType; + public Type SafeArrayUserDefinedSubType; + + // Field used with iid_is attribute (interface pointers). + public int IidParameterIndex; + + // Fields used with SubType = ByValArray and LPArray. + // Array size = parameter(PI) * PM + C + public UnmanagedType ArraySubType; + public short SizeParamIndex; // param index PI + public int SizeConst; // constant C + + // Fields used with SubType = CustomMarshaler + [System.Runtime.InteropServices.ComVisible(true)] + public String MarshalType; // Name of marshaler class + [System.Runtime.InteropServices.ComVisible(true)] + public Type MarshalTypeRef; // Type of marshaler class + public String MarshalCookie; // cookie to pass to marshaler + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComImportAttribute : Attribute + { + internal static Attribute GetCustomAttribute(RuntimeType type) + { + if ((type.Attributes & TypeAttributes.Import) == 0) + return null; + + return new ComImportAttribute(); + } + + internal static bool IsDefined(RuntimeType type) + { + return (type.Attributes & TypeAttributes.Import) != 0; + } + + public ComImportAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class GuidAttribute : Attribute + { + internal String _val; + public GuidAttribute(String guid) + { + _val = guid; + } + public String Value { get { return _val; } } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class PreserveSigAttribute : Attribute + { + internal static Attribute GetCustomAttribute(RuntimeMethodInfo method) + { + if ((method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) == 0) + return null; + + return new PreserveSigAttribute(); + } + + internal static bool IsDefined(RuntimeMethodInfo method) + { + return (method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0; + } + + public PreserveSigAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class InAttribute : Attribute + { + internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter) + { + return parameter.IsIn ? new InAttribute() : null; + } + internal static bool IsDefined(RuntimeParameterInfo parameter) + { + return parameter.IsIn; + } + + public InAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class OutAttribute : Attribute + { + internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter) + { + return parameter.IsOut ? new OutAttribute() : null; + } + internal static bool IsDefined(RuntimeParameterInfo parameter) + { + return parameter.IsOut; + } + + public OutAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class OptionalAttribute : Attribute + { + internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter) + { + return parameter.IsOptional ? new OptionalAttribute() : null; + } + internal static bool IsDefined(RuntimeParameterInfo parameter) + { + return parameter.IsOptional; + } + + public OptionalAttribute() + { + } + } + + [Flags] + public enum DllImportSearchPath + { + UseDllDirectoryForDependencies = 0x100, + ApplicationDirectory = 0x200, + UserDirectories = 0x400, + System32 = 0x800, + SafeDirectories = 0x1000, + AssemblyDirectory = 0x2, + LegacyBehavior = 0x0 + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)] + [System.Runtime.InteropServices.ComVisible(false)] + public sealed class DefaultDllImportSearchPathsAttribute : Attribute + { + internal DllImportSearchPath _paths; + public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) + { + _paths = paths; + } + + public DllImportSearchPath Paths { get { return _paths; } } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public unsafe sealed class DllImportAttribute : Attribute + { + [System.Security.SecurityCritical] // auto-generated + internal static Attribute GetCustomAttribute(RuntimeMethodInfo method) + { + if ((method.Attributes & MethodAttributes.PinvokeImpl) == 0) + return null; + + MetadataImport scope = ModuleHandle.GetMetadataImport(method.Module.ModuleHandle.GetRuntimeModule()); + string entryPoint, dllName = null; + int token = method.MetadataToken; + PInvokeAttributes flags = 0; + + scope.GetPInvokeMap(token, out flags, out entryPoint, out dllName); + + CharSet charSet = CharSet.None; + + switch (flags & PInvokeAttributes.CharSetMask) + { + case PInvokeAttributes.CharSetNotSpec: charSet = CharSet.None; break; + case PInvokeAttributes.CharSetAnsi: charSet = CharSet.Ansi; break; + case PInvokeAttributes.CharSetUnicode: charSet = CharSet.Unicode; break; + case PInvokeAttributes.CharSetAuto: charSet = CharSet.Auto; break; + + // Invalid: default to CharSet.None + default: break; + } + + CallingConvention callingConvention = CallingConvention.Cdecl; + + switch (flags & PInvokeAttributes.CallConvMask) + { + case PInvokeAttributes.CallConvWinapi: callingConvention = CallingConvention.Winapi; break; + case PInvokeAttributes.CallConvCdecl: callingConvention = CallingConvention.Cdecl; break; + case PInvokeAttributes.CallConvStdcall: callingConvention = CallingConvention.StdCall; break; + case PInvokeAttributes.CallConvThiscall: callingConvention = CallingConvention.ThisCall; break; + case PInvokeAttributes.CallConvFastcall: callingConvention = CallingConvention.FastCall; break; + + // Invalid: default to CallingConvention.Cdecl + default: break; + } + + bool exactSpelling = (flags & PInvokeAttributes.NoMangle) != 0; + bool setLastError = (flags & PInvokeAttributes.SupportsLastError) != 0; + bool bestFitMapping = (flags & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled; + bool throwOnUnmappableChar = (flags & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled; + bool preserveSig = (method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0; + + return new DllImportAttribute( + dllName, entryPoint, charSet, exactSpelling, setLastError, preserveSig, + callingConvention, bestFitMapping, throwOnUnmappableChar); + } + + internal static bool IsDefined(RuntimeMethodInfo method) + { + return (method.Attributes & MethodAttributes.PinvokeImpl) != 0; + } + + + internal DllImportAttribute( + string dllName, string entryPoint, CharSet charSet, bool exactSpelling, bool setLastError, bool preserveSig, + CallingConvention callingConvention, bool bestFitMapping, bool throwOnUnmappableChar) + { + _val = dllName; + EntryPoint = entryPoint; + CharSet = charSet; + ExactSpelling = exactSpelling; + SetLastError = setLastError; + PreserveSig = preserveSig; + CallingConvention = callingConvention; + BestFitMapping = bestFitMapping; + ThrowOnUnmappableChar = throwOnUnmappableChar; + } + + internal String _val; + + public DllImportAttribute(String dllName) + { + _val = dllName; + } + public String Value { get { return _val; } } + + public String EntryPoint; + public CharSet CharSet; + public bool SetLastError; + public bool ExactSpelling; + public bool PreserveSig; + public CallingConvention CallingConvention; + public bool BestFitMapping; + public bool ThrowOnUnmappableChar; + + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public unsafe sealed class StructLayoutAttribute : Attribute + { + private const int DEFAULT_PACKING_SIZE = 8; + + [System.Security.SecurityCritical] // auto-generated + internal static Attribute GetCustomAttribute(RuntimeType type) + { + if (!IsDefined(type)) + return null; + + int pack = 0, size = 0; + LayoutKind layoutKind = LayoutKind.Auto; + switch (type.Attributes & TypeAttributes.LayoutMask) + { + case TypeAttributes.ExplicitLayout: layoutKind = LayoutKind.Explicit; break; + case TypeAttributes.AutoLayout: layoutKind = LayoutKind.Auto; break; + case TypeAttributes.SequentialLayout: layoutKind = LayoutKind.Sequential; break; + default: Contract.Assume(false); break; + } + + CharSet charSet = CharSet.None; + switch (type.Attributes & TypeAttributes.StringFormatMask) + { + case TypeAttributes.AnsiClass: charSet = CharSet.Ansi; break; + case TypeAttributes.AutoClass: charSet = CharSet.Auto; break; + case TypeAttributes.UnicodeClass: charSet = CharSet.Unicode; break; + default: Contract.Assume(false); break; + } + type.GetRuntimeModule().MetadataImport.GetClassLayout(type.MetadataToken, out pack, out size); + + // Metadata parameter checking should not have allowed 0 for packing size. + // The runtime later converts a packing size of 0 to 8 so do the same here + // because it's more useful from a user perspective. + if (pack == 0) + pack = DEFAULT_PACKING_SIZE; + + return new StructLayoutAttribute(layoutKind, pack, size, charSet); + } + + internal static bool IsDefined(RuntimeType type) + { + if (type.IsInterface || type.HasElementType || type.IsGenericParameter) + return false; + + return true; + } + + internal LayoutKind _val; + + internal StructLayoutAttribute(LayoutKind layoutKind, int pack, int size, CharSet charSet) + { + _val = layoutKind; + Pack = pack; + Size = size; + CharSet = charSet; + } + + public StructLayoutAttribute(LayoutKind layoutKind) + { + _val = layoutKind; + } + public StructLayoutAttribute(short layoutKind) + { + _val = (LayoutKind)layoutKind; + } + public LayoutKind Value { get { return _val; } } + public int Pack; + public int Size; + public CharSet CharSet; + } + + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public unsafe sealed class FieldOffsetAttribute : Attribute + { + [System.Security.SecurityCritical] // auto-generated + internal static Attribute GetCustomAttribute(RuntimeFieldInfo field) + { + int fieldOffset; + + if (field.DeclaringType != null && + field.GetRuntimeModule().MetadataImport.GetFieldOffset(field.DeclaringType.MetadataToken, field.MetadataToken, out fieldOffset)) + return new FieldOffsetAttribute(fieldOffset); + + return null; + } + + [System.Security.SecurityCritical] // auto-generated + internal static bool IsDefined(RuntimeFieldInfo field) + { + return GetCustomAttribute(field) != null; + } + + internal int _val; + public FieldOffsetAttribute(int offset) + { + _val = offset; + } + public int Value { get { return _val; } } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComAliasNameAttribute : Attribute + { + internal String _val; + public ComAliasNameAttribute(String alias) + { + _val = alias; + } + public String Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class AutomationProxyAttribute : Attribute + { + internal bool _val; + public AutomationProxyAttribute(bool val) + { + _val = val; + } + public bool Value { get {return _val;} } + } + + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class PrimaryInteropAssemblyAttribute : Attribute + { + internal int _major; + internal int _minor; + + public PrimaryInteropAssemblyAttribute(int major, int minor) + { + _major = major; + _minor = minor; + } + + public int MajorVersion { get {return _major;} } + public int MinorVersion { get {return _minor;} } + } + + [AttributeUsage(AttributeTargets.Interface, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class CoClassAttribute : Attribute + { + internal Type _CoClass; + + public CoClassAttribute(Type coClass) + { + _CoClass = coClass; + } + + public Type CoClass { get { return _CoClass; } } + } + + [AttributeUsage(AttributeTargets.Interface, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComEventInterfaceAttribute : Attribute + { + internal Type _SourceInterface; + internal Type _EventProvider; + + public ComEventInterfaceAttribute(Type SourceInterface, Type EventProvider) + { + _SourceInterface = SourceInterface; + _EventProvider = EventProvider; + } + + public Type SourceInterface { get {return _SourceInterface;} } + public Type EventProvider { get {return _EventProvider;} } + } + + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibVersionAttribute : Attribute + { + internal int _major; + internal int _minor; + + public TypeLibVersionAttribute(int major, int minor) + { + _major = major; + _minor = minor; + } + + public int MajorVersion { get {return _major;} } + public int MinorVersion { get {return _minor;} } + } + + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class ComCompatibleVersionAttribute : Attribute + { + internal int _major; + internal int _minor; + internal int _build; + internal int _revision; + + public ComCompatibleVersionAttribute(int major, int minor, int build, int revision) + { + _major = major; + _minor = minor; + _build = build; + _revision = revision; + } + + public int MajorVersion { get {return _major;} } + public int MinorVersion { get {return _minor;} } + public int BuildNumber { get {return _build;} } + public int RevisionNumber { get {return _revision;} } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class BestFitMappingAttribute : Attribute + { + internal bool _bestFitMapping; + + public BestFitMappingAttribute(bool BestFitMapping) + { + _bestFitMapping = BestFitMapping; + } + + public bool BestFitMapping { get { return _bestFitMapping; } } + public bool ThrowOnUnmappableChar; + } + + [AttributeUsage(AttributeTargets.Module, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class DefaultCharSetAttribute : Attribute + { + internal CharSet _CharSet; + + public DefaultCharSetAttribute(CharSet charSet) + { + _CharSet = charSet; + } + + public CharSet CharSet { get { return _CharSet; } } + } + + [Obsolete("This attribute has been deprecated. Application Domains no longer respect Activation Context boundaries in IDispatch calls.", false)] + [AttributeUsage(AttributeTargets.Assembly, Inherited = false)] + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class SetWin32ContextInIDispatchAttribute : Attribute + { + public SetWin32ContextInIDispatchAttribute() + { + } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + [System.Runtime.InteropServices.ComVisible(false)] + public sealed class ManagedToNativeComInteropStubAttribute : Attribute + { + internal Type _classType; + internal String _methodName; + + public ManagedToNativeComInteropStubAttribute(Type classType, String methodName) + { + _classType = classType; + _methodName = methodName; + } + + public Type ClassType { get { return _classType; } } + public String MethodName { get { return _methodName; } } + } + +} + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/BStrWrapper.cs b/src/mscorlib/src/System/Runtime/InteropServices/BStrWrapper.cs new file mode 100644 index 0000000000..58e93a87ea --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/BStrWrapper.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Wrapper that is converted to a variant with VT_BSTR. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Security; + using System.Security.Permissions; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class BStrWrapper + { + [System.Security.SecuritySafeCritical] // auto-generated +#pragma warning disable 618 + [SecurityPermissionAttribute(SecurityAction.Demand,Flags=SecurityPermissionFlag.UnmanagedCode)] +#pragma warning restore 618 + public BStrWrapper(String value) + { + m_WrappedObject = value; + } + + [System.Security.SecuritySafeCritical] // auto-generated +#pragma warning disable 618 + [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] +#pragma warning restore 618 + public BStrWrapper(Object value) + { + m_WrappedObject = (String)value; + } + + public String WrappedObject + { + get + { + return m_WrappedObject; + } + } + + private String m_WrappedObject; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs b/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs new file mode 100644 index 0000000000..95b925c3f4 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Exception class for all errors from COM Interop where we don't +** recognize the HResult. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System; + using System.Runtime.Serialization; + using System.Globalization; + using System.Security; + using Microsoft.Win32; + + // Exception for COM Interop errors where we don't recognize the HResult. + // + [ComVisible(true)] + [Serializable] + public class COMException : ExternalException { + public COMException() + : base(Environment.GetResourceString("Arg_COMException")) + { + SetErrorCode(__HResults.E_FAIL); + } + + public COMException(String message) + : base(message) + { + SetErrorCode(__HResults.E_FAIL); + } + + public COMException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.E_FAIL); + } + + public COMException(String message,int errorCode) + : base(message) { + SetErrorCode(errorCode); + } + + [SecuritySafeCritical] + internal COMException(int hresult) + : base(Win32Native.GetMessage(hresult)) + { + SetErrorCode(hresult); + } + + internal COMException(String message, int hresult, Exception inner) + : base(message, inner) + { + SetErrorCode(hresult); + } + + protected COMException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + + public override String ToString() { + String message = Message; + String s; + String _className = GetType().ToString(); + s = _className + " (0x" + HResult.ToString("X8", CultureInfo.InvariantCulture) + ")"; + + if (!(message == null || message.Length <= 0)) { + s = s + ": " + message; + } + + Exception _innerException = InnerException; + + if (_innerException!=null) { + s = s + " ---> " + _innerException.ToString(); + } + + + if (StackTrace != null) + s += Environment.NewLine + StackTrace; + + return s; + } + + + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/CallingConvention.cs b/src/mscorlib/src/System/Runtime/InteropServices/CallingConvention.cs new file mode 100644 index 0000000000..cf4eb48af0 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/CallingConvention.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices { + + using System; + // Used for the CallingConvention named argument to the DllImport attribute + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public enum CallingConvention + { + Winapi = 1, + Cdecl = 2, + StdCall = 3, + ThisCall = 4, + FastCall = 5, + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/CharSet.cs b/src/mscorlib/src/System/Runtime/InteropServices/CharSet.cs new file mode 100644 index 0000000000..3c0710a558 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/CharSet.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices { + using System; + // Use this in P/Direct function prototypes to specify + // which character set to use when marshalling Strings. + // Using Ansi will marshal the strings as 1 byte char*'s. + // Using Unicode will marshal the strings as 2 byte wchar*'s. + // Generally you probably want to use Auto, which does the + // right thing 99% of the time. + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public enum CharSet + { + None = 1, // User didn't specify how to marshal strings. + Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars. + Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars. + Auto = 4, // Marshal Strings in the right way for the target system. + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs new file mode 100644 index 0000000000..0bf616d94c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +/*============================================================ +** +** +** Purpose: ComEventHelpers APIs allow binding +** managed delegates to COM's connection point based events. +** +**/ +namespace System.Runtime.InteropServices { + // + // #ComEventsFeature + // + // code:#ComEventsFeature defines two public methods allowing to add/remove .NET delegates handling + // events from COM objects. Those methods are defined as part of code:ComEventsHelper static class + // * code:ComEventsHelper.Combine - will create/reuse-an-existing COM event sink and register the + // specified delegate to be raised when corresponding COM event is raised + // * code:ComEventsHelper.Remove + // + // + // To bind an event handler to the COM object you need to provide the following data: + // * rcw - the instance of the COM object you want to bind to + // * iid - Guid of the source interface you want the sink to implement + // * dispid - dispatch identifier of the event on the source interface you are interested in + // * d - delegate to invoked when corresponding COM event is raised. + // + // #ComEventsArchitecture: + // In COM world, events are handled by so-called event sinks. What these are? COM-based Object Models + // (OMs) define "source" interfaces that need to be implemented by the COM clients to receive events. So, + // event sinks are COM objects implementing a source interfaces. Once an event sink is passed to the COM + // server (through a mechanism known as 'binding/advising to connection point'), COM server will be + // calling source interface methods to "fire events" (advising, connection points, firing events etc. - + // is all COM jargon). + // + // There are few interesting obervations about source interfaces. Usually source interfaces are defined + // as 'dispinterface' - meaning that only late-bound invocations on this interface are allowed. Even + // though it is not illegal to use early bound invocations on source interfaces - the practice is + // discouraged because of versioning concerns. + // + // Notice also that each COM server object might define multiple source interfaces and hence have + // multiple connection points (each CP handles exactly one source interface). COM objects that want to + // fire events are required to implement IConnectionPointContainer interface which is used by the COM + // clients to discovery connection poitns - objects implementing IConnectionPoint interface. Once + // connection point is found - clients can bind to it using IConnectionPoint::Advise (see + // code:ComEventsSink.Advise). + // + // The idea behind code:#ComEventsFeature is to write a "universal event sink" COM component that is + // generic enough to handle all late-bound event firings and invoke corresponding COM delegates (through + // reflection). + // + // When delegate is registered (using code:ComEventsHelper.Combine) we will verify we have corresponding + // event sink created and bound. + // + // But what happens when COM events are fired? code:ComEventsSink.Invoke implements IDispatch::Invoke method + // and this is the entry point that is called. Once our event sink is invoked, we need to find the + // corresponding delegate to invoke . We need to match the dispid of the call that is coming in to a + // dispid of .NET delegate that has been registered for this object. Once this is found we do call the + // delegates using reflection (code:ComEventsMethod.Invoke). + // + // #ComEventsArgsMarshalling + // Notice, that we may not have a delegate registered against every method on the source interface. If we + // were to marshal all the input parameters for methods that do not reach user code - we would end up + // generatic RCWs that are not reachable for user code (the inconvenience it might create is there will + // be RCWs that users can not call Marshal.ReleaseComObject on to explicitly manage the lifetime of these + // COM objects). The above behavior was one of the shortcoimings of legacy TLBIMP's implementation of COM + // event sinking. In our code we will not marshal any data if there is no delegate registered to handle + // the event. (code:ComEventsMethod.Invoke) + // + // #ComEventsFinalization: + // Additional area of interest is when COM sink should be unadvised from the connection point. Legacy + // TLBIMP's implementation of COM event sinks will unadvises the sink when corresponding RCW is GCed. + // This is achieved by rooting the event sinks in a finalizable object stored in RCW's property bag + // (using Marshal.SetComObjectData). Hence, once RCW is no longer reachable - the finalizer is called and + // it would unadvise all the event sinks. We are employing the same strategy here. See storing an + // instance in the RCW at code:ComEventsInfo.FromObject and undadvsing the sinks at + // code:ComEventsInfo.~ComEventsInfo + // + // Classes of interest: + // * code:ComEventsHelpers - defines public methods but there are also a number of internal classes that + // implement the actual COM event sink: + // * code:ComEventsInfo - represents a finalizable container for all event sinks for a particular RCW. + // Lifetime of this instance corresponds to the lifetime of the RCW object + // * code:ComEventsSink - represents a single event sink. Maintains an internal pointer to the next + // instance (in a singly linked list). A collection of code:ComEventsSink is stored at + // code:ComEventsInfo._sinks + // * code:ComEventsMethod - represents a single method from the source interface which has .NET delegates + // attached to it. Maintains an internal pointer to the next instance (in a singly linked list). A + // collection of code:ComEventMethod is stored at code:ComEventsSink._methods + // + // #ComEventsRetValIssue: + // Issue: normally, COM events would not return any value. However, it may happen as described in + // http://support.microsoft.com/kb/810228. Such design might represent a problem for us - e.g. what is + // the return value of a chain of delegates - is it the value of the last call in the chain or the the + // first one? As the above KB article indicates, in cases where OM has events returning values, it is + // suggested that people implement their event sink by explicitly implementing the source interface. This + // means that the problem is already quite complex and we should not be dealing with it - see + // code:ComEventsMethod.Invoke + + using System; + using System.Runtime.Remoting; + + /// <summary> + /// The static methods provided in ComEventsHelper allow using .NET delegates to subscribe to events + /// raised COM objects. + /// </summary> + public static class ComEventsHelper { + + /// <summary> + /// Adds a delegate to the invocation list of events originating from the COM object. + /// </summary> + /// <param name="rcw">COM object firing the events the caller would like to respond to</param> + /// <param name="iid">identifier of the source interface used by COM object to fire events</param> + /// <param name="dispid">dispatch identifier of the method on the source interface</param> + /// <param name="d">delegate to invoke when specifed COM event is fired</param> + [System.Security.SecurityCritical] + public static void Combine(object rcw, Guid iid, int dispid, System.Delegate d) { + + rcw = UnwrapIfTransparentProxy(rcw); + + lock (rcw) { + ComEventsInfo eventsInfo = ComEventsInfo.FromObject(rcw); + + ComEventsSink sink = eventsInfo.FindSink(ref iid); + if (sink == null) { + sink = eventsInfo.AddSink(ref iid); + } + + + ComEventsMethod method = sink.FindMethod(dispid); + if (method == null) { + method = sink.AddMethod(dispid); + } + + method.AddDelegate(d); + } + } + + /// <summary> + /// Removes a delegate from the invocation list of events originating from the COM object. + /// </summary> + /// <param name="rcw">COM object the delegate is attached to</param> + /// <param name="iid">identifier of the source interface used by COM object to fire events</param> + /// <param name="dispid">dispatch identifier of the method on the source interface</param> + /// <param name="d">delegate to remove from the invocation list</param> + /// <returns></returns> + [System.Security.SecurityCritical] + public static Delegate Remove(object rcw, Guid iid, int dispid, System.Delegate d) { + + rcw = UnwrapIfTransparentProxy(rcw); + + lock (rcw) { + + ComEventsInfo eventsInfo = ComEventsInfo.Find(rcw); + if (eventsInfo == null) + return null; + ComEventsSink sink = eventsInfo.FindSink(ref iid); + if (sink == null) + return null; + ComEventsMethod method = sink.FindMethod(dispid); + if (method == null) + return null; + + method.RemoveDelegate(d); + + if (method.Empty) { + // removed the last event handler for this dispid - need to remove dispid handler + method = sink.RemoveMethod(method); + } + if (method == null) { + // removed last dispid handler for this sink - need to remove the sink + sink = eventsInfo.RemoveSink(sink); + } + if (sink == null) { + // removed last sink for this rcw - need to remove all traces of event info + Marshal.SetComObjectData(rcw, typeof(ComEventsInfo), null); + GC.SuppressFinalize(eventsInfo); + } + + return d; + } + } + + [System.Security.SecurityCritical] + internal static object UnwrapIfTransparentProxy(object rcw) { +#if FEATURE_REMOTING + if (RemotingServices.IsTransparentProxy(rcw)) { + IntPtr punk = Marshal.GetIUnknownForObject(rcw); + try { + rcw = Marshal.GetObjectForIUnknown(punk); + } finally { + Marshal.Release(punk); + } + } +#endif + return rcw; + } + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComEventsInfo.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsInfo.cs new file mode 100644 index 0000000000..6feb52445d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsInfo.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +/*============================================================ +** +** +** Purpose: part of ComEventHelpers APIs which allow binding +** managed delegates to COM's connection point based events. +** +**/ + +namespace System.Runtime.InteropServices { + + using System; + using ComTypes = System.Runtime.InteropServices.ComTypes; + + // see code:ComEventsHelper#ComEventsArchitecture + [System.Security.SecurityCritical] + internal class ComEventsInfo { + + +#region fields + + private ComEventsSink _sinks; + private object _rcw; + +#endregion + + +#region ctor/dtor + + ComEventsInfo(object rcw) { + _rcw = rcw; + } + + [System.Security.SecuritySafeCritical] + ~ComEventsInfo() { + // see code:ComEventsHelper#ComEventsFinalization + _sinks = ComEventsSink.RemoveAll(_sinks); + } + +#endregion + + +#region static methods + + [System.Security.SecurityCritical] + internal static ComEventsInfo Find(object rcw) { + return (ComEventsInfo)Marshal.GetComObjectData(rcw, typeof(ComEventsInfo)); + } + + // it is caller's responsibility to call this method under lock(rcw) + [System.Security.SecurityCritical] + internal static ComEventsInfo FromObject(object rcw) { + ComEventsInfo eventsInfo = Find(rcw); + if (eventsInfo == null) { + eventsInfo = new ComEventsInfo(rcw); + Marshal.SetComObjectData(rcw, typeof(ComEventsInfo), eventsInfo); + } + return eventsInfo; + } + +#endregion + + +#region internal methods + + internal ComEventsSink FindSink(ref Guid iid) { + return ComEventsSink.Find(_sinks, ref iid); + } + + // it is caller's responsibility to call this method under lock(rcw) + internal ComEventsSink AddSink(ref Guid iid) { + ComEventsSink sink = new ComEventsSink(_rcw, iid); + _sinks = ComEventsSink.Add(_sinks, sink); + + return _sinks; + } + + // it is caller's responsibility to call this method under lock(rcw) + [System.Security.SecurityCritical] + internal ComEventsSink RemoveSink(ComEventsSink sink) { + _sinks = ComEventsSink.Remove(_sinks, sink); + return _sinks; + } + +#endregion + + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsMethod.cs new file mode 100644 index 0000000000..2da0c5eea3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +/*============================================================ +** +** +** Purpose: part of ComEventHelpers APIs which allow binding +** managed delegates to COM's connection point based events. +** +**/ +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Reflection; + + +namespace System.Runtime.InteropServices { + + // see code:ComEventsHelper#ComEventsArchitecture + internal class ComEventsMethod { + + // This delegate wrapper class handles dynamic invocation of delegates. The reason for the wrapper's + // existence is that under certain circumstances we need to coerce arguments to types expected by the + // delegates signature. Normally, reflection (Delegate.DynamicInvoke) handles types coercion + // correctly but one known case is when the expected signature is 'ref Enum' - in this case + // reflection by design does not do the coercion. Since we need to be compatible with COM interop + // handling of this scenario - we are pre-processing delegate's signature by looking for 'ref enums' + // and cache the types required for such coercion. + internal class DelegateWrapper { + private Delegate _d; + + private bool _once = false; + private int _expectedParamsCount; + private Type[] _cachedTargetTypes; + + public DelegateWrapper(Delegate d) { + _d = d; + } + + public Delegate Delegate { + get { return _d; } + set { _d = value; } + } + + public object Invoke(object[] args) { + if (_d == null) + return null; + + if (_once == false) { + PreProcessSignature(); + _once = true; + } + + if (_cachedTargetTypes != null && _expectedParamsCount == args.Length) { + for (int i = 0; i < _expectedParamsCount; i++) { + if (_cachedTargetTypes[i] != null) { + args[i] = Enum.ToObject(_cachedTargetTypes[i], args[i]); + } + } + } + + return _d.DynamicInvoke(args); + } + + private void PreProcessSignature() { + ParameterInfo[] parameters = _d.Method.GetParameters(); + _expectedParamsCount = parameters.Length; + + Type[] enumTypes = new Type[_expectedParamsCount]; + + bool needToHandleCoercion = false; + + for (int i = 0; i < _expectedParamsCount; i++) { + ParameterInfo pi = parameters[i]; + // recognize only 'ref Enum' signatures and cache + // both enum type and the underlying type. + if (pi.ParameterType.IsByRef && + pi.ParameterType.HasElementType && + pi.ParameterType.GetElementType().IsEnum) { + + needToHandleCoercion = true; + enumTypes[i] = pi.ParameterType.GetElementType(); + } + } + + if (needToHandleCoercion == true) { + _cachedTargetTypes = enumTypes; + } + } + } + + #region private fields + + /// <summary> + /// Invoking ComEventsMethod means invoking a multi-cast delegate attached to it. + /// Since multicast delegate's built-in chaining supports only chaining instances of the same type, + /// we need to complement this design by using an explicit linked list data structure. + /// </summary> + private DelegateWrapper [] _delegateWrappers; + + private int _dispid; + private ComEventsMethod _next; + + #endregion + + + #region ctor + + internal ComEventsMethod(int dispid) { + _delegateWrappers = null; + _dispid = dispid; + } + + #endregion + + + #region static internal methods + + internal static ComEventsMethod Find(ComEventsMethod methods, int dispid) { + while (methods != null && methods._dispid != dispid) { + methods = methods._next; + } + return methods; + } + + internal static ComEventsMethod Add(ComEventsMethod methods, ComEventsMethod method) { + method._next = methods; + return method; + } + + internal static ComEventsMethod Remove(ComEventsMethod methods, ComEventsMethod method) { + if (methods == method) { + methods = methods._next; + } else { + ComEventsMethod current = methods; + while (current != null && current._next != method) + current = current._next; + if (current != null) + current._next = method._next; + } + + return methods; + } + + #endregion + + + #region public properties / methods + + internal int DispId { + get { return _dispid; } + } + + internal bool Empty { + get { return _delegateWrappers == null || _delegateWrappers.Length == 0; } + } + + internal void AddDelegate(Delegate d) { + int count = 0; + if (_delegateWrappers != null) { + count = _delegateWrappers.Length; + } + + for (int i = 0; i < count; i++) { + if (_delegateWrappers[i].Delegate.GetType() == d.GetType()) { + _delegateWrappers[i].Delegate = Delegate.Combine(_delegateWrappers[i].Delegate, d); + return; + } + } + + DelegateWrapper [] newDelegateWrappers = new DelegateWrapper[count + 1]; + if (count > 0) { + _delegateWrappers.CopyTo(newDelegateWrappers, 0); + } + + DelegateWrapper wrapper = new DelegateWrapper(d); + newDelegateWrappers[count] = wrapper; + + _delegateWrappers = newDelegateWrappers; + } + + internal void RemoveDelegate(Delegate d) { + + int count = _delegateWrappers.Length; + int removeIdx = -1; + + for (int i = 0; i < count; i++) { + if (_delegateWrappers[i].Delegate.GetType() == d.GetType()) { + removeIdx = i; + break; + } + } + + if (removeIdx < 0) + return; + + Delegate newDelegate = Delegate.Remove(_delegateWrappers[removeIdx].Delegate, d); + if (newDelegate != null) { + _delegateWrappers[removeIdx].Delegate = newDelegate; + return; + } + + // now remove the found entry from the _delegates array + + if (count == 1) { + _delegateWrappers = null; + return; + } + + DelegateWrapper [] newDelegateWrappers = new DelegateWrapper[count - 1]; + int j = 0; + while (j < removeIdx) { + newDelegateWrappers[j] = _delegateWrappers[j]; + j++; + } + while (j < count-1) { + newDelegateWrappers[j] = _delegateWrappers[j + 1]; + j++; + } + + _delegateWrappers = newDelegateWrappers; + } + + internal object Invoke(object[] args) { + BCLDebug.Assert(Empty == false, "event sink is executed but delegates list is empty"); + + // Issue: see code:ComEventsHelper#ComEventsRetValIssue + object result = null; + DelegateWrapper[] invocationList = _delegateWrappers; + foreach (DelegateWrapper wrapper in invocationList) { + if (wrapper == null || wrapper.Delegate == null) + continue; + + result = wrapper.Invoke(args); + } + + return result; + } + + #endregion + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComEventsSink.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsSink.cs new file mode 100644 index 0000000000..a414eff3a1 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsSink.cs @@ -0,0 +1,286 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +/*============================================================ +** +** +** Purpose: part of ComEventHelpers APIs which allow binding +** managed delegates to COM's connection point based events. +** +**/ + +namespace System.Runtime.InteropServices { + using System; + using System.Diagnostics; + + // see code:ComEventsHelper#ComEventsArchitecture + [System.Security.SecurityCritical] + internal class ComEventsSink : NativeMethods.IDispatch, ICustomQueryInterface + { +#region private fields + + private Guid _iidSourceItf; + private ComTypes.IConnectionPoint _connectionPoint; + private int _cookie; + private ComEventsMethod _methods; + private ComEventsSink _next; + +#endregion + + +#region ctor + + internal ComEventsSink(object rcw, Guid iid) { + _iidSourceItf = iid; + this.Advise(rcw); + } + +#endregion + + +#region static members + + internal static ComEventsSink Find(ComEventsSink sinks, ref Guid iid) { + + ComEventsSink sink = sinks; + while (sink != null && sink._iidSourceItf != iid) { + sink = sink._next; + } + + return sink; + } + + internal static ComEventsSink Add(ComEventsSink sinks, ComEventsSink sink) { + sink._next = sinks; + return sink; + } + + [System.Security.SecurityCritical] + internal static ComEventsSink RemoveAll(ComEventsSink sinks) { + while (sinks != null) { + sinks.Unadvise(); + sinks = sinks._next; + } + + return null; + } + + [System.Security.SecurityCritical] + internal static ComEventsSink Remove(ComEventsSink sinks, ComEventsSink sink) { + BCLDebug.Assert(sinks != null, "removing event sink from empty sinks collection"); + BCLDebug.Assert(sink != null, "specify event sink is null"); + + if (sink == sinks) { + sinks = sinks._next; + } else { + ComEventsSink current = sinks; + while (current != null && current._next != sink) + current = current._next; + + if (current != null) { + current._next = sink._next; + } + } + + sink.Unadvise(); + + return sinks; + } + +#endregion + + +#region public methods + + public ComEventsMethod RemoveMethod(ComEventsMethod method) { + _methods = ComEventsMethod.Remove(_methods, method); + return _methods; + } + + public ComEventsMethod FindMethod(int dispid) { + return ComEventsMethod.Find(_methods, dispid); + } + + public ComEventsMethod AddMethod(int dispid) { + ComEventsMethod method = new ComEventsMethod(dispid); + _methods = ComEventsMethod.Add(_methods, method); + return method; + } + +#endregion + + +#region IDispatch Members + + [System.Security.SecurityCritical] + void NativeMethods.IDispatch.GetTypeInfoCount(out uint pctinfo) { + pctinfo = 0; + } + + [System.Security.SecurityCritical] + void NativeMethods.IDispatch.GetTypeInfo(uint iTInfo, int lcid, out IntPtr info) { + throw new NotImplementedException(); + } + + [System.Security.SecurityCritical] + void NativeMethods.IDispatch.GetIDsOfNames(ref Guid iid, string[] names, uint cNames, int lcid, int[] rgDispId) { + throw new NotImplementedException(); + } + + private const VarEnum VT_BYREF_VARIANT = VarEnum.VT_BYREF | VarEnum.VT_VARIANT; + private const VarEnum VT_TYPEMASK = (VarEnum) 0x0fff; + private const VarEnum VT_BYREF_TYPEMASK = VT_TYPEMASK | VarEnum.VT_BYREF; + + private static unsafe Variant *GetVariant(Variant *pSrc) + { + if (pSrc->VariantType == VT_BYREF_VARIANT) + { + // For VB6 compatibility reasons, if the VARIANT is a VT_BYREF | VT_VARIANT that + // contains another VARIANT with VT_BYREF | VT_VARIANT, then we need to extract the + // inner VARIANT and use it instead of the outer one. Note that if the inner VARIANT + // is VT_BYREF | VT_VARIANT | VT_ARRAY, it will pass the below test too. + Variant *pByRefVariant = (Variant *)pSrc->AsByRefVariant; + if ((pByRefVariant->VariantType & VT_BYREF_TYPEMASK) == VT_BYREF_VARIANT) + return (Variant *)pByRefVariant; + } + + return pSrc; + } + + [System.Security.SecurityCritical] + unsafe void NativeMethods.IDispatch.Invoke( + int dispid, + ref Guid riid, + int lcid, + ComTypes.INVOKEKIND wFlags, + ref ComTypes.DISPPARAMS pDispParams, + IntPtr pvarResult, + IntPtr pExcepInfo, + IntPtr puArgErr) { + + ComEventsMethod method = FindMethod(dispid); + if (method == null) + return; + + // notice the unsafe pointers we are using. This is to avoid unnecessary + // arguments marshalling. see code:ComEventsHelper#ComEventsArgsMarshalling + + object [] args = new object[pDispParams.cArgs]; + int [] byrefsMap = new int[pDispParams.cArgs]; + bool [] usedArgs = new bool[pDispParams.cArgs]; + + Variant* pvars = (Variant*)pDispParams.rgvarg; + int* pNamedArgs = (int*)pDispParams.rgdispidNamedArgs; + + // copy the named args (positional) as specified + int i; + int pos; + for (i = 0; i < pDispParams.cNamedArgs; i++) { + pos = pNamedArgs[i]; + + Variant* pvar = GetVariant(&pvars[i]); + args[pos] = pvar->ToObject(); + usedArgs[pos] = true; + + if (pvar->IsByRef) { + byrefsMap[pos] = i; + } else { + byrefsMap[pos] = -1; + } + } + + // copy the rest of the arguments in the reverse order + pos = 0; + for (; i < pDispParams.cArgs; i++) { + // find the next unassigned argument + while (usedArgs[pos]) { + ++pos; + } + + Variant* pvar = GetVariant(&pvars[pDispParams.cArgs - 1 - i]); + args[pos] = pvar->ToObject(); + + if (pvar->IsByRef) + byrefsMap[pos] = pDispParams.cArgs - 1 - i; + else + byrefsMap[pos] = -1; + + pos++; + } + + // Do the actual delegate invocation + object result; + result = method.Invoke(args); + + // convert result to VARIANT + if (pvarResult != IntPtr.Zero) { + Marshal.GetNativeVariantForObject(result, pvarResult); + } + + // Now we need to marshal all the byrefs back + for (i = 0; i < pDispParams.cArgs; i++) { + int idxToPos = byrefsMap[i]; + if (idxToPos == -1) + continue; + + GetVariant(&pvars[idxToPos])->CopyFromIndirect(args[i]); + } + } + +#endregion + + static Guid IID_IManagedObject = new Guid("{C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4}"); + + [System.Security.SecurityCritical] + CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) { + ppv = IntPtr.Zero; + if (iid == this._iidSourceItf || iid == typeof(NativeMethods.IDispatch).GUID) { + ppv = Marshal.GetComInterfaceForObject(this, typeof(NativeMethods.IDispatch), CustomQueryInterfaceMode.Ignore); + return CustomQueryInterfaceResult.Handled; + } + else if (iid == IID_IManagedObject) + { + return CustomQueryInterfaceResult.Failed; + } + + return CustomQueryInterfaceResult.NotHandled; + } + +#region private methods + + + private void Advise(object rcw) { + BCLDebug.Assert(_connectionPoint == null, "comevent sink is already advised"); + + ComTypes.IConnectionPointContainer cpc = (ComTypes.IConnectionPointContainer)rcw; + ComTypes.IConnectionPoint cp; + cpc.FindConnectionPoint(ref _iidSourceItf, out cp); + + object sinkObject = this; + + cp.Advise(sinkObject, out _cookie); + + _connectionPoint = cp; + } + + [System.Security.SecurityCritical] + private void Unadvise() { + BCLDebug.Assert(_connectionPoint != null, "can not unadvise from empty connection point"); + + try { + _connectionPoint.Unadvise(_cookie); + Marshal.ReleaseComObject(_connectionPoint); + } catch (System.Exception) { + // swallow all exceptions on unadvise + // the host may not be available at this point + } finally { + _connectionPoint = null; + } + + } + +#endregion + }; +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComMemberType.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComMemberType.cs new file mode 100644 index 0000000000..3be2a56da5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComMemberType.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices { + + using System; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public enum ComMemberType + { + Method = 0, + PropGet = 1, + PropSet = 2 + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IBindCtx.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IBindCtx.cs new file mode 100644 index 0000000000..aac3f59d0f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IBindCtx.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IBindCtx interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [StructLayout(LayoutKind.Sequential)] + + public struct BIND_OPTS + { + public int cbStruct; + public int grfFlags; + public int grfMode; + public int dwTickCountDeadline; + } + + [Guid("0000000e-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IBindCtx + { + void RegisterObjectBound([MarshalAs(UnmanagedType.Interface)] Object punk); + void RevokeObjectBound([MarshalAs(UnmanagedType.Interface)] Object punk); + void ReleaseBoundObjects(); + void SetBindOptions([In()] ref BIND_OPTS pbindopts); + void GetBindOptions(ref BIND_OPTS pbindopts); + void GetRunningObjectTable(out IRunningObjectTable pprot); + void RegisterObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey, [MarshalAs(UnmanagedType.Interface)] Object punk); + void GetObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey, [MarshalAs(UnmanagedType.Interface)] out Object ppunk); + void EnumObjectParam(out IEnumString ppenum); + [PreserveSig] + int RevokeObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IConnectionPoint.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IConnectionPoint.cs new file mode 100644 index 0000000000..f70973e60d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IConnectionPoint.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IConnectionPoint interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("B196B286-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IConnectionPoint + { + void GetConnectionInterface(out Guid pIID); + void GetConnectionPointContainer(out IConnectionPointContainer ppCPC); + void Advise([MarshalAs(UnmanagedType.Interface)] Object pUnkSink, out int pdwCookie); + void Unadvise(int dwCookie); + void EnumConnections(out IEnumConnections ppEnum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IConnectionPointContainer.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IConnectionPointContainer.cs new file mode 100644 index 0000000000..fffff3c170 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IConnectionPointContainer.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IConnectionPointContainer interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("B196B284-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IConnectionPointContainer + { + void EnumConnectionPoints(out IEnumConnectionPoints ppEnum); + void FindConnectionPoint([In] ref Guid riid, out IConnectionPoint ppCP); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumConnectionPoints.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumConnectionPoints.cs new file mode 100644 index 0000000000..d667925b7e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumConnectionPoints.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IEnumConnectionPoints interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("B196B285-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IEnumConnectionPoints + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] IConnectionPoint[] rgelt, IntPtr pceltFetched); + [PreserveSig] + int Skip(int celt); + void Reset(); + void Clone(out IEnumConnectionPoints ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumConnections.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumConnections.cs new file mode 100644 index 0000000000..8a2b4f0e6f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumConnections.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IEnumConnections interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct CONNECTDATA + { + [MarshalAs(UnmanagedType.Interface)] + public Object pUnk; + public int dwCookie; + } + + [Guid("B196B287-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IEnumConnections + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] CONNECTDATA[] rgelt, IntPtr pceltFetched); + [PreserveSig] + int Skip(int celt); + void Reset(); + void Clone(out IEnumConnections ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumMoniker.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumMoniker.cs new file mode 100644 index 0000000000..57994b66bc --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumMoniker.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IEnumMoniker interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("00000102-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IEnumMoniker + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] IMoniker[] rgelt, IntPtr pceltFetched); + [PreserveSig] + int Skip(int celt); + void Reset(); + void Clone(out IEnumMoniker ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumString.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumString.cs new file mode 100644 index 0000000000..f1e9233581 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumString.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IEnumString interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("00000101-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IEnumString + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0), Out] String[] rgelt, IntPtr pceltFetched); + [PreserveSig] + int Skip(int celt); + void Reset(); + void Clone(out IEnumString ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumVARIANT.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumVARIANT.cs new file mode 100644 index 0000000000..ea9b74b7c1 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumVARIANT.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IEnumVARIANT interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("00020404-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IEnumVARIANT + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0), Out] object[] rgVar, IntPtr pceltFetched); + + [PreserveSig] + int Skip(int celt); + + [PreserveSig] + int Reset(); + + IEnumVARIANT Clone(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumerable.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumerable.cs new file mode 100644 index 0000000000..1a13399a88 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumerable.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: IEnumerable +** +** +** Purpose: +** This interface is redefined here since the original IEnumerable interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IEnumerable interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")] + internal interface IEnumerable + { + [DispId(-4)] + System.Collections.IEnumerator GetEnumerator(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumerator.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumerator.cs new file mode 100644 index 0000000000..aea2017b1e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IEnumerator.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: IEnumerator +** +** +** Purpose: +** This interface is redefined here since the original IEnumerator interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IEnumerator interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")] + internal interface IEnumerator + { + bool MoveNext(); + + Object Current + { + get; + } + + void Reset(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IExpando.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IExpando.cs new file mode 100644 index 0000000000..ad4ed0b8be --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IExpando.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: IExpando +** +** +** Purpose: +** This interface is redefined here since the original IExpando interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IExpando interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + using System.Reflection; + + [Guid("AFBF15E6-C37C-11d2-B88E-00A0C9B471B8")] + internal interface IExpando : IReflect + { + FieldInfo AddField(String name); + PropertyInfo AddProperty(String name); + MethodInfo AddMethod(String name, Delegate method); + void RemoveMember(MemberInfo m); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IMoniker.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IMoniker.cs new file mode 100644 index 0000000000..caee5e7fa3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IMoniker.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IMoniker interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [StructLayout(LayoutKind.Sequential)] + + public struct FILETIME + { + public int dwLowDateTime; + public int dwHighDateTime; + } + + [Guid("0000000f-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IMoniker + { + // IPersist portion + void GetClassID(out Guid pClassID); + + // IPersistStream portion + [PreserveSig] + int IsDirty(); + void Load(IStream pStm); + void Save(IStream pStm, [MarshalAs(UnmanagedType.Bool)] bool fClearDirty); + void GetSizeMax(out Int64 pcbSize); + + // IMoniker portion + void BindToObject(IBindCtx pbc, IMoniker pmkToLeft, [In()] ref Guid riidResult, [MarshalAs(UnmanagedType.Interface)] out Object ppvResult); + void BindToStorage(IBindCtx pbc, IMoniker pmkToLeft, [In()] ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out Object ppvObj); + void Reduce(IBindCtx pbc, int dwReduceHowFar, ref IMoniker ppmkToLeft, out IMoniker ppmkReduced); + void ComposeWith(IMoniker pmkRight, [MarshalAs(UnmanagedType.Bool)] bool fOnlyIfNotGeneric, out IMoniker ppmkComposite); + void Enum([MarshalAs(UnmanagedType.Bool)] bool fForward, out IEnumMoniker ppenumMoniker); + [PreserveSig] + int IsEqual(IMoniker pmkOtherMoniker); + void Hash(out int pdwHash); + [PreserveSig] + int IsRunning(IBindCtx pbc, IMoniker pmkToLeft, IMoniker pmkNewlyRunning); + void GetTimeOfLastChange(IBindCtx pbc, IMoniker pmkToLeft, out FILETIME pFileTime); + void Inverse(out IMoniker ppmk); + void CommonPrefixWith(IMoniker pmkOther, out IMoniker ppmkPrefix); + void RelativePathTo(IMoniker pmkOther, out IMoniker ppmkRelPath); + void GetDisplayName(IBindCtx pbc, IMoniker pmkToLeft, [MarshalAs(UnmanagedType.LPWStr)] out String ppszDisplayName); + void ParseDisplayName(IBindCtx pbc, IMoniker pmkToLeft, [MarshalAs(UnmanagedType.LPWStr)] String pszDisplayName, out int pchEaten, out IMoniker ppmkOut); + [PreserveSig] + int IsSystemMoniker(out int pdwMksys); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IPersistFile.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IPersistFile.cs new file mode 100644 index 0000000000..a9f118a354 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IPersistFile.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IPersistFile interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("0000010b-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IPersistFile + { + // IPersist portion + void GetClassID(out Guid pClassID); + + // IPersistFile portion + [PreserveSig] + int IsDirty(); + void Load([MarshalAs(UnmanagedType.LPWStr)] String pszFileName, int dwMode); + void Save([MarshalAs(UnmanagedType.LPWStr)] String pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember); + void SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] String pszFileName); + void GetCurFile([MarshalAs(UnmanagedType.LPWStr)] out String ppszFileName); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IReflect.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IReflect.cs new file mode 100644 index 0000000000..c86f961d01 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IReflect.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: IReflect +** +** +** Purpose: +** This interface is redefined here since the original IReflect interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IReflect interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + using System.Reflection; + using CultureInfo = System.Globalization.CultureInfo; + + [Guid("AFBF15E5-C37C-11d2-B88E-00A0C9B471B8")] + internal interface IReflect + { + MethodInfo GetMethod(String name,BindingFlags bindingAttr,Binder binder, + Type[] types,ParameterModifier[] modifiers); + + MethodInfo GetMethod(String name,BindingFlags bindingAttr); + + MethodInfo[] GetMethods( + BindingFlags bindingAttr); + + FieldInfo GetField( + String name, + BindingFlags bindingAttr); + + FieldInfo[] GetFields( + BindingFlags bindingAttr); + + PropertyInfo GetProperty( + String name, + BindingFlags bindingAttr); + + PropertyInfo GetProperty( + String name, + BindingFlags bindingAttr, + Binder binder, + Type returnType, + Type[] types, + ParameterModifier[] modifiers); + + PropertyInfo[] GetProperties( + BindingFlags bindingAttr); + + MemberInfo[] GetMember( + String name, + BindingFlags bindingAttr); + + MemberInfo[] GetMembers( + BindingFlags bindingAttr); + + Object InvokeMember( + String name, + BindingFlags invokeAttr, + Binder binder, + Object target, + Object[] args, + ParameterModifier[] modifiers, + CultureInfo culture, + String[] namedParameters); + + Type UnderlyingSystemType + { + get; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IRunningObjectTable.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IRunningObjectTable.cs new file mode 100644 index 0000000000..bebbdec70c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IRunningObjectTable.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IRunningObjectTable interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("00000010-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IRunningObjectTable + { + int Register(int grfFlags, [MarshalAs(UnmanagedType.Interface)] Object punkObject, IMoniker pmkObjectName); + void Revoke(int dwRegister); + [PreserveSig] + int IsRunning(IMoniker pmkObjectName); + [PreserveSig] + int GetObject(IMoniker pmkObjectName, [MarshalAs(UnmanagedType.Interface)] out Object ppunkObject); + void NoteChangeTime(int dwRegister, ref FILETIME pfiletime); + [PreserveSig] + int GetTimeOfLastChange(IMoniker pmkObjectName, out FILETIME pfiletime); + void EnumRunning(out IEnumMoniker ppenumMoniker); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IStream.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IStream.cs new file mode 100644 index 0000000000..616ecbfa18 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/IStream.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: IStream interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct STATSTG + { + public String pwcsName; + public int type; + public Int64 cbSize; + public FILETIME mtime; + public FILETIME ctime; + public FILETIME atime; + public int grfMode; + public int grfLocksSupported; + public Guid clsid; + public int grfStateBits; + public int reserved; + } + + [Guid("0000000c-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IStream + { + // ISequentialStream portion + void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] Byte[] pv, int cb, IntPtr pcbRead); + void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Byte[] pv, int cb, IntPtr pcbWritten); + + // IStream portion + void Seek(Int64 dlibMove, int dwOrigin, IntPtr plibNewPosition); + void SetSize(Int64 libNewSize); + void CopyTo(IStream pstm, Int64 cb, IntPtr pcbRead, IntPtr pcbWritten); + void Commit(int grfCommitFlags); + void Revert(); + void LockRegion(Int64 libOffset, Int64 cb, int dwLockType); + void UnlockRegion(Int64 libOffset, Int64 cb, int dwLockType); + void Stat(out STATSTG pstatstg, int grfStatFlag); + void Clone(out IStream ppstm); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeComp.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeComp.cs new file mode 100644 index 0000000000..f938ba91a7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeComp.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: ITypeComp interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Serializable] + public enum DESCKIND + { + DESCKIND_NONE = 0, + DESCKIND_FUNCDESC = DESCKIND_NONE + 1, + DESCKIND_VARDESC = DESCKIND_FUNCDESC + 1, + DESCKIND_TYPECOMP = DESCKIND_VARDESC + 1, + DESCKIND_IMPLICITAPPOBJ = DESCKIND_TYPECOMP + 1, + DESCKIND_MAX = DESCKIND_IMPLICITAPPOBJ + 1 + } + + [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)] + + public struct BINDPTR + { + [FieldOffset(0)] + public IntPtr lpfuncdesc; + [FieldOffset(0)] + public IntPtr lpvardesc; + [FieldOffset(0)] + public IntPtr lptcomp; + } + + [Guid("00020403-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface ITypeComp + { + void Bind([MarshalAs(UnmanagedType.LPWStr)] String szName, int lHashVal, Int16 wFlags, out ITypeInfo ppTInfo, out DESCKIND pDescKind, out BINDPTR pBindPtr); + void BindType([MarshalAs(UnmanagedType.LPWStr)] String szName, int lHashVal, out ITypeInfo ppTInfo, out ITypeComp ppTComp); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeInfo.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeInfo.cs new file mode 100644 index 0000000000..7a12605f20 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeInfo.cs @@ -0,0 +1,335 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: ITypeInfo interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Serializable] + public enum TYPEKIND + { + TKIND_ENUM = 0, + TKIND_RECORD = TKIND_ENUM + 1, + TKIND_MODULE = TKIND_RECORD + 1, + TKIND_INTERFACE = TKIND_MODULE + 1, + TKIND_DISPATCH = TKIND_INTERFACE + 1, + TKIND_COCLASS = TKIND_DISPATCH + 1, + TKIND_ALIAS = TKIND_COCLASS + 1, + TKIND_UNION = TKIND_ALIAS + 1, + TKIND_MAX = TKIND_UNION + 1 + } + +[Serializable] +[Flags()] + public enum TYPEFLAGS : short + { + TYPEFLAG_FAPPOBJECT = 0x1, + TYPEFLAG_FCANCREATE = 0x2, + TYPEFLAG_FLICENSED = 0x4, + TYPEFLAG_FPREDECLID = 0x8, + TYPEFLAG_FHIDDEN = 0x10, + TYPEFLAG_FCONTROL = 0x20, + TYPEFLAG_FDUAL = 0x40, + TYPEFLAG_FNONEXTENSIBLE = 0x80, + TYPEFLAG_FOLEAUTOMATION = 0x100, + TYPEFLAG_FRESTRICTED = 0x200, + TYPEFLAG_FAGGREGATABLE = 0x400, + TYPEFLAG_FREPLACEABLE = 0x800, + TYPEFLAG_FDISPATCHABLE = 0x1000, + TYPEFLAG_FREVERSEBIND = 0x2000, + TYPEFLAG_FPROXY = 0x4000 + } + +[Serializable] +[Flags()] + public enum IMPLTYPEFLAGS + { + IMPLTYPEFLAG_FDEFAULT = 0x1, + IMPLTYPEFLAG_FSOURCE = 0x2, + IMPLTYPEFLAG_FRESTRICTED = 0x4, + IMPLTYPEFLAG_FDEFAULTVTABLE = 0x8, + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct TYPEATTR + { + // Constant used with the memid fields. + public const int MEMBER_ID_NIL = unchecked((int)0xFFFFFFFF); + + // Actual fields of the TypeAttr struct. + public Guid guid; + public Int32 lcid; + public Int32 dwReserved; + public Int32 memidConstructor; + public Int32 memidDestructor; + public IntPtr lpstrSchema; + public Int32 cbSizeInstance; + public TYPEKIND typekind; + public Int16 cFuncs; + public Int16 cVars; + public Int16 cImplTypes; + public Int16 cbSizeVft; + public Int16 cbAlignment; + public TYPEFLAGS wTypeFlags; + public Int16 wMajorVerNum; + public Int16 wMinorVerNum; + public TYPEDESC tdescAlias; + public IDLDESC idldescType; + } + + [StructLayout(LayoutKind.Sequential)] + + public struct FUNCDESC + { + public int memid; //MEMBERID memid; + public IntPtr lprgscode; // /* [size_is(cScodes)] */ SCODE RPC_FAR *lprgscode; + public IntPtr lprgelemdescParam; // /* [size_is(cParams)] */ ELEMDESC __RPC_FAR *lprgelemdescParam; + public FUNCKIND funckind; //FUNCKIND funckind; + public INVOKEKIND invkind; //INVOKEKIND invkind; + public CALLCONV callconv; //CALLCONV callconv; + public Int16 cParams; //short cParams; + public Int16 cParamsOpt; //short cParamsOpt; + public Int16 oVft; //short oVft; + public Int16 cScodes; //short cScodes; + public ELEMDESC elemdescFunc; //ELEMDESC elemdescFunc; + public Int16 wFuncFlags; //WORD wFuncFlags; + } + +[Serializable] +[Flags()] + public enum IDLFLAG : short + { + IDLFLAG_NONE = PARAMFLAG.PARAMFLAG_NONE, + IDLFLAG_FIN = PARAMFLAG.PARAMFLAG_FIN, + IDLFLAG_FOUT = PARAMFLAG.PARAMFLAG_FOUT, + IDLFLAG_FLCID = PARAMFLAG.PARAMFLAG_FLCID, + IDLFLAG_FRETVAL = PARAMFLAG.PARAMFLAG_FRETVAL + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct IDLDESC + { + public IntPtr dwReserved; + public IDLFLAG wIDLFlags; + } + +[Serializable] +[Flags()] + public enum PARAMFLAG :short + { + PARAMFLAG_NONE = 0, + PARAMFLAG_FIN = 0x1, + PARAMFLAG_FOUT = 0x2, + PARAMFLAG_FLCID = 0x4, + PARAMFLAG_FRETVAL = 0x8, + PARAMFLAG_FOPT = 0x10, + PARAMFLAG_FHASDEFAULT = 0x20, + PARAMFLAG_FHASCUSTDATA = 0x40 + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct PARAMDESC + { + public IntPtr lpVarValue; + public PARAMFLAG wParamFlags; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct TYPEDESC + { + public IntPtr lpValue; + public Int16 vt; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct ELEMDESC + { + public TYPEDESC tdesc; + + [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)] + + public struct DESCUNION + { + [FieldOffset(0)] + public IDLDESC idldesc; + [FieldOffset(0)] + public PARAMDESC paramdesc; + }; + public DESCUNION desc; + } + + [Serializable] + public enum VARKIND : int + { + VAR_PERINSTANCE = 0x0, + VAR_STATIC = 0x1, + VAR_CONST = 0x2, + VAR_DISPATCH = 0x3 + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct VARDESC + { + public int memid; + public String lpstrSchema; + + [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)] + + public struct DESCUNION + { + [FieldOffset(0)] + public int oInst; + [FieldOffset(0)] + public IntPtr lpvarValue; + }; + + public DESCUNION desc; + + public ELEMDESC elemdescVar; + public short wVarFlags; + public VARKIND varkind; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct DISPPARAMS + { + public IntPtr rgvarg; + public IntPtr rgdispidNamedArgs; + public int cArgs; + public int cNamedArgs; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct EXCEPINFO + { + public Int16 wCode; + public Int16 wReserved; + [MarshalAs(UnmanagedType.BStr)] public String bstrSource; + [MarshalAs(UnmanagedType.BStr)] public String bstrDescription; + [MarshalAs(UnmanagedType.BStr)] public String bstrHelpFile; + public int dwHelpContext; + public IntPtr pvReserved; + public IntPtr pfnDeferredFillIn; + public Int32 scode; + } + + [Serializable] + public enum FUNCKIND : int + { + FUNC_VIRTUAL = 0, + FUNC_PUREVIRTUAL = 1, + FUNC_NONVIRTUAL = 2, + FUNC_STATIC = 3, + FUNC_DISPATCH = 4 + } + +[Serializable] +[Flags] + public enum INVOKEKIND : int + { + INVOKE_FUNC = 0x1, + INVOKE_PROPERTYGET = 0x2, + INVOKE_PROPERTYPUT = 0x4, + INVOKE_PROPERTYPUTREF = 0x8 + } + + [Serializable] + public enum CALLCONV : int + { + CC_CDECL =1, + CC_MSCPASCAL=2, + CC_PASCAL =CC_MSCPASCAL, + CC_MACPASCAL=3, + CC_STDCALL =4, + CC_RESERVED =5, + CC_SYSCALL =6, + CC_MPWCDECL =7, + CC_MPWPASCAL=8, + CC_MAX =9 + } + +[Serializable] +[Flags()] + public enum FUNCFLAGS : short + { + FUNCFLAG_FRESTRICTED= 0x1, + FUNCFLAG_FSOURCE = 0x2, + FUNCFLAG_FBINDABLE = 0x4, + FUNCFLAG_FREQUESTEDIT = 0x8, + FUNCFLAG_FDISPLAYBIND = 0x10, + FUNCFLAG_FDEFAULTBIND = 0x20, + FUNCFLAG_FHIDDEN = 0x40, + FUNCFLAG_FUSESGETLASTERROR= 0x80, + FUNCFLAG_FDEFAULTCOLLELEM= 0x100, + FUNCFLAG_FUIDEFAULT = 0x200, + FUNCFLAG_FNONBROWSABLE = 0x400, + FUNCFLAG_FREPLACEABLE = 0x800, + FUNCFLAG_FIMMEDIATEBIND = 0x1000 + } + +[Serializable] +[Flags()] + public enum VARFLAGS : short + { + VARFLAG_FREADONLY =0x1, + VARFLAG_FSOURCE =0x2, + VARFLAG_FBINDABLE =0x4, + VARFLAG_FREQUESTEDIT =0x8, + VARFLAG_FDISPLAYBIND =0x10, + VARFLAG_FDEFAULTBIND =0x20, + VARFLAG_FHIDDEN =0x40, + VARFLAG_FRESTRICTED =0x80, + VARFLAG_FDEFAULTCOLLELEM =0x100, + VARFLAG_FUIDEFAULT =0x200, + VARFLAG_FNONBROWSABLE =0x400, + VARFLAG_FREPLACEABLE =0x800, + VARFLAG_FIMMEDIATEBIND =0x1000 + } + + [Guid("00020401-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface ITypeInfo + { + void GetTypeAttr(out IntPtr ppTypeAttr); + void GetTypeComp(out ITypeComp ppTComp); + void GetFuncDesc(int index, out IntPtr ppFuncDesc); + void GetVarDesc(int index, out IntPtr ppVarDesc); + void GetNames(int memid, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] String[] rgBstrNames, int cMaxNames, out int pcNames); + void GetRefTypeOfImplType(int index, out int href); + void GetImplTypeFlags(int index, out IMPLTYPEFLAGS pImplTypeFlags); + void GetIDsOfNames([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1), In] String[] rgszNames, int cNames, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] int[] pMemId); + void Invoke([MarshalAs(UnmanagedType.IUnknown)] Object pvInstance, int memid, Int16 wFlags, ref DISPPARAMS pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, out int puArgErr); + void GetDocumentation(int index, out String strName, out String strDocString, out int dwHelpContext, out String strHelpFile); + void GetDllEntry(int memid, INVOKEKIND invKind, IntPtr pBstrDllName, IntPtr pBstrName, IntPtr pwOrdinal); + void GetRefTypeInfo(int hRef, out ITypeInfo ppTI); + void AddressOfMember(int memid, INVOKEKIND invKind, out IntPtr ppv); + void CreateInstance([MarshalAs(UnmanagedType.IUnknown)] Object pUnkOuter, [In] ref Guid riid, [MarshalAs(UnmanagedType.IUnknown), Out] out Object ppvObj); + void GetMops(int memid, out String pBstrMops); + void GetContainingTypeLib(out ITypeLib ppTLB, out int pIndex); + [PreserveSig] + void ReleaseTypeAttr(IntPtr pTypeAttr); + [PreserveSig] + void ReleaseFuncDesc(IntPtr pFuncDesc); + [PreserveSig] + void ReleaseVarDesc(IntPtr pVarDesc); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeInfo2.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeInfo2.cs new file mode 100644 index 0000000000..a782b9edd6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeInfo2.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: ITypeInfo2 interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("00020412-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface ITypeInfo2 : ITypeInfo + { + new void GetTypeAttr(out IntPtr ppTypeAttr); + new void GetTypeComp(out ITypeComp ppTComp); + new void GetFuncDesc(int index, out IntPtr ppFuncDesc); + new void GetVarDesc(int index, out IntPtr ppVarDesc); + new void GetNames(int memid, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] String[] rgBstrNames, int cMaxNames, out int pcNames); + new void GetRefTypeOfImplType(int index, out int href); + new void GetImplTypeFlags(int index, out IMPLTYPEFLAGS pImplTypeFlags); + new void GetIDsOfNames([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1), In] String[] rgszNames, int cNames, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] int[] pMemId); + new void Invoke([MarshalAs(UnmanagedType.IUnknown)] Object pvInstance, int memid, Int16 wFlags, ref DISPPARAMS pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, out int puArgErr); + new void GetDocumentation(int index, out String strName, out String strDocString, out int dwHelpContext, out String strHelpFile); + new void GetDllEntry(int memid, INVOKEKIND invKind, IntPtr pBstrDllName, IntPtr pBstrName, IntPtr pwOrdinal); + new void GetRefTypeInfo(int hRef, out ITypeInfo ppTI); + new void AddressOfMember(int memid, INVOKEKIND invKind, out IntPtr ppv); + new void CreateInstance([MarshalAs(UnmanagedType.IUnknown)] Object pUnkOuter, [In] ref Guid riid, [MarshalAs(UnmanagedType.IUnknown), Out] out Object ppvObj); + new void GetMops(int memid, out String pBstrMops); + new void GetContainingTypeLib(out ITypeLib ppTLB, out int pIndex); + [PreserveSig] + new void ReleaseTypeAttr(IntPtr pTypeAttr); + [PreserveSig] + new void ReleaseFuncDesc(IntPtr pFuncDesc); + [PreserveSig] + new void ReleaseVarDesc(IntPtr pVarDesc); + void GetTypeKind(out TYPEKIND pTypeKind); + void GetTypeFlags(out int pTypeFlags); + void GetFuncIndexOfMemId(int memid, INVOKEKIND invKind, out int pFuncIndex); + void GetVarIndexOfMemId(int memid, out int pVarIndex); + void GetCustData(ref Guid guid, out Object pVarVal); + void GetFuncCustData(int index, ref Guid guid, out Object pVarVal); + void GetParamCustData(int indexFunc, int indexParam, ref Guid guid, out Object pVarVal); + void GetVarCustData(int index, ref Guid guid, out Object pVarVal); + void GetImplTypeCustData(int index, ref Guid guid, out Object pVarVal); + [LCIDConversionAttribute(1)] + void GetDocumentation2(int memid, out String pbstrHelpString, out int pdwHelpStringContext, out String pbstrHelpStringDll); + void GetAllCustData(IntPtr pCustData); + void GetAllFuncCustData(int index, IntPtr pCustData); + void GetAllParamCustData(int indexFunc, int indexParam, IntPtr pCustData); + void GetAllVarCustData(int index, IntPtr pCustData); + void GetAllImplTypeCustData(int index, IntPtr pCustData); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeLib.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeLib.cs new file mode 100644 index 0000000000..c39b088c0b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeLib.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: ITypeLib interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Serializable] + public enum SYSKIND + { + SYS_WIN16 = 0, + SYS_WIN32 = SYS_WIN16 + 1, + SYS_MAC = SYS_WIN32 + 1, + SYS_WIN64 = SYS_MAC + 1 + } + +[Serializable] +[Flags()] + public enum LIBFLAGS : short + { + LIBFLAG_FRESTRICTED = 0x1, + LIBFLAG_FCONTROL = 0x2, + LIBFLAG_FHIDDEN = 0x4, + LIBFLAG_FHASDISKIMAGE = 0x8 + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + [Serializable] + public struct TYPELIBATTR + { + public Guid guid; + public int lcid; + public SYSKIND syskind; + public Int16 wMajorVerNum; + public Int16 wMinorVerNum; + public LIBFLAGS wLibFlags; + } + + [Guid("00020402-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface ITypeLib + { + [PreserveSig] + int GetTypeInfoCount(); + void GetTypeInfo(int index, out ITypeInfo ppTI); + void GetTypeInfoType(int index, out TYPEKIND pTKind); + void GetTypeInfoOfGuid(ref Guid guid, out ITypeInfo ppTInfo); + void GetLibAttr(out IntPtr ppTLibAttr); + void GetTypeComp(out ITypeComp ppTComp); + void GetDocumentation(int index, out String strName, out String strDocString, out int dwHelpContext, out String strHelpFile); + [return : MarshalAs(UnmanagedType.Bool)] + bool IsName([MarshalAs(UnmanagedType.LPWStr)] String szNameBuf, int lHashVal); + void FindName([MarshalAs(UnmanagedType.LPWStr)] String szNameBuf, int lHashVal, [MarshalAs(UnmanagedType.LPArray), Out] ITypeInfo[] ppTInfo, [MarshalAs(UnmanagedType.LPArray), Out] int[] rgMemId, ref Int16 pcFound); + [PreserveSig] + void ReleaseTLibAttr(IntPtr pTLibAttr); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeLib2.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeLib2.cs new file mode 100644 index 0000000000..5a7d07c001 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ComTypes/ITypeLib2.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: ITypeLib2 interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices.ComTypes +{ + using System; + + [Guid("00020411-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface ITypeLib2 : ITypeLib + { + [PreserveSig] + new int GetTypeInfoCount(); + new void GetTypeInfo(int index, out ITypeInfo ppTI); + new void GetTypeInfoType(int index, out TYPEKIND pTKind); + new void GetTypeInfoOfGuid(ref Guid guid, out ITypeInfo ppTInfo); + new void GetLibAttr(out IntPtr ppTLibAttr); + new void GetTypeComp(out ITypeComp ppTComp); + new void GetDocumentation(int index, out String strName, out String strDocString, out int dwHelpContext, out String strHelpFile); + [return : MarshalAs(UnmanagedType.Bool)] + new bool IsName([MarshalAs(UnmanagedType.LPWStr)] String szNameBuf, int lHashVal); + new void FindName([MarshalAs(UnmanagedType.LPWStr)] String szNameBuf, int lHashVal, [MarshalAs(UnmanagedType.LPArray), Out] ITypeInfo[] ppTInfo, [MarshalAs(UnmanagedType.LPArray), Out] int[] rgMemId, ref Int16 pcFound); + [PreserveSig] + new void ReleaseTLibAttr(IntPtr pTLibAttr); + void GetCustData(ref Guid guid, out Object pVarVal); + [LCIDConversionAttribute(1)] + void GetDocumentation2(int index, out String pbstrHelpString, out int pdwHelpStringContext, out String pbstrHelpStringDll); + void GetLibStatistics(IntPtr pcUniqueNames, out int pcchUniqueNames); + void GetAllCustData(IntPtr pCustData); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs b/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs new file mode 100644 index 0000000000..54fd6b0cdd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs @@ -0,0 +1,275 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** A specially designed handle wrapper to ensure we never leak +** an OS handle. The runtime treats this class specially during +** P/Invoke marshaling and finalization. Users should write +** subclasses of CriticalHandle for each distinct handle type. +** This class is similar to SafeHandle, but lacks the ref counting +** behavior on marshaling that prevents handle recycling errors +** or security holes. This lowers the overhead of using the handle +** considerably, but leaves the onus on the caller to protect +** themselves from any recycling effects. +** +** **** NOTE **** +** +** Since there are no ref counts tracking handle usage there is +** no thread safety either. Your application must ensure that +** usages of the handle do not cross with attempts to close the +** handle (or tolerate such crossings). Normal GC mechanics will +** prevent finalization until the handle class isn't used any more, +** but explicit Close or Dispose operations may be initiated at any +** time. +** +** Similarly, multiple calls to Close or Dispose on different +** threads at the same time may cause the ReleaseHandle method to be +** called more than once. +** +** In general (and as might be inferred from the lack of handle +** recycle protection) you should be very cautious about exposing +** CriticalHandle instances directly or indirectly to untrusted users. +** At a minimum you should restrict their ability to queue multiple +** operations against a single handle at the same time or block their +** access to Close and Dispose unless you are very comfortable with the +** semantics of passing an invalid (or possibly invalidated and +** reallocated) to the unamanged routines you marshal your handle to +** (and the effects of closing such a handle while those calls are in +** progress). The runtime cannot protect you from undefined program +** behvior that might result from such scenarios. You have been warned. +** +** +===========================================================*/ + +using System; +using System.Reflection; +using System.Threading; +using System.Security.Permissions; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using System.Runtime.ConstrainedExecution; +using System.IO; + +/* + Problems addressed by the CriticalHandle class: + 1) Critical finalization - ensure we never leak OS resources in SQL. Done + without running truly arbitrary & unbounded amounts of managed code. + 2) Reduced graph promotion - during finalization, keep object graph small + 3) GC.KeepAlive behavior - P/Invoke vs. finalizer thread race condition (HandleRef) + 4) Enforcement of the above via the type system - Don't use IntPtr anymore. + + Subclasses of CriticalHandle will implement the ReleaseHandle + abstract method used to execute any code required to free the + handle. This method will be prepared as a constrained execution + region at instance construction time (along with all the methods in + its statically determinable call graph). This implies that we won't + get any inconvenient jit allocation errors or rude thread abort + interrupts while releasing the handle but the user must still write + careful code to avoid injecting fault paths of their own (see the + CER spec for more details). In particular, any sub-methods you call + should be decorated with a reliability contract of the appropriate + level. In most cases this should be: + ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success) + Also, any P/Invoke methods should use the + SuppressUnmanagedCodeSecurity attribute to avoid a runtime security + check that can also inject failures (even if the check is guaranteed + to pass). + + Subclasses must also implement the IsInvalid property so that the + infrastructure can tell when critical finalization is actually required. + Again, this method is prepared ahead of time. It's envisioned that direct + subclasses of CriticalHandle will provide an IsInvalid implementation that suits + the general type of handle they support (null is invalid, -1 is invalid etc.) + and then these classes will be further derived for specific handle types. + + Most classes using CriticalHandle should not provide a finalizer. If they do + need to do so (ie, for flushing out file buffers, needing to write some data + back into memory, etc), then they can provide a finalizer that will be + guaranteed to run before the CriticalHandle's critical finalizer. + + Subclasses are expected to be written as follows (note that + SuppressUnmanagedCodeSecurity should always be used on any P/Invoke methods + invoked as part of ReleaseHandle, in order to switch the security check from + runtime to jit time and thus remove a possible failure path from the + invocation of the method): + + internal sealed MyCriticalHandleSubclass : CriticalHandle { + // Called by P/Invoke when returning CriticalHandles + private MyCriticalHandleSubclass() : base(IntPtr.Zero) + { + } + + // Do not provide a finalizer - CriticalHandle's critical finalizer will + // call ReleaseHandle for you. + + public override bool IsInvalid { + get { return handle == IntPtr.Zero; } + } + + [DllImport(Win32Native.KERNEL32), SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static extern bool CloseHandle(IntPtr handle); + + override protected bool ReleaseHandle() + { + return CloseHandle(handle); + } + } + + Then elsewhere to create one of these CriticalHandles, define a method + with the following type of signature (CreateFile follows this model). + Note that when returning a CriticalHandle like this, P/Invoke will call your + classes default constructor. + + [DllImport(Win32Native.KERNEL32)] + private static extern MyCriticalHandleSubclass CreateHandle(int someState); + + */ + +namespace System.Runtime.InteropServices +{ + +// This class should not be serializable - it's a handle. We require unmanaged +// code permission to subclass CriticalHandle to prevent people from writing a +// subclass and suddenly being able to run arbitrary native code with the +// same signature as CloseHandle. This is technically a little redundant, but +// we'll do this to ensure we've cut off all attack vectors. Similarly, all +// methods have a link demand to ensure untrusted code cannot directly edit +// or alter a handle. +[System.Security.SecurityCritical] // auto-generated_required +#if !FEATURE_CORECLR +[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode=true)] +#endif +public abstract class CriticalHandle : CriticalFinalizerObject, IDisposable +{ + // ! Do not add or rearrange fields as the EE depends on this layout. + //------------------------------------------------------------------ +#if DEBUG + private String _stackTrace; // Where we allocated this CriticalHandle. +#endif + protected IntPtr handle; // This must be protected so derived classes can use out params. + private bool _isClosed; // Set by SetHandleAsInvalid or Close/Dispose/finalization. + + // Creates a CriticalHandle class. Users must then set the Handle property or allow P/Invoke marshaling to set it implicitly. + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected CriticalHandle(IntPtr invalidHandleValue) + { + handle = invalidHandleValue; + _isClosed = false; + +#if DEBUG + if (BCLDebug.SafeHandleStackTracesEnabled) + _stackTrace = Environment.GetStackTrace(null, false); + else + _stackTrace = "For a stack trace showing who allocated this CriticalHandle, set SafeHandleStackTraces to 1 and rerun your app."; +#endif + } + +#if FEATURE_CORECLR + // Adding an empty default constructor for annotation purposes + private CriticalHandle(){} +#endif + + [System.Security.SecuritySafeCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + ~CriticalHandle() + { + Dispose(false); + } + + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private void Cleanup() + { + if (IsClosed) + return; + _isClosed = true; + + if (IsInvalid) + return; + + // Save last error from P/Invoke in case the implementation of + // ReleaseHandle trashes it (important because this ReleaseHandle could + // occur implicitly as part of unmarshaling another P/Invoke). + int lastError = Marshal.GetLastWin32Error(); + + if (!ReleaseHandle()) + FireCustomerDebugProbe(); + + Marshal.SetLastWin32Error(lastError); + + GC.SuppressFinalize(this); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private extern void FireCustomerDebugProbe(); + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected void SetHandle(IntPtr handle) { + this.handle = handle; + } + + // Returns whether the handle has been explicitly marked as closed + // (Close/Dispose) or invalid (SetHandleAsInvalid). + public bool IsClosed { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get { return _isClosed; } + } + + // Returns whether the handle looks like an invalid value (i.e. matches one + // of the handle's designated illegal values). CriticalHandle itself doesn't + // know what an invalid handle looks like, so this method is abstract and + // must be provided by a derived type. + public abstract bool IsInvalid { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get; + } + + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public void Close() { + Dispose(true); + } + + [System.Security.SecuritySafeCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public void Dispose() + { + Dispose(true); + } + + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected virtual void Dispose(bool disposing) + { + Cleanup(); + } + + // This should only be called for cases when you know for a fact that + // your handle is invalid and you want to record that information. + // An example is calling a syscall and getting back ERROR_INVALID_HANDLE. + // This method will normally leak handles! + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public void SetHandleAsInvalid() + { + _isClosed = true; + GC.SuppressFinalize(this); + } + + // Implement this abstract method in your derived class to specify how to + // free the handle. Be careful not write any code that's subject to faults + // in this method (the runtime will prepare the infrastructure for you so + // that no jit allocations etc. will occur, but don't allocate memory unless + // you can deal with the failure and still free the handle). + // The boolean returned should be true for success and false if a + // catastrophic error occurred and you wish to trigger a diagnostic for + // debugging purposes (the SafeHandleCriticalFailure MDA). + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected abstract bool ReleaseHandle(); +} + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/CurrencyWrapper.cs b/src/mscorlib/src/System/Runtime/InteropServices/CurrencyWrapper.cs new file mode 100644 index 0000000000..1f7a9f18bf --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/CurrencyWrapper.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Wrapper that is converted to a variant with VT_CURRENCY. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class CurrencyWrapper + { + public CurrencyWrapper(Decimal obj) + { + m_WrappedObject = obj; + } + + public CurrencyWrapper(Object obj) + { + if (!(obj is Decimal)) + throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDecimal"), "obj"); + m_WrappedObject = (Decimal)obj; + } + + public Decimal WrappedObject + { + get + { + return m_WrappedObject; + } + } + + private Decimal m_WrappedObject; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/DispatchWrapper.cs b/src/mscorlib/src/System/Runtime/InteropServices/DispatchWrapper.cs new file mode 100644 index 0000000000..1fc72f74c8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/DispatchWrapper.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Wrapper that is converted to a variant with VT_DISPATCH. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Security; + using System.Security.Permissions; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class DispatchWrapper + { + [System.Security.SecuritySafeCritical] // auto-generated +#pragma warning disable 618 + [SecurityPermissionAttribute(SecurityAction.Demand,Flags=SecurityPermissionFlag.UnmanagedCode)] +#pragma warning restore 618 + public DispatchWrapper(Object obj) + { + if (obj != null) + { + // Make sure this guy has an IDispatch + IntPtr pdisp = Marshal.GetIDispatchForObject(obj); + + // If we got here without throwing an exception, the QI for IDispatch succeeded. + Marshal.Release(pdisp); + } + m_WrappedObject = obj; + } + + public Object WrappedObject + { + get + { + return m_WrappedObject; + } + } + + private Object m_WrappedObject; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ErrorWrapper.cs b/src/mscorlib/src/System/Runtime/InteropServices/ErrorWrapper.cs new file mode 100644 index 0000000000..d63d69cabd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ErrorWrapper.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Wrapper that is converted to a variant with VT_ERROR. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Security.Permissions; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class ErrorWrapper + { + public ErrorWrapper(int errorCode) + { + m_ErrorCode = errorCode; + } + + public ErrorWrapper(Object errorCode) + { + if (!(errorCode is int)) + throw new ArgumentException(Environment.GetResourceString("Arg_MustBeInt32"), "errorCode"); + m_ErrorCode = (int)errorCode; + } + + [System.Security.SecuritySafeCritical] // auto-generated +#pragma warning disable 618 + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] +#pragma warning restore 618 + public ErrorWrapper(Exception e) + { + m_ErrorCode = Marshal.GetHRForException(e); + } + + public int ErrorCode + { + get + { + return m_ErrorCode; + } + } + + private int m_ErrorCode; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Expando/IExpando.cs b/src/mscorlib/src/System/Runtime/InteropServices/Expando/IExpando.cs new file mode 100644 index 0000000000..62b65d1aff --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/Expando/IExpando.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// IExpando is an interface which allows Objects implemeningt this interface +// support the ability to modify the object by adding and removing members, +// represented by MemberInfo objects. +// +// +// The IExpando Interface. +namespace System.Runtime.InteropServices.Expando { + + using System; + using System.Reflection; + + [Guid("AFBF15E6-C37C-11d2-B88E-00A0C9B471B8")] +[System.Runtime.InteropServices.ComVisible(true)] + public interface IExpando : IReflect + { + // Add a new Field to the reflection object. The field has + // name as its name. + FieldInfo AddField(String name); + + // Add a new Property to the reflection object. The property has + // name as its name. + PropertyInfo AddProperty(String name); + + // Add a new Method to the reflection object. The method has + // name as its name and method is a delegate + // to the method. + MethodInfo AddMethod(String name, Delegate method); + + // Removes the specified member. + void RemoveMember(MemberInfo m); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ExtensibleClassFactory.cs b/src/mscorlib/src/System/Runtime/InteropServices/ExtensibleClassFactory.cs new file mode 100644 index 0000000000..62718de757 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ExtensibleClassFactory.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Methods used to customize the creation of managed objects that +** extend from unmanaged objects. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System.Runtime.InteropServices; + using System.Runtime.Remoting; + using System.Runtime.CompilerServices; + using System.Runtime.Versioning; + + using System; +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class ExtensibleClassFactory + { + + // Prevent instantiation. + private ExtensibleClassFactory() {} + + // Register a delegate that will be called whenever an instance of a managed + // type that extends from an unmanaged type needs to allocate the aggregated + // unmanaged object. This delegate is expected to allocate and aggregate the + // unmanaged object and is called in place of a CoCreateInstance. This + // routine must be called in the context of the static initializer for the + // class for which the callbacks will be made. + // It is not legal to register this callback from a class that has any + // parents that have already registered a callback. + [System.Security.SecuritySafeCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void RegisterObjectCreationCallback(ObjectCreationDelegate callback); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ExternalException.cs b/src/mscorlib/src/System/Runtime/InteropServices/ExternalException.cs new file mode 100644 index 0000000000..417cf94bd4 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ExternalException.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Exception base class for all errors from Interop or Structured +** Exception Handling code. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Globalization; + using System.Runtime.Serialization; + // Base exception for COM Interop errors &; Structured Exception Handler + // exceptions. + // + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class ExternalException : SystemException { + public ExternalException() + : base(Environment.GetResourceString("Arg_ExternalException")) { + SetErrorCode(__HResults.E_FAIL); + } + + public ExternalException(String message) + : base(message) { + SetErrorCode(__HResults.E_FAIL); + } + + public ExternalException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.E_FAIL); + } + + public ExternalException(String message,int errorCode) + : base(message) { + SetErrorCode(errorCode); + } + + protected ExternalException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + + public virtual int ErrorCode { + get { + return HResult; + } + } + +#if !FEATURE_CORECLR // Breaks the subset-of-Orcas property + public override String ToString() { + String message = Message; + String s; + String _className = GetType().ToString(); + s = _className + " (0x" + HResult.ToString("X8", CultureInfo.InvariantCulture) + ")"; + + if (!(String.IsNullOrEmpty(message))) { + s = s + ": " + message; + } + + Exception _innerException = InnerException; + + if (_innerException!=null) { + s = s + " ---> " + _innerException.ToString(); + } + + + if (StackTrace != null) + s += Environment.NewLine + StackTrace; + + return s; + } +#endif // !FEATURE_CORECLR + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/GCHandleCookieTable.cs b/src/mscorlib/src/System/Runtime/InteropServices/GCHandleCookieTable.cs new file mode 100644 index 0000000000..304f369879 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/GCHandleCookieTable.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if MDA_SUPPORTED + +namespace System.Runtime.InteropServices +{ + using System; + using System.Collections.Generic; + using System.Threading; + + using ObjectHandle = IntPtr; + using GCHandleCookie = IntPtr; + + // Internal class used to map a GCHandle to an IntPtr. Instead of handing out the underlying CLR + // handle, we now hand out a cookie that can later be converted back to the CLR handle it + // is associated with. + + // NOTE: + // this implementation uses a single lock between FindOrAddHandle and RemoveHandleIfPresent which + // could create some scalability issues when this MDA is turned on. if this is affecting perf + // then additional tuning work will be required. + + internal class GCHandleCookieTable + { + private const int InitialHandleCount = 10; + private const int MaxListSize = 0xFFFFFF; + private const uint CookieMaskIndex = 0x00FFFFFF; + private const uint CookieMaskSentinal = 0xFF000000; + + internal GCHandleCookieTable() + { + m_HandleList = new ObjectHandle[InitialHandleCount]; + m_CycleCounts = new byte[InitialHandleCount]; + m_HandleToCookieMap = new Dictionary<ObjectHandle, GCHandleCookie>(InitialHandleCount); + m_syncObject = new object(); + + for (int i = 0; i < InitialHandleCount; i++) + { + m_HandleList[i] = ObjectHandle.Zero; + m_CycleCounts[i] = 0; + } + } + + // Retrieve a cookie for the passed in handle. If no cookie has yet been allocated for + // this handle, one will be created. This method is thread safe. + internal GCHandleCookie FindOrAddHandle(ObjectHandle handle) + { + // Don't accept a null object handle + if (handle == ObjectHandle.Zero) + return GCHandleCookie.Zero; + + GCHandleCookie cookie = GCHandleCookie.Zero; + + lock (m_syncObject) + { + // First see if we already have a cookie for this handle. + if (m_HandleToCookieMap.ContainsKey(handle)) + return m_HandleToCookieMap[handle]; + + if ((m_FreeIndex < m_HandleList.Length) && (Volatile.Read(ref m_HandleList[m_FreeIndex]) == ObjectHandle.Zero)) + { + Volatile.Write(ref m_HandleList[m_FreeIndex], handle); + cookie = GetCookieFromData((uint)m_FreeIndex, m_CycleCounts[m_FreeIndex]); + + // Set our next guess just one higher as this index is now in use. + // it's ok if this sets m_FreeIndex > m_HandleList.Length as this condition is + // checked at the beginning of the if statement. + ++m_FreeIndex; + } + else + { + for (m_FreeIndex = 0; m_FreeIndex < MaxListSize; ++m_FreeIndex) + { + if (m_HandleList[m_FreeIndex] == ObjectHandle.Zero) + { + Volatile.Write(ref m_HandleList[m_FreeIndex], handle); + cookie = GetCookieFromData((uint)m_FreeIndex, m_CycleCounts[m_FreeIndex]); + + // this will be our next guess for a free index. + // it's ok if this sets m_FreeIndex > m_HandleList.Length + // since we check for this condition in the if statement. + ++m_FreeIndex; + break; + } + + if (m_FreeIndex + 1 == m_HandleList.Length) + GrowArrays(); + } + } + + if (cookie == GCHandleCookie.Zero) + throw new OutOfMemoryException(Environment.GetResourceString("OutOfMemory_GCHandleMDA")); + + // This handle hasn't been added to the map yet so add it. + m_HandleToCookieMap.Add(handle, cookie); + } + + return cookie; + } + + // Get a handle. + internal ObjectHandle GetHandle(GCHandleCookie cookie) + { + ObjectHandle oh = ObjectHandle.Zero; + + if (!ValidateCookie(cookie)) + return ObjectHandle.Zero; + + oh = Volatile.Read(ref m_HandleList[GetIndexFromCookie(cookie)]); + + return oh; + } + + // Remove the handle from the cookie table if it is present. + // + internal void RemoveHandleIfPresent(ObjectHandle handle) + { + if (handle == ObjectHandle.Zero) + return; + + lock (m_syncObject) + { + if (m_HandleToCookieMap.ContainsKey(handle)) + { + GCHandleCookie cookie = m_HandleToCookieMap[handle]; + + // Remove it from the array first + if (!ValidateCookie(cookie)) + return; + + int index = GetIndexFromCookie(cookie); + + m_CycleCounts[index]++; + Volatile.Write(ref m_HandleList[index], ObjectHandle.Zero); + + // Remove it from the hashtable last + m_HandleToCookieMap.Remove(handle); + + // Update our guess + m_FreeIndex = index; + } + } + } + + private bool ValidateCookie(GCHandleCookie cookie) + { + int index; + byte xorData; + + GetDataFromCookie(cookie, out index, out xorData); + + // Validate the index + if (index >= MaxListSize) + return false; + + if (index >= m_HandleList.Length) + return false; + + if (Volatile.Read(ref m_HandleList[index]) == ObjectHandle.Zero) + return false; + + // Validate the xorData byte (this contains the cycle count and appdomain id). + byte ADID = (byte)(AppDomain.CurrentDomain.Id % 0xFF); + byte goodData = (byte)(Volatile.Read(ref m_CycleCounts[index]) ^ ADID); + if (xorData != goodData) + return false; + + return true; + } + + // Double the size of our arrays - must be called with the lock taken. + private void GrowArrays() + { + int CurrLength = m_HandleList.Length; + + ObjectHandle[] newHandleList = new ObjectHandle[CurrLength * 2]; + byte[] newCycleCounts = new byte[CurrLength * 2]; + + Array.Copy(m_HandleList, newHandleList, CurrLength); + Array.Copy(m_CycleCounts, newCycleCounts, CurrLength); + + m_HandleList = newHandleList; + m_CycleCounts = newCycleCounts; + } + + // Generate a cookie based on the index, cyclecount, and current domain id. + private GCHandleCookie GetCookieFromData(uint index, byte cycleCount) + { + byte ADID = (byte)(AppDomain.CurrentDomain.Id % 0xFF); + return (GCHandleCookie)(((cycleCount ^ ADID) << 24) + index + 1); + } + + // Break down the cookie into its parts + private void GetDataFromCookie(GCHandleCookie cookie, out int index, out byte xorData) + { + uint intCookie = (uint)cookie; + index = (int)(intCookie & CookieMaskIndex) - 1; + xorData = (byte)((intCookie & CookieMaskSentinal) >> 24); + } + + // Just get the index from the cookie + private int GetIndexFromCookie(GCHandleCookie cookie) + { + uint intCookie = (uint)cookie; + return (int)(intCookie & CookieMaskIndex) - 1; + } + + private Dictionary<ObjectHandle, GCHandleCookie> m_HandleToCookieMap; + private volatile ObjectHandle[] m_HandleList; + private volatile byte[] m_CycleCounts; + private int m_FreeIndex; + private readonly object m_syncObject; + } +} + +#endif + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/GcHandle.cs b/src/mscorlib/src/System/Runtime/InteropServices/GcHandle.cs new file mode 100644 index 0000000000..58fea97cb8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/GcHandle.cs @@ -0,0 +1,330 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + using System; + using System.Security.Permissions; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Runtime.Versioning; + using System.Diagnostics.Contracts; + + // These are the types of handles used by the EE. + // IMPORTANT: These must match the definitions in ObjectHandle.h in the EE. + // IMPORTANT: If new values are added to the enum the GCHandle::MaxHandleType + // constant must be updated. + [Serializable] + [System.Runtime.InteropServices.ComVisible(true)] + public enum GCHandleType + { + Weak = 0, + WeakTrackResurrection = 1, + Normal = 2, + Pinned = 3 + } + + // This class allows you to create an opaque, GC handle to any + // COM+ object. A GC handle is used when an object reference must be + // reachable from unmanaged memory. There are 3 kinds of roots: + // Normal - keeps the object from being collected. + // Weak - allows object to be collected and handle contents will be zeroed. + // Weak references are zeroed before the finalizer runs, so if the + // object is resurrected in the finalizer the weak reference is + // still zeroed. + // WeakTrackResurrection - Same as weak, but stays until after object is + // really gone. + // Pinned - same as normal, but allows the address of the actual object + // to be taken. + // + + [StructLayout(LayoutKind.Sequential)] + [System.Runtime.InteropServices.ComVisible(true)] + public struct GCHandle + { + // IMPORTANT: This must be kept in sync with the GCHandleType enum. + private const GCHandleType MaxHandleType = GCHandleType.Pinned; + +#if MDA_SUPPORTED + [System.Security.SecuritySafeCritical] // auto-generated + static GCHandle() + { + s_probeIsActive = Mda.IsInvalidGCHandleCookieProbeEnabled(); + if (s_probeIsActive) + s_cookieTable = new GCHandleCookieTable(); + } +#endif + + // Allocate a handle storing the object and the type. + [System.Security.SecurityCritical] // auto-generated + internal GCHandle(Object value, GCHandleType type) + { + // Make sure the type parameter is within the valid range for the enum. + if ((uint)type > (uint)MaxHandleType) + throw new ArgumentOutOfRangeException("type", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + Contract.EndContractBlock(); + + m_handle = InternalAlloc(value, type); + + // Record if the handle is pinned. + if (type == GCHandleType.Pinned) + SetIsPinned(); + } + + // Used in the conversion functions below. + [System.Security.SecurityCritical] // auto-generated + internal GCHandle(IntPtr handle) + { + InternalCheckDomain(handle); + m_handle = handle; + } + + // Creates a new GC handle for an object. + // + // value - The object that the GC handle is created for. + // type - The type of GC handle to create. + // + // returns a new GC handle that protects the object. + [System.Security.SecurityCritical] // auto-generated_required + public static GCHandle Alloc(Object value) + { + return new GCHandle(value, GCHandleType.Normal); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static GCHandle Alloc(Object value, GCHandleType type) + { + return new GCHandle(value, type); + } + + + // Frees a GC handle. + [System.Security.SecurityCritical] // auto-generated_required + public void Free() + { + // Copy the handle instance member to a local variable. This is required to prevent + // race conditions releasing the handle. + IntPtr handle = m_handle; + + // Free the handle if it hasn't already been freed. + if (handle != IntPtr.Zero && Interlocked.CompareExchange(ref m_handle, IntPtr.Zero, handle) == handle) + { +#if MDA_SUPPORTED + // If this handle was passed out to unmanaged code, we need to remove it + // from the cookie table. + // NOTE: the entry in the cookie table must be released before the + // internal handle is freed to prevent a race with reusing GC handles. + if (s_probeIsActive) + s_cookieTable.RemoveHandleIfPresent(handle); +#endif + +#if BIT64 + InternalFree((IntPtr)(((long)handle) & ~1L)); +#else // BIT64 (32) + InternalFree((IntPtr)(((int)handle) & ~1)); +#endif + } + else + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized")); + } + } + + // Target property - allows getting / updating of the handle's referent. + public Object Target + { + [System.Security.SecurityCritical] // auto-generated_required + get + { + // Check if the handle was never initialized or was freed. + if (m_handle == IntPtr.Zero) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized")); + + return InternalGet(GetHandleValue()); + } + + [System.Security.SecurityCritical] // auto-generated_required + set + { + // Check if the handle was never initialized or was freed. + if (m_handle == IntPtr.Zero) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized")); + + InternalSet(GetHandleValue(), value, IsPinned()); + } + } + + // Retrieve the address of an object in a Pinned handle. This throws + // an exception if the handle is any type other than Pinned. + [System.Security.SecurityCritical] // auto-generated_required + public IntPtr AddrOfPinnedObject() + { + // Check if the handle was not a pinned handle. + if (!IsPinned()) + { + // Check if the handle was never initialized for was freed. + if (m_handle == IntPtr.Zero) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized")); + + // You can only get the address of pinned handles. + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotPinned")); + } + + // Get the address. + return InternalAddrOfPinnedObject(GetHandleValue()); + } + + // Determine whether this handle has been allocated or not. + public bool IsAllocated + { + get + { + return m_handle != IntPtr.Zero; + } + } + + // Used to create a GCHandle from an int. This is intended to + // be used with the reverse conversion. + [System.Security.SecurityCritical] // auto-generated_required + public static explicit operator GCHandle(IntPtr value) + { + return FromIntPtr(value); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static GCHandle FromIntPtr(IntPtr value) + { + if (value == IntPtr.Zero) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized")); + Contract.EndContractBlock(); + + IntPtr handle = value; + +#if MDA_SUPPORTED + if (s_probeIsActive) + { + // Make sure this cookie matches up with a GCHandle we've passed out a cookie for. + handle = s_cookieTable.GetHandle(value); + if (IntPtr.Zero == handle) + { + // Fire an MDA if we were unable to retrieve the GCHandle. + Mda.FireInvalidGCHandleCookieProbe(value); + return new GCHandle(IntPtr.Zero); + } + } +#endif + + return new GCHandle(handle); + } + + // Used to get the internal integer representation of the handle out. + public static explicit operator IntPtr(GCHandle value) + { + return ToIntPtr(value); + } + + public static IntPtr ToIntPtr(GCHandle value) + { +#if MDA_SUPPORTED + if (s_probeIsActive) + { + // Remember that we passed this GCHandle out by storing the cookie we returned so we + // can later validate. + return s_cookieTable.FindOrAddHandle(value.m_handle); + } +#endif + return value.m_handle; + } + + public override int GetHashCode() + { + return m_handle.GetHashCode(); + } + + public override bool Equals(Object o) + { + GCHandle hnd; + + // Check that o is a GCHandle first + if(o == null || !(o is GCHandle)) + return false; + else + hnd = (GCHandle) o; + + return m_handle == hnd.m_handle; + } + + public static bool operator ==(GCHandle a, GCHandle b) + { + return a.m_handle == b.m_handle; + } + + public static bool operator !=(GCHandle a, GCHandle b) + { + return a.m_handle != b.m_handle; + } + + internal IntPtr GetHandleValue() + { +#if BIT64 + return new IntPtr(((long)m_handle) & ~1L); +#else // !BIT64 (32) + return new IntPtr(((int)m_handle) & ~1); +#endif + } + + internal bool IsPinned() + { +#if BIT64 + return (((long)m_handle) & 1) != 0; +#else // !BIT64 (32) + return (((int)m_handle) & 1) != 0; +#endif + } + + internal void SetIsPinned() + { +#if BIT64 + m_handle = new IntPtr(((long)m_handle) | 1L); +#else // !BIT64 (32) + m_handle = new IntPtr(((int)m_handle) | 1); +#endif + } + + // Internal native calls that this implementation uses. + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern IntPtr InternalAlloc(Object value, GCHandleType type); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void InternalFree(IntPtr handle); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern Object InternalGet(IntPtr handle); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void InternalSet(IntPtr handle, Object value, bool isPinned); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern Object InternalCompareExchange(IntPtr handle, Object value, Object oldValue, bool isPinned); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern IntPtr InternalAddrOfPinnedObject(IntPtr handle); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void InternalCheckDomain(IntPtr handle); + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern GCHandleType InternalGetHandleType(IntPtr handle); + + // The actual integer handle value that the EE uses internally. + private IntPtr m_handle; + +#if MDA_SUPPORTED + // The GCHandle cookie table. + static private volatile GCHandleCookieTable s_cookieTable = null; + static private volatile bool s_probeIsActive = false; +#endif + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/HandleRef.cs b/src/mscorlib/src/System/Runtime/InteropServices/HandleRef.cs new file mode 100644 index 0000000000..d76750fdd7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/HandleRef.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + + using System; + + [System.Runtime.InteropServices.ComVisible(true)] + public struct HandleRef + { + + // ! Do not add or rearrange fields as the EE depends on this layout. + //------------------------------------------------------------------ + internal Object m_wrapper; + internal IntPtr m_handle; + //------------------------------------------------------------------ + + + public HandleRef(Object wrapper, IntPtr handle) + { + m_wrapper = wrapper; + m_handle = handle; + } + + public Object Wrapper { + get { + return m_wrapper; + } + } + + public IntPtr Handle { + get { + return m_handle; + } + } + + + public static explicit operator IntPtr(HandleRef value) + { + return value.m_handle; + } + + public static IntPtr ToIntPtr(HandleRef value) + { + return value.m_handle; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ICustomAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/ICustomAdapter.cs new file mode 100644 index 0000000000..00abf7b3bf --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ICustomAdapter.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: This the base interface that custom adapters can chose to implement +** when they want to expose the underlying object. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System; + +[System.Runtime.InteropServices.ComVisible(true)] + public interface ICustomAdapter + { + [return:MarshalAs(UnmanagedType.IUnknown)] Object GetUnderlyingObject(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ICustomFactory.cs b/src/mscorlib/src/System/Runtime/InteropServices/ICustomFactory.cs new file mode 100644 index 0000000000..33d2556bd0 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ICustomFactory.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices { + + using System; + +[System.Runtime.InteropServices.ComVisible(true)] + public interface ICustomFactory + { + MarshalByRefObject CreateInstance(Type serverType); + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ICustomMarshaler.cs b/src/mscorlib/src/System/Runtime/InteropServices/ICustomMarshaler.cs new file mode 100644 index 0000000000..4db4acceeb --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ICustomMarshaler.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: This the base interface that must be implemented by all custom +** marshalers. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System; + +[System.Runtime.InteropServices.ComVisible(true)] + public interface ICustomMarshaler + { + Object MarshalNativeToManaged( IntPtr pNativeData ); + + IntPtr MarshalManagedToNative( Object ManagedObj ); + + void CleanUpNativeData( IntPtr pNativeData ); + + void CleanUpManagedData( Object ManagedObj ); + + int GetNativeDataSize(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ICustomQueryInterface.cs b/src/mscorlib/src/System/Runtime/InteropServices/ICustomQueryInterface.cs new file mode 100644 index 0000000000..61688b90b4 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ICustomQueryInterface.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: This the interface that be implemented by class that want to +** customize the behavior of QueryInterface. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System; + + //==================================================================== + // The enum of the return value of IQuerable.GetInterface + //==================================================================== + [Serializable] + [System.Runtime.InteropServices.ComVisible(false)] + public enum CustomQueryInterfaceResult + { + Handled = 0, + NotHandled = 1, + Failed = 2, + } + + //==================================================================== + // The interface for customizing IQueryInterface + //==================================================================== + [System.Runtime.InteropServices.ComVisible(false)] + public interface ICustomQueryInterface + { + [System.Security.SecurityCritical] + CustomQueryInterfaceResult GetInterface([In]ref Guid iid, out IntPtr ppv); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/IException.cs b/src/mscorlib/src/System/Runtime/InteropServices/IException.cs new file mode 100644 index 0000000000..2da0a564a2 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/IException.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** Interface: _Exception +** +** +** Purpose: COM backwards compatibility with v1 Exception +** object layout. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System; + using System.Reflection; + using System.Runtime.Serialization; + using System.Security.Permissions; + + [GuidAttribute("b36b5c63-42ef-38bc-a07e-0b34c98f164a")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)] + [CLSCompliant(false)] +[System.Runtime.InteropServices.ComVisible(true)] + public interface _Exception + { +#if !FEATURE_CORECLR + // This contains all of our V1 Exception class's members. + + // From Object + String ToString(); + bool Equals (Object obj); + int GetHashCode (); + Type GetType (); + + // From V1's Exception class + String Message { + get; + } + + Exception GetBaseException(); + + String StackTrace { + get; + } + + String HelpLink { + get; + set; + } + + String Source { + #if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + #endif + get; + #if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + #endif + set; + } + [System.Security.SecurityCritical] // auto-generated_required + void GetObjectData(SerializationInfo info, StreamingContext context); +#endif + + // + // This method is intentionally included in CoreCLR to make Exception.get_InnerException "newslot virtual final". + // Some phone apps include MEF from desktop Silverlight. MEF's ComposablePartException depends on implicit interface + // implementations of get_InnerException to be provided by the base class. It works only if Exception.get_InnerException + // is virtual. + // + Exception InnerException { + get; + } + +#if !FEATURE_CORECLR + MethodBase TargetSite { + get; + } +#endif + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/IRegistrationServices.cs b/src/mscorlib/src/System/Runtime/InteropServices/IRegistrationServices.cs new file mode 100644 index 0000000000..ae330e8652 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/IRegistrationServices.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: This interface provides services for registering and unregistering +** a managed server for use by COM. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + + [Flags()] +[System.Runtime.InteropServices.ComVisible(true)] + public enum AssemblyRegistrationFlags + { + None = 0x00000000, + SetCodeBase = 0x00000001, + } + + [Guid("CCBD682C-73A5-4568-B8B0-C7007E11ABA2")] +[System.Runtime.InteropServices.ComVisible(true)] + public interface IRegistrationServices + { + [System.Security.SecurityCritical] // auto-generated_required + bool RegisterAssembly(Assembly assembly, AssemblyRegistrationFlags flags); + + [System.Security.SecurityCritical] // auto-generated_required + bool UnregisterAssembly(Assembly assembly); + + [System.Security.SecurityCritical] // auto-generated_required + Type[] GetRegistrableTypesInAssembly(Assembly assembly); + + [System.Security.SecurityCritical] // auto-generated_required + String GetProgIdForType(Type type); + + [System.Security.SecurityCritical] // auto-generated_required + void RegisterTypeForComClients(Type type, ref Guid g); + + Guid GetManagedCategoryGuid(); + + [System.Security.SecurityCritical] // auto-generated_required + bool TypeRequiresRegistration(Type type); + + bool TypeRepresentsComType(Type type); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ITypeLibConverter.cs b/src/mscorlib/src/System/Runtime/InteropServices/ITypeLibConverter.cs new file mode 100644 index 0000000000..a7b6889c55 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ITypeLibConverter.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Methods used to convert a TypeLib to metadata and vice versa. +** +** +=============================================================================*/ + +// *************************************************************************** +// *** Note: The following definitions must remain synchronized with the IDL +// *** in src/inc/TlbImpExp.idl. +// *************************************************************************** + +namespace System.Runtime.InteropServices { + + using System; + using System.Reflection; + using System.Reflection.Emit; + +[Serializable] +[Flags()] +[System.Runtime.InteropServices.ComVisible(true)] + public enum TypeLibImporterFlags + { + None = 0x00000000, + PrimaryInteropAssembly = 0x00000001, + UnsafeInterfaces = 0x00000002, + SafeArrayAsSystemArray = 0x00000004, + TransformDispRetVals = 0x00000008, + PreventClassMembers = 0x00000010, + SerializableValueClasses = 0x00000020, + ImportAsX86 = 0x00000100, + ImportAsX64 = 0x00000200, + ImportAsItanium = 0x00000400, + ImportAsAgnostic = 0x00000800, + ReflectionOnlyLoading = 0x00001000, + NoDefineVersionResource = 0x00002000, + ImportAsArm = 0x00004000, + } + +[Serializable] +[Flags()] +[System.Runtime.InteropServices.ComVisible(true)] + public enum TypeLibExporterFlags + { + None = 0x00000000, + OnlyReferenceRegistered = 0x00000001, + CallerResolvedReferences = 0x00000002, + OldNames = 0x00000004, + ExportAs32Bit = 0x00000010, + ExportAs64Bit = 0x00000020, + } + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public enum ImporterEventKind + { + NOTIF_TYPECONVERTED = 0, + NOTIF_CONVERTWARNING = 1, + ERROR_REFTOINVALIDTYPELIB = 2, + } + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public enum ExporterEventKind + { + NOTIF_TYPECONVERTED = 0, + NOTIF_CONVERTWARNING = 1, + ERROR_REFTOINVALIDASSEMBLY = 2 + } + + [GuidAttribute("F1C3BF76-C3E4-11d3-88E7-00902754C43A")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] +[System.Runtime.InteropServices.ComVisible(true)] + public interface ITypeLibImporterNotifySink + { + void ReportEvent( + ImporterEventKind eventKind, + int eventCode, + String eventMsg); + Assembly ResolveRef( + [MarshalAs(UnmanagedType.Interface)] Object typeLib); + } + + [GuidAttribute("F1C3BF77-C3E4-11d3-88E7-00902754C43A")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] +[System.Runtime.InteropServices.ComVisible(true)] + public interface ITypeLibExporterNotifySink + { + void ReportEvent( + ExporterEventKind eventKind, + int eventCode, + String eventMsg); + + [return : MarshalAs(UnmanagedType.Interface)] + Object ResolveRef( + Assembly assembly); + } + + [GuidAttribute("F1C3BF78-C3E4-11d3-88E7-00902754C43A")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] +[System.Runtime.InteropServices.ComVisible(true)] + public interface ITypeLibConverter + { + AssemblyBuilder ConvertTypeLibToAssembly( + [MarshalAs(UnmanagedType.Interface)] Object typeLib, + String asmFileName, + TypeLibImporterFlags flags, + ITypeLibImporterNotifySink notifySink, + byte[] publicKey, + StrongNameKeyPair keyPair, + String asmNamespace, + Version asmVersion); + + [return : MarshalAs(UnmanagedType.Interface)] + Object ConvertAssemblyToTypeLib( + Assembly assembly, + String typeLibName, + TypeLibExporterFlags flags, + ITypeLibExporterNotifySink notifySink); + + bool GetPrimaryInteropAssembly(Guid g, Int32 major, Int32 minor, Int32 lcid, out String asmName, out String asmCodeBase); + + AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib, + String asmFileName, + int flags, + ITypeLibImporterNotifySink notifySink, + byte[] publicKey, + StrongNameKeyPair keyPair, + bool unsafeInterfaces); + } + + [GuidAttribute("FA1F3615-ACB9-486d-9EAC-1BEF87E36B09")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] +[System.Runtime.InteropServices.ComVisible(true)] + public interface ITypeLibExporterNameProvider + { + [return : MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)] + String[] GetNames(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs b/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs new file mode 100644 index 0000000000..ac8258b872 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: This exception is thrown when an invalid COM object is used. This +** happens when a the __ComObject type is used directly without +** having a backing class factory. +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Runtime.Serialization; + + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class InvalidComObjectException : SystemException { + public InvalidComObjectException() + : base(Environment.GetResourceString("Arg_InvalidComObjectException")) { + SetErrorCode(__HResults.COR_E_INVALIDCOMOBJECT); + } + + public InvalidComObjectException(String message) + : base(message) { + SetErrorCode(__HResults.COR_E_INVALIDCOMOBJECT); + } + + public InvalidComObjectException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.COR_E_INVALIDCOMOBJECT); + } + + protected InvalidComObjectException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs b/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs new file mode 100644 index 0000000000..60c9aa67b4 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: The type of an OLE variant that was passed into the runtime is +** invalid. +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Runtime.Serialization; + + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class InvalidOleVariantTypeException : SystemException { + public InvalidOleVariantTypeException() + : base(Environment.GetResourceString("Arg_InvalidOleVariantTypeException")) { + SetErrorCode(__HResults.COR_E_INVALIDOLEVARIANTTYPE); + } + + public InvalidOleVariantTypeException(String message) + : base(message) { + SetErrorCode(__HResults.COR_E_INVALIDOLEVARIANTTYPE); + } + + public InvalidOleVariantTypeException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.COR_E_INVALIDOLEVARIANTTYPE); + } + + protected InvalidOleVariantTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/LayoutKind.cs b/src/mscorlib/src/System/Runtime/InteropServices/LayoutKind.cs new file mode 100644 index 0000000000..231779872e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/LayoutKind.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices { + using System; + // Used in the StructLayoutAttribute class + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public enum LayoutKind + { + Sequential = 0, // 0x00000008, + Explicit = 2, // 0x00000010, + Auto = 3, // 0x00000000, + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs new file mode 100644 index 0000000000..86e88306f0 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs @@ -0,0 +1,2806 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: This class contains methods that are mainly used to marshal +** between unmanaged and managed types. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Reflection.Emit; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Threading; + using System.Runtime.Remoting; + using System.Runtime.CompilerServices; + using System.Globalization; + using System.Runtime.ConstrainedExecution; + using System.Runtime.Versioning; + using Win32Native = Microsoft.Win32.Win32Native; + using Microsoft.Win32.SafeHandles; + using System.Diagnostics.Contracts; + using System.Runtime.InteropServices.ComTypes; + + [Serializable] + public enum CustomQueryInterfaceMode + { + Ignore = 0, + Allow = 1 + } + + //======================================================================== + // All public methods, including PInvoke, are protected with linkchecks. + // Remove the default demands for all PInvoke methods with this global + // declaration on the class. + //======================================================================== + + #if FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + #endif + public static partial class Marshal + { + //==================================================================== + // Defines used inside the Marshal class. + //==================================================================== + private const int LMEM_FIXED = 0; + private const int LMEM_MOVEABLE = 2; +#if !FEATURE_PAL + private const long HIWORDMASK = unchecked((long)0xffffffffffff0000L); +#endif //!FEATURE_PAL +#if FEATURE_COMINTEROP + private static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); +#endif //FEATURE_COMINTEROP + + // Win32 has the concept of Atoms, where a pointer can either be a pointer + // or an int. If it's less than 64K, this is guaranteed to NOT be a + // pointer since the bottom 64K bytes are reserved in a process' page table. + // We should be careful about deallocating this stuff. Extracted to + // a function to avoid C# problems with lack of support for IntPtr. + // We have 2 of these methods for slightly different semantics for NULL. + private static bool IsWin32Atom(IntPtr ptr) + { +#if FEATURE_PAL + return false; +#else + long lPtr = (long)ptr; + return 0 == (lPtr & HIWORDMASK); +#endif + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static bool IsNotWin32Atom(IntPtr ptr) + { +#if FEATURE_PAL + return true; +#else + long lPtr = (long)ptr; + return 0 != (lPtr & HIWORDMASK); +#endif + } + + //==================================================================== + // The default character size for the system. This is always 2 because + // the framework only runs on UTF-16 systems. + //==================================================================== + public static readonly int SystemDefaultCharSize = 2; + + //==================================================================== + // The max DBCS character size for the system. + //==================================================================== + public static readonly int SystemMaxDBCSCharSize = GetSystemMaxDBCSCharSize(); + + + //==================================================================== + // The name, title and description of the assembly that will contain + // the dynamically generated interop types. + //==================================================================== + private const String s_strConvertedTypeInfoAssemblyName = "InteropDynamicTypes"; + private const String s_strConvertedTypeInfoAssemblyTitle = "Interop Dynamic Types"; + private const String s_strConvertedTypeInfoAssemblyDesc = "Type dynamically generated from ITypeInfo's"; + private const String s_strConvertedTypeInfoNameSpace = "InteropDynamicTypes"; + + + //==================================================================== + // Helper method to retrieve the system's maximum DBCS character size. + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int GetSystemMaxDBCSCharSize(); + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static String PtrToStringAnsi(IntPtr ptr) + { + if (IntPtr.Zero == ptr) { + return null; + } + else if (IsWin32Atom(ptr)) { + return null; + } + else { + int nb = Win32Native.lstrlenA(ptr); + if( nb == 0) { + return string.Empty; + } + else { + return new String((sbyte *)ptr); + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static String PtrToStringAnsi(IntPtr ptr, int len) + { + if (ptr == IntPtr.Zero) + throw new ArgumentNullException("ptr"); + if (len < 0) + throw new ArgumentException("len"); + + return new String((sbyte *)ptr, 0, len); + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static String PtrToStringUni(IntPtr ptr, int len) + { + if (ptr == IntPtr.Zero) + throw new ArgumentNullException("ptr"); + if (len < 0) + throw new ArgumentException("len"); + + return new String((char *)ptr, 0, len); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static String PtrToStringAuto(IntPtr ptr, int len) + { + // Ansi platforms are no longer supported + return PtrToStringUni(ptr, len); + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static String PtrToStringUni(IntPtr ptr) + { + if (IntPtr.Zero == ptr) { + return null; + } + else if (IsWin32Atom(ptr)) { + return null; + } + else { + return new String((char *)ptr); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static String PtrToStringAuto(IntPtr ptr) + { + // Ansi platforms are no longer supported + return PtrToStringUni(ptr); + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static String PtrToStringUTF8(IntPtr ptr) + { + int nbBytes = System.StubHelpers.StubHelpers.strlen((sbyte*)ptr.ToPointer()); + return PtrToStringUTF8(ptr, nbBytes); + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static String PtrToStringUTF8(IntPtr ptr,int byteLen) + { + if (byteLen < 0) + { + throw new ArgumentException("byteLen"); + } + else if (IntPtr.Zero == ptr) + { + return null; + } + else if (IsWin32Atom(ptr)) + { + return null; + } + else if (byteLen == 0) + { + return string.Empty; + } + else + { + byte* pByte = (byte*)ptr.ToPointer(); + return Encoding.UTF8.GetString(pByte, byteLen); + } + } + + //==================================================================== + // SizeOf() + //==================================================================== + [System.Runtime.InteropServices.ComVisible(true)] + public static int SizeOf(Object structure) + { + if (structure == null) + throw new ArgumentNullException("structure"); + // we never had a check for generics here + Contract.EndContractBlock(); + + return SizeOfHelper(structure.GetType(), true); + } + + public static int SizeOf<T>(T structure) + { + return SizeOf((object)structure); + } + + [Pure] + public static int SizeOf(Type t) + { + if (t == null) + throw new ArgumentNullException("t"); + if (!(t is RuntimeType)) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "t"); + if (t.IsGenericType) + throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "t"); + Contract.EndContractBlock(); + + return SizeOfHelper(t, true); + } + + public static int SizeOf<T>() + { + return SizeOf(typeof(T)); + } + + /// <summary> + /// Returns the aligned size of an instance of a value type. + /// </summary> + /// <typeparam name="T">Provide a value type to figure out its size</typeparam> + /// <returns>The aligned size of T in bytes.</returns> + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + internal static uint AlignedSizeOf<T>() where T : struct + { + uint size = SizeOfType(typeof(T)); + if (size == 1 || size == 2) + { + return size; + } + if (IntPtr.Size == 8 && size == 4) + { + return size; + } + return AlignedSizeOfType(typeof(T)); + } + + // Type must be a value type with no object reference fields. We only + // assert this, due to the lack of a suitable generic constraint. + [MethodImpl(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal static extern uint SizeOfType(Type type); + + // Type must be a value type with no object reference fields. We only + // assert this, due to the lack of a suitable generic constraint. + [MethodImpl(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static extern uint AlignedSizeOfType(Type type); + +#if !FEATURE_CORECLR // Marshal is critical in CoreCLR, so SafeCritical members trigger Annotator violations + [System.Security.SecuritySafeCritical] +#endif // !FEATURE_CORECLR + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern int SizeOfHelper(Type t, bool throwIfNotMarshalable); + + //==================================================================== + // OffsetOf() + //==================================================================== + public static IntPtr OffsetOf(Type t, String fieldName) + { + if (t == null) + throw new ArgumentNullException("t"); + Contract.EndContractBlock(); + + FieldInfo f = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (f == null) + throw new ArgumentException(Environment.GetResourceString("Argument_OffsetOfFieldNotFound", t.FullName), "fieldName"); + RtFieldInfo rtField = f as RtFieldInfo; + if (rtField == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeFieldInfo"), "fieldName"); + + return OffsetOfHelper(rtField); + } + public static IntPtr OffsetOf<T>(string fieldName) + { + return OffsetOf(typeof(T), fieldName); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr OffsetOfHelper(IRuntimeFieldInfo f); + + //==================================================================== + // UnsafeAddrOfPinnedArrayElement() + // + // IMPORTANT NOTICE: This method does not do any verification on the + // array. It must be used with EXTREME CAUTION since passing in + // an array that is not pinned or in the fixed heap can cause + // unexpected results ! + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern IntPtr UnsafeAddrOfPinnedArrayElement(Array arr, int index); + + [System.Security.SecurityCritical] + public static IntPtr UnsafeAddrOfPinnedArrayElement<T>(T[] arr, int index) + { + return UnsafeAddrOfPinnedArrayElement((Array)arr, index); + } + + //==================================================================== + // Copy blocks from CLR arrays to native memory. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(int[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(char[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(short[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(long[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(float[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(double[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(byte[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr[] source, int startIndex, IntPtr destination, int length) + { + CopyToNative(source, startIndex, destination, length); + } + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void CopyToNative(Object source, int startIndex, IntPtr destination, int length); + + //==================================================================== + // Copy blocks from native memory to CLR arrays + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, int[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, char[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, short[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, long[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, float[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, double[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, byte[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void Copy(IntPtr source, IntPtr[] destination, int startIndex, int length) + { + CopyToManaged(source, destination, startIndex, length); + } + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void CopyToManaged(IntPtr source, Object destination, int startIndex, int length); + + //==================================================================== + // Read from memory + //==================================================================== + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_RU1")] + [SuppressUnmanagedCodeSecurity] + public static extern byte ReadByte([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs); +#else + public static byte ReadByte([MarshalAs(UnmanagedType.AsAny), In] Object ptr, int ofs) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe byte ReadByte(IntPtr ptr, int ofs) + { + try + { + byte *addr = (byte *)ptr + ofs; + return *addr; + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static byte ReadByte(IntPtr ptr) + { + return ReadByte(ptr,0); + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_RI2")] + [SuppressUnmanagedCodeSecurity] + public static extern short ReadInt16([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs); +#else + public static short ReadInt16([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe short ReadInt16(IntPtr ptr, int ofs) + { + try + { + byte *addr = (byte *)ptr + ofs; + if ((unchecked((int)addr) & 0x1) == 0) + { + // aligned read + return *((short *)addr); + } + else + { + // unaligned read + short val; + byte *valPtr = (byte *)&val; + valPtr[0] = addr[0]; + valPtr[1] = addr[1]; + return val; + } + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static short ReadInt16(IntPtr ptr) + { + return ReadInt16(ptr, 0); + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_RI4"), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [SuppressUnmanagedCodeSecurity] + public static extern int ReadInt32([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs); +#else + public static int ReadInt32([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static unsafe int ReadInt32(IntPtr ptr, int ofs) + { + try + { + byte *addr = (byte *)ptr + ofs; + if ((unchecked((int)addr) & 0x3) == 0) + { + // aligned read + return *((int *)addr); + } + else + { + // unaligned read + int val; + byte *valPtr = (byte *)&val; + valPtr[0] = addr[0]; + valPtr[1] = addr[1]; + valPtr[2] = addr[2]; + valPtr[3] = addr[3]; + return val; + } + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static int ReadInt32(IntPtr ptr) + { + return ReadInt32(ptr,0); + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static IntPtr ReadIntPtr([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs) + { + #if BIT64 + return (IntPtr) ReadInt64(ptr, ofs); + #else // 32 + return (IntPtr) ReadInt32(ptr, ofs); + #endif + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static IntPtr ReadIntPtr(IntPtr ptr, int ofs) + { + #if BIT64 + return (IntPtr) ReadInt64(ptr, ofs); + #else // 32 + return (IntPtr) ReadInt32(ptr, ofs); + #endif + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static IntPtr ReadIntPtr(IntPtr ptr) + { + #if BIT64 + return (IntPtr) ReadInt64(ptr, 0); + #else // 32 + return (IntPtr) ReadInt32(ptr, 0); + #endif + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_RI8"), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [SuppressUnmanagedCodeSecurity] + public static extern long ReadInt64([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs); +#else + public static long ReadInt64([MarshalAs(UnmanagedType.AsAny),In] Object ptr, int ofs) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe long ReadInt64(IntPtr ptr, int ofs) + { + try + { + byte *addr = (byte *)ptr + ofs; + if ((unchecked((int)addr) & 0x7) == 0) + { + // aligned read + return *((long *)addr); + } + else + { + // unaligned read + long val; + byte *valPtr = (byte *)&val; + valPtr[0] = addr[0]; + valPtr[1] = addr[1]; + valPtr[2] = addr[2]; + valPtr[3] = addr[3]; + valPtr[4] = addr[4]; + valPtr[5] = addr[5]; + valPtr[6] = addr[6]; + valPtr[7] = addr[7]; + return val; + } + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static long ReadInt64(IntPtr ptr) + { + return ReadInt64(ptr,0); + } + + + //==================================================================== + // Write to memory + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe void WriteByte(IntPtr ptr, int ofs, byte val) + { + try + { + byte *addr = (byte *)ptr + ofs; + *addr = val; + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_WU1")] + [SuppressUnmanagedCodeSecurity] + public static extern void WriteByte([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, byte val); +#else + public static void WriteByte([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, byte val) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteByte(IntPtr ptr, byte val) + { + WriteByte(ptr, 0, val); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe void WriteInt16(IntPtr ptr, int ofs, short val) + { + try + { + byte *addr = (byte *)ptr + ofs; + if ((unchecked((int)addr) & 0x1) == 0) + { + // aligned write + *((short *)addr) = val; + } + else + { + // unaligned write + byte *valPtr = (byte *)&val; + addr[0] = valPtr[0]; + addr[1] = valPtr[1]; + } + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_WI2")] + [SuppressUnmanagedCodeSecurity] + public static extern void WriteInt16([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, short val); +#else + public static void WriteInt16([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, short val) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteInt16(IntPtr ptr, short val) + { + WriteInt16(ptr, 0, val); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteInt16(IntPtr ptr, int ofs, char val) + { + WriteInt16(ptr, ofs, (short)val); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteInt16([In,Out]Object ptr, int ofs, char val) + { + WriteInt16(ptr, ofs, (short)val); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteInt16(IntPtr ptr, char val) + { + WriteInt16(ptr, 0, (short)val); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe void WriteInt32(IntPtr ptr, int ofs, int val) + { + try + { + byte *addr = (byte *)ptr + ofs; + if ((unchecked((int)addr) & 0x3) == 0) + { + // aligned write + *((int *)addr) = val; + } + else + { + // unaligned write + byte *valPtr = (byte *)&val; + addr[0] = valPtr[0]; + addr[1] = valPtr[1]; + addr[2] = valPtr[2]; + addr[3] = valPtr[3]; + } + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_WI4")] + [SuppressUnmanagedCodeSecurity] + public static extern void WriteInt32([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, int val); +#else + public static void WriteInt32([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, int val) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteInt32(IntPtr ptr, int val) + { + WriteInt32(ptr,0,val); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteIntPtr(IntPtr ptr, int ofs, IntPtr val) + { + #if BIT64 + WriteInt64(ptr, ofs, (long)val); + #else // 32 + WriteInt32(ptr, ofs, (int)val); + #endif + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteIntPtr([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, IntPtr val) + { + #if BIT64 + WriteInt64(ptr, ofs, (long)val); + #else // 32 + WriteInt32(ptr, ofs, (int)val); + #endif + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteIntPtr(IntPtr ptr, IntPtr val) + { + #if BIT64 + WriteInt64(ptr, 0, (long)val); + #else // 32 + WriteInt32(ptr, 0, (int)val); + #endif + } + + [System.Security.SecurityCritical] // auto-generated_required + public static unsafe void WriteInt64(IntPtr ptr, int ofs, long val) + { + try + { + byte *addr = (byte *)ptr + ofs; + if ((unchecked((int)addr) & 0x7) == 0) + { + // aligned write + *((long *)addr) = val; + } + else + { + // unaligned write + byte *valPtr = (byte *)&val; + addr[0] = valPtr[0]; + addr[1] = valPtr[1]; + addr[2] = valPtr[2]; + addr[3] = valPtr[3]; + addr[4] = valPtr[4]; + addr[5] = valPtr[5]; + addr[6] = valPtr[6]; + addr[7] = valPtr[7]; + } + } + catch (NullReferenceException) + { + // this method is documented to throw AccessViolationException on any AV + throw new AccessViolationException(); + } + } + + [System.Security.SecurityCritical] // auto-generated +#if !FEATURE_CORECLR + [DllImport(Win32Native.SHIM, EntryPoint="ND_WI8")] + [SuppressUnmanagedCodeSecurity] + public static extern void WriteInt64([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, long val); +#else + public static void WriteInt64([MarshalAs(UnmanagedType.AsAny),In,Out] Object ptr, int ofs, long val) + { + throw new PlatformNotSupportedException(); + } +#endif // !FEATURE_CORECLR + + [System.Security.SecurityCritical] // auto-generated_required + public static void WriteInt64(IntPtr ptr, long val) + { + WriteInt64(ptr, 0, val); + } + + + //==================================================================== + // GetLastWin32Error + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static extern int GetLastWin32Error(); + + + //==================================================================== + // SetLastWin32Error + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal static extern void SetLastWin32Error(int error); + + + //==================================================================== + // GetHRForLastWin32Error + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static int GetHRForLastWin32Error() + { + int dwLastError = GetLastWin32Error(); + if ((dwLastError & 0x80000000) == 0x80000000) + return dwLastError; + else + return (dwLastError & 0x0000FFFF) | unchecked((int)0x80070000); + } + + + //==================================================================== + // Prelink + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static void Prelink(MethodInfo m) + { + if (m == null) + throw new ArgumentNullException("m"); + Contract.EndContractBlock(); + + RuntimeMethodInfo rmi = m as RuntimeMethodInfo; + + if (rmi == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeMethodInfo")); + + InternalPrelink(rmi); + } + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + [SecurityCritical] + private static extern void InternalPrelink(IRuntimeMethodInfo m); + + [System.Security.SecurityCritical] // auto-generated_required + public static void PrelinkAll(Type c) + { + if (c == null) + throw new ArgumentNullException("c"); + Contract.EndContractBlock(); + + MethodInfo[] mi = c.GetMethods(); + if (mi != null) + { + for (int i = 0; i < mi.Length; i++) + { + Prelink(mi[i]); + } + } + } + + //==================================================================== + // NumParamBytes + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static int NumParamBytes(MethodInfo m) + { + if (m == null) + throw new ArgumentNullException("m"); + Contract.EndContractBlock(); + + RuntimeMethodInfo rmi = m as RuntimeMethodInfo; + if (rmi == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeMethodInfo")); + + return InternalNumParamBytes(rmi); + } + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + [SecurityCritical] + private static extern int InternalNumParamBytes(IRuntimeMethodInfo m); + + //==================================================================== + // Win32 Exception stuff + // These are mostly interesting for Structured exception handling, + // but need to be exposed for all exceptions (not just SEHException). + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [System.Runtime.InteropServices.ComVisible(true)] + public static extern /* struct _EXCEPTION_POINTERS* */ IntPtr GetExceptionPointers(); + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int GetExceptionCode(); + + + //==================================================================== + // 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. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [System.Runtime.InteropServices.ComVisible(true)] + public static extern void StructureToPtr(Object structure, IntPtr ptr, bool fDeleteOld); + + [System.Security.SecurityCritical] + public static void StructureToPtr<T>(T structure, IntPtr ptr, bool fDeleteOld) + { + StructureToPtr((object)structure, ptr, fDeleteOld); + } + + //==================================================================== + // Marshals data from a native memory block to a preallocated structure class. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [System.Runtime.InteropServices.ComVisible(true)] + public static void PtrToStructure(IntPtr ptr, Object structure) + { + PtrToStructureHelper(ptr, structure, false); + } + + [System.Security.SecurityCritical] + public static void PtrToStructure<T>(IntPtr ptr, T structure) + { + PtrToStructure(ptr, (object)structure); + } + + //==================================================================== + // Creates a new instance of "structuretype" and marshals data from a + // native memory block to it. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [System.Runtime.InteropServices.ComVisible(true)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + public static Object PtrToStructure(IntPtr ptr, Type structureType) + { + if (ptr == IntPtr.Zero) return null; + + if (structureType == null) + throw new ArgumentNullException("structureType"); + + if (structureType.IsGenericType) + throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "structureType"); + + RuntimeType rt = structureType.UnderlyingSystemType as RuntimeType; + + if (rt == null) + throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "type"); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + + Object structure = rt.CreateInstanceDefaultCtor(false /*publicOnly*/, false /*skipCheckThis*/, false /*fillCache*/, ref stackMark); + PtrToStructureHelper(ptr, structure, true); + return structure; + } + + [System.Security.SecurityCritical] + public static T PtrToStructure<T>(IntPtr ptr) + { + return (T)PtrToStructure(ptr, typeof(T)); + } + + //==================================================================== + // Helper function to copy a pointer into a preallocated structure. + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void PtrToStructureHelper(IntPtr ptr, Object structure, bool allowValueClasses); + + + //==================================================================== + // Freeds all substructures pointed to by the native memory block. + // "structureclass" is used to provide layout information. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [System.Runtime.InteropServices.ComVisible(true)] + public static extern void DestroyStructure(IntPtr ptr, Type structuretype); + + [System.Security.SecurityCritical] + public static void DestroyStructure<T>(IntPtr ptr) + { + DestroyStructure(ptr, typeof(T)); + } + + //==================================================================== + // 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. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr GetHINSTANCE(Module m) + { + if (m == null) + throw new ArgumentNullException("m"); + Contract.EndContractBlock(); + + RuntimeModule rtModule = m as RuntimeModule; + if (rtModule == null) + { + ModuleBuilder mb = m as ModuleBuilder; + if (mb != null) + rtModule = mb.InternalModule; + } + + if (rtModule == null) + throw new ArgumentNullException("m",Environment.GetResourceString("Argument_MustBeRuntimeModule")); + + return GetHINSTANCE(rtModule.GetNativeHandle()); + } + + [System.Security.SecurityCritical] // auto-generated_required + [SuppressUnmanagedCodeSecurity] + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + private extern static IntPtr GetHINSTANCE(RuntimeModule m); + + //==================================================================== + // Throws a CLR exception based on the HRESULT. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static void ThrowExceptionForHR(int errorCode) + { + if (errorCode < 0) + ThrowExceptionForHRInternal(errorCode, IntPtr.Zero); + } + [System.Security.SecurityCritical] // auto-generated_required + public static void ThrowExceptionForHR(int errorCode, IntPtr errorInfo) + { + if (errorCode < 0) + ThrowExceptionForHRInternal(errorCode, errorInfo); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void ThrowExceptionForHRInternal(int errorCode, IntPtr errorInfo); + + + //==================================================================== + // Converts the HRESULT to a CLR exception. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Exception GetExceptionForHR(int errorCode) + { + if (errorCode < 0) + return GetExceptionForHRInternal(errorCode, IntPtr.Zero); + else + return null; + } + [System.Security.SecurityCritical] // auto-generated_required + public static Exception GetExceptionForHR(int errorCode, IntPtr errorInfo) + { + if (errorCode < 0) + return GetExceptionForHRInternal(errorCode, errorInfo); + else + return null; + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern Exception GetExceptionForHRInternal(int errorCode, IntPtr errorInfo); + + + //==================================================================== + // This method is intended for compiler code generators rather + // than applications. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [ObsoleteAttribute("The GetUnmanagedThunkForManagedMethodPtr method has been deprecated and will be removed in a future release.", false)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern IntPtr GetUnmanagedThunkForManagedMethodPtr(IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature); + + //==================================================================== + // This method is intended for compiler code generators rather + // than applications. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [ObsoleteAttribute("The GetManagedThunkForUnmanagedMethodPtr method has been deprecated and will be removed in a future release.", false)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern IntPtr GetManagedThunkForUnmanagedMethodPtr(IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature); + + //==================================================================== + // The hosting APIs allow a sophisticated host to schedule fibers + // onto OS threads, so long as they notify the runtime of this + // activity. A fiber cookie can be redeemed for its managed Thread + // object by calling the following service. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [ObsoleteAttribute("The GetThreadFromFiberCookie method has been deprecated. Use the hosting API to perform this operation.", false)] + public static Thread GetThreadFromFiberCookie(int cookie) + { + if (cookie == 0) + throw new ArgumentException(Environment.GetResourceString("Argument_ArgumentZero"), "cookie"); + Contract.EndContractBlock(); + + return InternalGetThreadFromFiberCookie(cookie); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Thread InternalGetThreadFromFiberCookie(int cookie); + + + //==================================================================== + // Memory allocation and deallocation. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public static IntPtr AllocHGlobal(IntPtr cb) + { + // For backwards compatibility on 32 bit platforms, ensure we pass values between + // Int32.MaxValue and UInt32.MaxValue to Windows. If the binary has had the + // LARGEADDRESSAWARE bit set in the PE header, it may get 3 or 4 GB of user mode + // address space. It is remotely that those allocations could have succeeded, + // though I couldn't reproduce that. In either case, that means we should continue + // throwing an OOM instead of an ArgumentOutOfRangeException for "negative" amounts of memory. + UIntPtr numBytes; +#if BIT64 + numBytes = new UIntPtr(unchecked((ulong)cb.ToInt64())); +#else // 32 + numBytes = new UIntPtr(unchecked((uint)cb.ToInt32())); +#endif + + IntPtr pNewMem = Win32Native.LocalAlloc_NoSafeHandle(LMEM_FIXED, unchecked(numBytes)); + + if (pNewMem == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + return pNewMem; + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public static IntPtr AllocHGlobal(int cb) + { + return AllocHGlobal((IntPtr)cb); + } + + [System.Security.SecurityCritical] // auto-generated_required + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static void FreeHGlobal(IntPtr hglobal) + { + if (IsNotWin32Atom(hglobal)) { + if (IntPtr.Zero != Win32Native.LocalFree(hglobal)) { + ThrowExceptionForHR(GetHRForLastWin32Error()); + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr ReAllocHGlobal(IntPtr pv, IntPtr cb) + { + IntPtr pNewMem = Win32Native.LocalReAlloc(pv, cb, LMEM_MOVEABLE); + if (pNewMem == IntPtr.Zero) { + throw new OutOfMemoryException(); + } + return pNewMem; + } + + + //==================================================================== + // String convertions. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static IntPtr StringToHGlobalAnsi(String s) + { + if (s == null) + { + return IntPtr.Zero; + } + else + { + int nb = (s.Length + 1) * SystemMaxDBCSCharSize; + + // Overflow checking + if (nb < s.Length) + throw new ArgumentOutOfRangeException("s"); + + UIntPtr len = new UIntPtr((uint)nb); + IntPtr hglobal = Win32Native.LocalAlloc_NoSafeHandle(LMEM_FIXED, len); + + if (hglobal == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + else + { + s.ConvertToAnsi((byte *)hglobal, nb, false, false); + return hglobal; + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static IntPtr StringToHGlobalUni(String s) + { + if (s == null) + { + return IntPtr.Zero; + } + else + { + int nb = (s.Length + 1) * 2; + + // Overflow checking + if (nb < s.Length) + throw new ArgumentOutOfRangeException("s"); + + UIntPtr len = new UIntPtr((uint)nb); + IntPtr hglobal = Win32Native.LocalAlloc_NoSafeHandle(LMEM_FIXED, len); + + if (hglobal == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + else + { + fixed (char* firstChar = s) + { + String.wstrcpy((char*)hglobal, firstChar, s.Length + 1); + } + return hglobal; + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr StringToHGlobalAuto(String s) + { + // Ansi platforms are no longer supported + return StringToHGlobalUni(s); + } + +#if FEATURE_COMINTEROP + + //==================================================================== + // Converts the CLR exception to an HRESULT. This function also sets + // up an IErrorInfo for the exception. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int GetHRForException(Exception e); + + //==================================================================== + // Converts the CLR exception to an HRESULT. This function also sets + // up an IErrorInfo for the exception. + // This function is only used in WinRT and converts ObjectDisposedException + // to RO_E_CLOSED + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern int GetHRForException_WinRT(Exception e); + + internal static readonly Guid ManagedNameGuid = new Guid("{0F21F359-AB84-41E8-9A78-36D110E6D2F9}"); + + //==================================================================== + // Given a managed object that wraps a UCOMITypeLib, return its name + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [Obsolete("Use System.Runtime.InteropServices.Marshal.GetTypeLibName(ITypeLib pTLB) instead. http://go.microsoft.com/fwlink/?linkid=14202&ID=0000011.", false)] + public static String GetTypeLibName(UCOMITypeLib pTLB) + { + return GetTypeLibName((ITypeLib)pTLB); + } + + + //==================================================================== + // Given a managed object that wraps an ITypeLib, return its name + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static String GetTypeLibName(ITypeLib typelib) + { + if (typelib == null) + throw new ArgumentNullException("typelib"); + Contract.EndContractBlock(); + + String strTypeLibName = null; + String strDocString = null; + int dwHelpContext = 0; + String strHelpFile = null; + + typelib.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile); + + return strTypeLibName; + } + + //==================================================================== + // Internal version of GetTypeLibName + // Support GUID_ManagedName which aligns with TlbImp + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + internal static String GetTypeLibNameInternal(ITypeLib typelib) + { + if (typelib == null) + throw new ArgumentNullException("typelib"); + Contract.EndContractBlock(); + + // Try GUID_ManagedName first + ITypeLib2 typeLib2 = typelib as ITypeLib2; + if (typeLib2 != null) + { + Guid guid = ManagedNameGuid; + object val; + + try + { + typeLib2.GetCustData(ref guid, out val); + } + catch(Exception) + { + val = null; + } + + if (val != null && val.GetType() == typeof(string)) + { + string customManagedNamespace = (string)val; + customManagedNamespace = customManagedNamespace.Trim(); + if (customManagedNamespace.EndsWith(".DLL", StringComparison.OrdinalIgnoreCase)) + customManagedNamespace = customManagedNamespace.Substring(0, customManagedNamespace.Length - 4); + else if (customManagedNamespace.EndsWith(".EXE", StringComparison.OrdinalIgnoreCase)) + customManagedNamespace = customManagedNamespace.Substring(0, customManagedNamespace.Length - 4); + return customManagedNamespace; + } + } + + return GetTypeLibName(typelib); + } + + + //==================================================================== + // Given an managed object that wraps an UCOMITypeLib, return its guid + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [Obsolete("Use System.Runtime.InteropServices.Marshal.GetTypeLibGuid(ITypeLib pTLB) instead. http://go.microsoft.com/fwlink/?linkid=14202&ID=0000011.", false)] + public static Guid GetTypeLibGuid(UCOMITypeLib pTLB) + { + return GetTypeLibGuid((ITypeLib)pTLB); + } + + //==================================================================== + // Given an managed object that wraps an ITypeLib, return its guid + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Guid GetTypeLibGuid(ITypeLib typelib) + { + Guid result = new Guid (); + FCallGetTypeLibGuid (ref result, typelib); + return result; + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void FCallGetTypeLibGuid(ref Guid result, ITypeLib pTLB); + + //==================================================================== + // Given a managed object that wraps a UCOMITypeLib, return its lcid + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [Obsolete("Use System.Runtime.InteropServices.Marshal.GetTypeLibLcid(ITypeLib pTLB) instead. http://go.microsoft.com/fwlink/?linkid=14202&ID=0000011.", false)] + public static int GetTypeLibLcid(UCOMITypeLib pTLB) + { + return GetTypeLibLcid((ITypeLib)pTLB); + } + + //==================================================================== + // Given a managed object that wraps an ITypeLib, return its lcid + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int GetTypeLibLcid(ITypeLib typelib); + + //==================================================================== + // Given a managed object that wraps an ITypeLib, return it's + // version information. + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void GetTypeLibVersion(ITypeLib typeLibrary, out int major, out int minor); + + //==================================================================== + // Given a managed object that wraps an ITypeInfo, return its guid. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated + internal static Guid GetTypeInfoGuid(ITypeInfo typeInfo) + { + Guid result = new Guid (); + FCallGetTypeInfoGuid (ref result, typeInfo); + return result; + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void FCallGetTypeInfoGuid(ref Guid result, ITypeInfo typeInfo); + + //==================================================================== + // Given a assembly, return the TLBID that will be generated for the + // typelib exported from the assembly. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Guid GetTypeLibGuidForAssembly(Assembly asm) + { + if (asm == null) + throw new ArgumentNullException("asm"); + Contract.EndContractBlock(); + + RuntimeAssembly rtAssembly = asm as RuntimeAssembly; + if (rtAssembly == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"), "asm"); + + Guid result = new Guid(); + FCallGetTypeLibGuidForAssembly(ref result, rtAssembly); + return result; + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void FCallGetTypeLibGuidForAssembly(ref Guid result, RuntimeAssembly asm); + + //==================================================================== + // Given a assembly, return the version number of the type library + // that would be exported from the assembly. + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void _GetTypeLibVersionForAssembly(RuntimeAssembly inputAssembly, out int majorVersion, out int minorVersion); + + [System.Security.SecurityCritical] // auto-generated_required + public static void GetTypeLibVersionForAssembly(Assembly inputAssembly, out int majorVersion, out int minorVersion) + { + if (inputAssembly == null) + throw new ArgumentNullException("inputAssembly"); + Contract.EndContractBlock(); + + RuntimeAssembly rtAssembly = inputAssembly as RuntimeAssembly; + if (rtAssembly == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"), "inputAssembly"); + + _GetTypeLibVersionForAssembly(rtAssembly, out majorVersion, out minorVersion); + } + + //==================================================================== + // Given a managed object that wraps an UCOMITypeInfo, return its name + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [Obsolete("Use System.Runtime.InteropServices.Marshal.GetTypeInfoName(ITypeInfo pTLB) instead. http://go.microsoft.com/fwlink/?linkid=14202&ID=0000011.", false)] + public static String GetTypeInfoName(UCOMITypeInfo pTI) + { + return GetTypeInfoName((ITypeInfo)pTI); + } + + //==================================================================== + // Given a managed object that wraps an ITypeInfo, return its name + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static String GetTypeInfoName(ITypeInfo typeInfo) + { + if (typeInfo == null) + throw new ArgumentNullException("typeInfo"); + Contract.EndContractBlock(); + + String strTypeLibName = null; + String strDocString = null; + int dwHelpContext = 0; + String strHelpFile = null; + + typeInfo.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile); + + return strTypeLibName; + } + + //==================================================================== + // Internal version of GetTypeInfoName + // Support GUID_ManagedName which aligns with TlbImp + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + internal static String GetTypeInfoNameInternal(ITypeInfo typeInfo, out bool hasManagedName) + { + if (typeInfo == null) + throw new ArgumentNullException("typeInfo"); + Contract.EndContractBlock(); + + // Try ManagedNameGuid first + ITypeInfo2 typeInfo2 = typeInfo as ITypeInfo2; + if (typeInfo2 != null) + { + Guid guid = ManagedNameGuid; + object val; + + try + { + typeInfo2.GetCustData(ref guid, out val); + } + catch(Exception) + { + val = null; + } + + if (val != null && val.GetType() == typeof(string)) + { + hasManagedName = true; + return (string)val; + } + } + + hasManagedName = false; + return GetTypeInfoName(typeInfo); + } + + //==================================================================== + // Get the corresponding managed name as converted by TlbImp + // Used to get the type using GetType() from imported assemblies + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + internal static String GetManagedTypeInfoNameInternal(ITypeLib typeLib, ITypeInfo typeInfo) + { + bool hasManagedName; + string name = GetTypeInfoNameInternal(typeInfo, out hasManagedName); + if (hasManagedName) + return name; + else + return GetTypeLibNameInternal(typeLib) + "." + name; + } + + //==================================================================== + // If a type with the specified GUID is loaded, this method will + // return the reflection type that represents it. Otherwise it returns + // NULL. + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Type GetLoadedTypeForGUID(ref Guid guid); + +#if !FEATURE_CORECLR // current implementation requires reflection only load + //==================================================================== + // map ITypeInfo* to Type + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Type GetTypeForITypeInfo(IntPtr /* ITypeInfo* */ piTypeInfo) + { + ITypeInfo pTI = null; + ITypeLib pTLB = null; + Type TypeObj = null; + Assembly AsmBldr = null; + TypeLibConverter TlbConverter = null; + int Index = 0; + Guid clsid; + + // If the input ITypeInfo is NULL then return NULL. + if (piTypeInfo == IntPtr.Zero) + return null; + + // Wrap the ITypeInfo in a CLR object. + pTI = (ITypeInfo)GetObjectForIUnknown(piTypeInfo); + + // Check to see if a class exists with the specified GUID. + + clsid = GetTypeInfoGuid(pTI); + TypeObj = GetLoadedTypeForGUID(ref clsid); + + // If we managed to find the type based on the GUID then return it. + if (TypeObj != null) + return TypeObj; + + // There is no type with the specified GUID in the app domain so lets + // try and convert the containing typelib. + try + { + pTI.GetContainingTypeLib(out pTLB, out Index); + } + catch(COMException) + { + pTLB = null; + } + + // Check to see if we managed to get a containing typelib. + if (pTLB != null) + { + // Get the assembly name from the typelib. + AssemblyName AsmName = TypeLibConverter.GetAssemblyNameFromTypelib(pTLB, null, null, null, null, AssemblyNameFlags.None); + String AsmNameString = AsmName.FullName; + + // Check to see if the assembly that will contain the type already exists. + Assembly[] aAssemblies = Thread.GetDomain().GetAssemblies(); + int NumAssemblies = aAssemblies.Length; + for (int i = 0; i < NumAssemblies; i++) + { + if (String.Compare(aAssemblies[i].FullName, + AsmNameString,StringComparison.Ordinal) == 0) + AsmBldr = aAssemblies[i]; + } + + // If we haven't imported the assembly yet then import it. + if (AsmBldr == null) + { + TlbConverter = new TypeLibConverter(); + AsmBldr = TlbConverter.ConvertTypeLibToAssembly(pTLB, + GetTypeLibName(pTLB) + ".dll", 0, new ImporterCallback(), null, null, null, null); + } + + // Load the type object from the imported typelib. + // Call GetManagedTypeInfoNameInternal to align with TlbImp behavior + TypeObj = AsmBldr.GetType(GetManagedTypeInfoNameInternal(pTLB, pTI), true, false); + if (TypeObj != null && !TypeObj.IsVisible) + TypeObj = null; + } + else + { + // If the ITypeInfo does not have a containing typelib then simply + // return Object as the type. + TypeObj = typeof(Object); + } + + return TypeObj; + } +#endif // #if !FEATURE_CORECLR + + // This method is identical to Type.GetTypeFromCLSID. Since it's interop specific, we expose it + // on Marshal for more consistent API surface. +#if !FEATURE_CORECLR + [System.Security.SecuritySafeCritical] +#endif //!FEATURE_CORECLR + public static Type GetTypeFromCLSID(Guid clsid) + { + return RuntimeType.GetTypeFromCLSIDImpl(clsid, null, false); + } + + //==================================================================== + // map Type to ITypeInfo* + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern IntPtr /* ITypeInfo* */ GetITypeInfoForType(Type t); + + //==================================================================== + // return the IUnknown* for an Object if the current context + // is the one where the RCW was first seen. Will return null + // otherwise. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IUnknown* */ GetIUnknownForObject(Object o) + { + return GetIUnknownForObjectNative(o, false); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IUnknown* */ GetIUnknownForObjectInContext(Object o) + { + return GetIUnknownForObjectNative(o, true); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr /* IUnknown* */ GetIUnknownForObjectNative(Object o, bool onlyInContext); + + //==================================================================== + // return the raw IUnknown* for a COM Object not related to current + // context + // Does not call AddRef + //==================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern IntPtr /* IUnknown* */ GetRawIUnknownForComObjectNoAddRef(Object o); + + //==================================================================== + // return the IDispatch* for an Object + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IDispatch */ GetIDispatchForObject(Object o) + { + return GetIDispatchForObjectNative(o, false); + } + + //==================================================================== + // return the IDispatch* for an Object if the current context + // is the one where the RCW was first seen. Will return null + // otherwise. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IUnknown* */ GetIDispatchForObjectInContext(Object o) + { + return GetIDispatchForObjectNative(o, true); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr /* IUnknown* */ GetIDispatchForObjectNative(Object o, bool onlyInContext); + + //==================================================================== + // return the IUnknown* representing the interface for the Object + // Object o should support Type T + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IUnknown* */ GetComInterfaceForObject(Object o, Type T) + { + return GetComInterfaceForObjectNative(o, T, false, true); + } + + [System.Security.SecurityCritical] + public static IntPtr GetComInterfaceForObject<T, TInterface>(T o) + { + return GetComInterfaceForObject(o, typeof(TInterface)); + } + + //==================================================================== + // 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 + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IUnknown* */ GetComInterfaceForObject(Object o, Type T, CustomQueryInterfaceMode mode) + { + bool bEnableCustomizedQueryInterface = ((mode == CustomQueryInterfaceMode.Allow) ? true : false); + return GetComInterfaceForObjectNative(o, T, false, bEnableCustomizedQueryInterface); + } + + //==================================================================== + // return the IUnknown* representing the interface for the Object + // Object o should support Type T if the current context + // is the one where the RCW was first seen. Will return null + // otherwise. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr /* IUnknown* */ GetComInterfaceForObjectInContext(Object o, Type t) + { + return GetComInterfaceForObjectNative(o, t, true, true); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern IntPtr /* IUnknown* */ GetComInterfaceForObjectNative(Object o, Type t, bool onlyInContext, bool fEnalbeCustomizedQueryInterface); + + //==================================================================== + // return an Object for IUnknown + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern Object GetObjectForIUnknown(IntPtr /* IUnknown* */ pUnk); + + //==================================================================== + // 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 of said RCW. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern Object GetUniqueObjectForIUnknown(IntPtr unknown); + + //==================================================================== + // return an Object for IUnknown, using the Type T, + // NOTE: + // Type T should be either a COM imported Type or a sub-type of COM + // imported Type + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern Object GetTypedObjectForIUnknown(IntPtr /* IUnknown* */ pUnk, Type t); + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern IntPtr CreateAggregatedObject(IntPtr pOuter, Object o); + + [System.Security.SecurityCritical] + public static IntPtr CreateAggregatedObject<T>(IntPtr pOuter, T o) + { + return CreateAggregatedObject(pOuter, (object)o); + } + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void CleanupUnusedObjectsInCurrentContext(); + + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern bool AreComObjectsAvailableForCleanup(); + + //==================================================================== + // check if the object is classic COM component + //==================================================================== +#if !FEATURE_CORECLR // with FEATURE_CORECLR, the whole type is SecurityCritical + [System.Security.SecuritySafeCritical] +#endif + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern bool IsComObject(Object o); + +#endif // FEATURE_COMINTEROP + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr AllocCoTaskMem(int cb) + { + IntPtr pNewMem = Win32Native.CoTaskMemAlloc(new UIntPtr((uint)cb)); + if (pNewMem == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + return pNewMem; + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static IntPtr StringToCoTaskMemUni(String s) + { + if (s == null) + { + return IntPtr.Zero; + } + else + { + int nb = (s.Length + 1) * 2; + + // Overflow checking + if (nb < s.Length) + throw new ArgumentOutOfRangeException("s"); + + IntPtr hglobal = Win32Native.CoTaskMemAlloc(new UIntPtr((uint)nb)); + + if (hglobal == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + else + { + fixed (char* firstChar = s) + { + String.wstrcpy((char *)hglobal, firstChar, s.Length + 1); + } + return hglobal; + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static IntPtr StringToCoTaskMemUTF8(String s) + { + const int MAX_UTF8_CHAR_SIZE = 3; + if (s == null) + { + return IntPtr.Zero; + } + else + { + int nb = (s.Length + 1) * MAX_UTF8_CHAR_SIZE; + + // Overflow checking + if (nb < s.Length) + throw new ArgumentOutOfRangeException("s"); + + IntPtr pMem = Win32Native.CoTaskMemAlloc(new UIntPtr((uint)nb +1)); + + if (pMem == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + else + { + byte* pbMem = (byte*)pMem; + int nbWritten = s.GetBytesFromEncoding(pbMem, nb, Encoding.UTF8); + pbMem[nbWritten] = 0; + return pMem; + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr StringToCoTaskMemAuto(String s) + { + // Ansi platforms are no longer supported + return StringToCoTaskMemUni(s); + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static IntPtr StringToCoTaskMemAnsi(String s) + { + if (s == null) + { + return IntPtr.Zero; + } + else + { + int nb = (s.Length + 1) * SystemMaxDBCSCharSize; + + // Overflow checking + if (nb < s.Length) + throw new ArgumentOutOfRangeException("s"); + + IntPtr hglobal = Win32Native.CoTaskMemAlloc(new UIntPtr((uint)nb)); + + if (hglobal == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + else + { + s.ConvertToAnsi((byte *)hglobal, nb, false, false); + return hglobal; + } + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void FreeCoTaskMem(IntPtr ptr) + { + if (IsNotWin32Atom(ptr)) { + Win32Native.CoTaskMemFree(ptr); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr ReAllocCoTaskMem(IntPtr pv, int cb) + { + IntPtr pNewMem = Win32Native.CoTaskMemRealloc(pv, new UIntPtr((uint)cb)); + if (pNewMem == IntPtr.Zero && cb != 0) + { + throw new OutOfMemoryException(); + } + return pNewMem; + } + + //==================================================================== + // BSTR allocation and dealocation. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static void FreeBSTR(IntPtr ptr) + { + if (IsNotWin32Atom(ptr)) + { + Win32Native.SysFreeString(ptr); + } + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr StringToBSTR(String s) + { + if (s == null) + return IntPtr.Zero; + + // Overflow checking + if (s.Length + 1 < s.Length) + throw new ArgumentOutOfRangeException("s"); + + IntPtr bstr = Win32Native.SysAllocStringLen(s, s.Length); + if (bstr == IntPtr.Zero) + throw new OutOfMemoryException(); + + return bstr; + } + + [System.Security.SecurityCritical] // auto-generated_required + public static String PtrToStringBSTR(IntPtr ptr) + { + return PtrToStringUni(ptr, (int)Win32Native.SysStringLen(ptr)); + } + +#if FEATURE_COMINTEROP + //==================================================================== + // release the COM component and if the reference hits 0 zombie this object + // further usage of this Object might throw an exception + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static int ReleaseComObject(Object o) + { + __ComObject co = null; + + // Make sure the obj is an __ComObject. + try + { + co = (__ComObject)o; + } + catch (InvalidCastException) + { + throw new ArgumentException(Environment.GetResourceString("Argument_ObjNotComObject"), "o"); + } + + return co.ReleaseSelf(); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern int InternalReleaseComObject(Object o); + + + //==================================================================== + // release the COM component and zombie this object + // further usage of this Object might throw an exception + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Int32 FinalReleaseComObject(Object o) + { + if (o == null) + throw new ArgumentNullException("o"); + Contract.EndContractBlock(); + + __ComObject co = null; + + // Make sure the obj is an __ComObject. + try + { + co = (__ComObject)o; + } + catch (InvalidCastException) + { + throw new ArgumentException(Environment.GetResourceString("Argument_ObjNotComObject"), "o"); + } + + co.FinalReleaseSelf(); + + return 0; + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void InternalFinalReleaseComObject(Object o); + + //==================================================================== + // This method retrieves data from the COM object. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Object GetComObjectData(Object obj, Object key) + { + // Validate that the arguments aren't null. + if (obj == null) + throw new ArgumentNullException("obj"); + if (key == null) + throw new ArgumentNullException("key"); + Contract.EndContractBlock(); + + __ComObject comObj = null; + + // Make sure the obj is an __ComObject. + try + { + comObj = (__ComObject)obj; + } + catch (InvalidCastException) + { + throw new ArgumentException(Environment.GetResourceString("Argument_ObjNotComObject"), "obj"); + } + + if (obj.GetType().IsWindowsRuntimeObject) + { + throw new ArgumentException(Environment.GetResourceString("Argument_ObjIsWinRTObject"), "obj"); + } + + // Retrieve the data from the __ComObject. + return comObj.GetData(key); + } + + //==================================================================== + // This method 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. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static bool SetComObjectData(Object obj, Object key, Object data) + { + // Validate that the arguments aren't null. The data can validly be null. + if (obj == null) + throw new ArgumentNullException("obj"); + if (key == null) + throw new ArgumentNullException("key"); + Contract.EndContractBlock(); + + __ComObject comObj = null; + + // Make sure the obj is an __ComObject. + try + { + comObj = (__ComObject)obj; + } + catch (InvalidCastException) + { + throw new ArgumentException(Environment.GetResourceString("Argument_ObjNotComObject"), "obj"); + } + + if (obj.GetType().IsWindowsRuntimeObject) + { + throw new ArgumentException(Environment.GetResourceString("Argument_ObjIsWinRTObject"), "obj"); + } + + // Retrieve the data from the __ComObject. + return comObj.SetData(key, data); + } + + //==================================================================== + // This method takes the given COM object and wraps it in an object + // of the specified type. The type must be derived from __ComObject. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Object CreateWrapperOfType(Object o, Type t) + { + // Validate the arguments. + if (t == null) + throw new ArgumentNullException("t"); + if (!t.IsCOMObject) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeNotComObject"), "t"); + if (t.IsGenericType) + throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "t"); + Contract.EndContractBlock(); + + if (t.IsWindowsRuntimeObject) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeIsWinRTType"), "t"); + + // Check for the null case. + if (o == null) + return null; + + // Make sure the object is a COM object. + if (!o.GetType().IsCOMObject) + throw new ArgumentException(Environment.GetResourceString("Argument_ObjNotComObject"), "o"); + if (o.GetType().IsWindowsRuntimeObject) + throw new ArgumentException(Environment.GetResourceString("Argument_ObjIsWinRTObject"), "o"); + + // Check to see if the type of the object is the requested type. + 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 == 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 thead already cached the wrapper so use that one instead. + Wrapper = GetComObjectData(o, t); + } + } + + return Wrapper; + } + + [System.Security.SecurityCritical] + public static TWrapper CreateWrapperOfType<T, TWrapper>(T o) + { + return (TWrapper)CreateWrapperOfType(o, typeof(TWrapper)); + } + + //==================================================================== + // Helper method called from CreateWrapperOfType. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Object InternalCreateWrapperOfType(Object o, Type t); + + //==================================================================== + // There may be a thread-based cache of COM components. This service can + // force the aggressive release of the current thread's cache. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [Obsolete("This API did not perform any operation and will be removed in future versions of the CLR.", false)] + public static void ReleaseThreadCache() + { + } + + //==================================================================== + // check if the type is visible from COM. + //==================================================================== + [System.Security.SecuritySafeCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern bool IsTypeVisibleFromCom(Type t); + + //==================================================================== + // IUnknown Helpers + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int /* HRESULT */ QueryInterface(IntPtr /* IUnknown */ pUnk, ref Guid iid, out IntPtr ppv); + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int /* ULONG */ AddRef(IntPtr /* IUnknown */ pUnk ); + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public static extern int /* ULONG */ Release(IntPtr /* IUnknown */ pUnk ); + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void GetNativeVariantForObject(Object obj, /* VARIANT * */ IntPtr pDstNativeVariant); + + [System.Security.SecurityCritical] + public static void GetNativeVariantForObject<T>(T obj, IntPtr pDstNativeVariant) + { + GetNativeVariantForObject((object)obj, pDstNativeVariant); + } + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern Object GetObjectForNativeVariant(/* VARIANT * */ IntPtr pSrcNativeVariant ); + + [System.Security.SecurityCritical] + public static T GetObjectForNativeVariant<T>(IntPtr pSrcNativeVariant) + { + return (T)GetObjectForNativeVariant(pSrcNativeVariant); + } + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern Object[] GetObjectsForNativeVariants(/* VARIANT * */ IntPtr aSrcNativeVariant, int cVars ); + + [System.Security.SecurityCritical] + public static T[] GetObjectsForNativeVariants<T>(IntPtr aSrcNativeVariant, int cVars) + { + object[] objects = GetObjectsForNativeVariants(aSrcNativeVariant, cVars); + T[] result = null; + + if (objects != null) + { + 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> + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int GetStartComSlot(Type t); + + /// <summary> + /// <para>Returns the last valid COM slot that GetMethodInfoForSlot will work on. </para> + /// </summary> + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern int GetEndComSlot(Type t); + + /// <summary> + /// <para>Returns the MemberInfo that COM callers calling through the exposed + /// vtable on the given slot will be calling. The slot should take into account + /// if the exposed interface is IUnknown based or IDispatch based. + /// For classes, the lookup is done on the default interface that will be + /// exposed for the class. </para> + /// </summary> + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern MemberInfo GetMethodInfoForComSlot(Type t, int slot, ref ComMemberType memberType); + + /// <summary> + /// <para>Returns the COM slot for a memeber info, taking into account whether + /// the exposed interface is IUnknown based or IDispatch based</para> + /// </summary> + [System.Security.SecurityCritical] // auto-generated_required + public static int GetComSlotForMethodInfo(MemberInfo m) + { + if (m== null) + throw new ArgumentNullException("m"); + + if (!(m is RuntimeMethodInfo)) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeMethodInfo"), "m"); + + if (!m.DeclaringType.IsInterface) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeInterfaceMethod"), "m"); + if (m.DeclaringType.IsGenericType) + throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "m"); + Contract.EndContractBlock(); + + return InternalGetComSlotForMethodInfo((IRuntimeMethodInfo)m); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int InternalGetComSlotForMethodInfo(IRuntimeMethodInfo m); + + //==================================================================== + // This method generates a GUID for the specified type. If the type + // has a GUID in the metadata then it is returned otherwise a stable + // guid GUID is generated based on the fully qualified name of the + // type. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Guid GenerateGuidForType(Type type) + { + Guid result = new Guid (); + FCallGenerateGuidForType (ref result, type); + return result; + } + + // The full assembly name is used to compute the GUID, so this should be SxS-safe + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void FCallGenerateGuidForType(ref Guid result, Type type); + + //==================================================================== + // This method generates a PROGID for the specified type. If the type + // has a PROGID in the metadata then it is returned otherwise a stable + // PROGID is generated based on the fully qualified name of the + // type. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static String GenerateProgIdForType(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + if (type.IsImport) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeMustNotBeComImport"), "type"); + if (type.IsGenericType) + throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "type"); + Contract.EndContractBlock(); + + if (!RegistrationServices.TypeRequiresRegistrationHelper(type)) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeMustBeComCreatable"), "type"); + + IList<CustomAttributeData> cas = CustomAttributeData.GetCustomAttributes(type); + for (int i = 0; i < cas.Count; i ++) + { + if (cas[i].Constructor.DeclaringType == typeof(ProgIdAttribute)) + { + // Retrieve the PROGID string from the ProgIdAttribute. + IList<CustomAttributeTypedArgument> caConstructorArgs = cas[i].ConstructorArguments; + Contract.Assert(caConstructorArgs.Count == 1, "caConstructorArgs.Count == 1"); + + CustomAttributeTypedArgument progIdConstructorArg = caConstructorArgs[0]; + Contract.Assert(progIdConstructorArg.ArgumentType == typeof(String), "progIdConstructorArg.ArgumentType == typeof(String)"); + + String strProgId = (String)progIdConstructorArg.Value; + + if (strProgId == null) + strProgId = String.Empty; + + return strProgId; + } + } + + // If there is no prog ID attribute then use the full name of the type as the prog id. + return type.FullName; + } + + //==================================================================== + // This method binds to the specified moniker. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Object BindToMoniker(String monikerName) + { + Object obj = null; + IBindCtx bindctx = null; + CreateBindCtx(0, out bindctx); + + UInt32 cbEaten; + IMoniker pmoniker = null; + MkParseDisplayName(bindctx, monikerName, out cbEaten, out pmoniker); + + BindMoniker(pmoniker, 0, ref IID_IUnknown, out obj); + return obj; + } + + //==================================================================== + // This method gets the currently running object. + //==================================================================== + [System.Security.SecurityCritical] // auto-generated_required + public static Object GetActiveObject(String progID) + { + Object obj = null; + Guid clsid; + + // Call CLSIDFromProgIDEx first then fall back on CLSIDFromProgID if + // CLSIDFromProgIDEx doesn't exist. + try + { + CLSIDFromProgIDEx(progID, out clsid); + } +// catch + catch(Exception) + { + CLSIDFromProgID(progID, out clsid); + } + + GetActiveObject(ref clsid, IntPtr.Zero, out obj); + return obj; + } + + [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)] + [SuppressUnmanagedCodeSecurity] + [System.Security.SecurityCritical] // auto-generated + private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid); + + [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)] + [SuppressUnmanagedCodeSecurity] + [System.Security.SecurityCritical] // auto-generated + private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid); + + [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)] + [SuppressUnmanagedCodeSecurity] + [System.Security.SecurityCritical] // auto-generated + private static extern void CreateBindCtx(UInt32 reserved, out IBindCtx ppbc); + + [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)] + [SuppressUnmanagedCodeSecurity] + [System.Security.SecurityCritical] // auto-generated + private static extern void MkParseDisplayName(IBindCtx pbc, [MarshalAs(UnmanagedType.LPWStr)] String szUserName, out UInt32 pchEaten, out IMoniker ppmk); + + [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)] + [SuppressUnmanagedCodeSecurity] + [System.Security.SecurityCritical] // auto-generated + private static extern void BindMoniker(IMoniker pmk, UInt32 grfOpt, ref Guid iidResult, [MarshalAs(UnmanagedType.Interface)] out Object ppvResult); + + [DllImport(Microsoft.Win32.Win32Native.OLEAUT32, PreserveSig = false)] + [SuppressUnmanagedCodeSecurity] + [System.Security.SecurityCritical] // auto-generated + private static extern void GetActiveObject(ref Guid rclsid, IntPtr reserved, [MarshalAs(UnmanagedType.Interface)] out Object ppunk); + + //======================================================================== + // Private method called from remoting to support ServicedComponents. + //======================================================================== + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern bool InternalSwitchCCW(Object oldtp, Object newtp); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern Object InternalWrapIUnknownWithComObject(IntPtr i); + + //======================================================================== + // Private method called from EE upon use of license/ICF2 marshaling. + //======================================================================== + [SecurityCritical] + private static IntPtr LoadLicenseManager() + { + Assembly sys = Assembly.Load("System, Version="+ ThisAssembly.Version + + ", Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken); + Type t = sys.GetType("System.ComponentModel.LicenseManager"); + if (t == null || !t.IsVisible) + return IntPtr.Zero; + return t.TypeHandle.Value; + } + + [System.Security.SecurityCritical] // auto-generated_required + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void ChangeWrapperHandleStrength(Object otp, bool fIsWeak); + + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void InitializeWrapperForWinRT(object o, ref IntPtr pUnk); + +#if FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void InitializeManagedWinRTFactoryObject(object o, RuntimeType runtimeClassType); +#endif + + //======================================================================== + // Create activation factory and wraps it with a unique RCW + //======================================================================== + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object GetNativeActivationFactory(Type type); + + //======================================================================== + // Methods allowing retrieval of the IIDs exposed by an underlying WinRT + // object, as specified by the object's IInspectable::GetIids() + //======================================================================== + [System.Security.SecurityCritical] + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + private static extern void _GetInspectableIids(ObjectHandleOnStack obj, ObjectHandleOnStack guids); + + [System.Security.SecurityCritical] + internal static System.Guid[] GetInspectableIids(object obj) + { + System.Guid[] result = null; + System.__ComObject comObj = obj as System.__ComObject; + if (comObj != null) + { + _GetInspectableIids(JitHelpers.GetObjectHandleOnStack(ref comObj), + JitHelpers.GetObjectHandleOnStack(ref result)); + } + + return result; + } + + //======================================================================== + // Methods allowing retrieval of the cached WinRT type corresponding to + // the specified GUID + //======================================================================== + [System.Security.SecurityCritical] + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + private static extern void _GetCachedWinRTTypeByIid( + ObjectHandleOnStack appDomainObj, + System.Guid iid, + out IntPtr rthHandle); + + [System.Security.SecurityCritical] + internal static System.Type GetCachedWinRTTypeByIid( + System.AppDomain ad, + System.Guid iid) + { + IntPtr rthHandle; + _GetCachedWinRTTypeByIid(JitHelpers.GetObjectHandleOnStack(ref ad), + iid, + out rthHandle); + System.Type res = Type.GetTypeFromHandleUnsafe(rthHandle); + return res; + } + + + //======================================================================== + // Methods allowing retrieval of the WinRT types cached in the specified + // app domain + //======================================================================== + [System.Security.SecurityCritical] + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + private static extern void _GetCachedWinRTTypes( + ObjectHandleOnStack appDomainObj, + ref int epoch, + ObjectHandleOnStack winrtTypes); + + [System.Security.SecurityCritical] + internal static System.Type[] GetCachedWinRTTypes( + System.AppDomain ad, + ref int epoch) + { + System.IntPtr[] res = null; + + _GetCachedWinRTTypes(JitHelpers.GetObjectHandleOnStack(ref ad), + ref epoch, + JitHelpers.GetObjectHandleOnStack(ref res)); + + System.Type[] result = new System.Type[res.Length]; + for (int i = 0; i < res.Length; ++i) + { + result[i] = Type.GetTypeFromHandleUnsafe(res[i]); + } + + return result; + } + + [System.Security.SecurityCritical] + internal static System.Type[] GetCachedWinRTTypes( + System.AppDomain ad) + { + int dummyEpoch = 0; + return GetCachedWinRTTypes(ad, ref dummyEpoch); + } + + +#endif // FEATURE_COMINTEROP + + [System.Security.SecurityCritical] // auto-generated_required + public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t) + { + // Validate the parameters + if (ptr == IntPtr.Zero) + throw new ArgumentNullException("ptr"); + + if (t == null) + throw new ArgumentNullException("t"); + Contract.EndContractBlock(); + + if ((t as RuntimeType) == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "t"); + + if (t.IsGenericType) + throw new ArgumentException(Environment.GetResourceString("Argument_NeedNonGenericType"), "t"); + + Type c = t.BaseType; + if (c == null || (c != typeof(Delegate) && c != typeof(MulticastDelegate))) + throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDelegate"), "t"); + + return GetDelegateForFunctionPointerInternal(ptr, t); + } + + [System.Security.SecurityCritical] + public static TDelegate GetDelegateForFunctionPointer<TDelegate>(IntPtr ptr) + { + return (TDelegate)(object)GetDelegateForFunctionPointer(ptr, typeof(TDelegate)); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, Type t); + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr GetFunctionPointerForDelegate(Delegate d) + { + if (d == null) + throw new ArgumentNullException("d"); + Contract.EndContractBlock(); + + return GetFunctionPointerForDelegateInternal(d); + } + + [System.Security.SecurityCritical] + public static IntPtr GetFunctionPointerForDelegate<TDelegate>(TDelegate d) + { + return GetFunctionPointerForDelegate((Delegate)(object)d); + } + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern IntPtr GetFunctionPointerForDelegateInternal(Delegate d); + +#if FEATURE_LEGACYSURFACE + +#if FEATURE_COMINTEROP + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr SecureStringToBSTR(SecureString s) { + if( s == null) { + throw new ArgumentNullException("s"); + } + Contract.EndContractBlock(); + + return s.ToBSTR(); + } +#endif + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr SecureStringToCoTaskMemAnsi(SecureString s) { + if( s == null) { + throw new ArgumentNullException("s"); + } + Contract.EndContractBlock(); + + return s.ToAnsiStr(false); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr SecureStringToCoTaskMemUnicode(SecureString s) + { + if (s == null) + { + throw new ArgumentNullException("s"); + } + Contract.EndContractBlock(); + + return s.ToUniStr(false); + } + +#endif // FEATURE_LEGACYSURFACE + + +#if FEATURE_COMINTEROP + [System.Security.SecurityCritical] // auto-generated_required + public static void ZeroFreeBSTR(IntPtr s) + { + Win32Native.ZeroMemory(s, (UIntPtr)(Win32Native.SysStringLen(s) * 2)); + FreeBSTR(s); + } +#endif + + [System.Security.SecurityCritical] // auto-generated_required + public static void ZeroFreeCoTaskMemAnsi(IntPtr s) + { + Win32Native.ZeroMemory(s, (UIntPtr)(Win32Native.lstrlenA(s))); + FreeCoTaskMem(s); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void ZeroFreeCoTaskMemUnicode(IntPtr s) + { + Win32Native.ZeroMemory(s, (UIntPtr)(Win32Native.lstrlenW(s) * 2)); + FreeCoTaskMem(s); + } + + [System.Security.SecurityCritical] // auto-generated_required + unsafe public static void ZeroFreeCoTaskMemUTF8(IntPtr s) + { + Win32Native.ZeroMemory(s, (UIntPtr)System.StubHelpers.StubHelpers.strlen((sbyte*)s)); + FreeCoTaskMem(s); + } + +#if FEATURE_LEGACYSURFACE + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr SecureStringToGlobalAllocAnsi(SecureString s) { + if( s == null) { + throw new ArgumentNullException("s"); + } + Contract.EndContractBlock(); + + return s.ToAnsiStr(true); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static IntPtr SecureStringToGlobalAllocUnicode(SecureString s) { + if( s == null) { + throw new ArgumentNullException("s"); + } + Contract.EndContractBlock(); + + return s.ToUniStr(true); + } +#endif // FEATURE_LEGACYSURFACE + + [System.Security.SecurityCritical] // auto-generated_required + public static void ZeroFreeGlobalAllocAnsi(IntPtr s) { + Win32Native.ZeroMemory(s, (UIntPtr)(Win32Native.lstrlenA(s))); + FreeHGlobal(s); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static void ZeroFreeGlobalAllocUnicode(IntPtr s) { + Win32Native.ZeroMemory(s, (UIntPtr)(Win32Native.lstrlenW(s) * 2)); + FreeHGlobal(s); + } + } + +#if FEATURE_COMINTEROP && !FEATURE_CORECLR // current implementation requires reflection only load + //======================================================================== + // Typelib importer callback implementation. + //======================================================================== + internal class ImporterCallback : ITypeLibImporterNotifySink + { + public void ReportEvent(ImporterEventKind EventKind, int EventCode, String EventMsg) + { + } + + [System.Security.SecuritySafeCritical] // overrides transparent public member + public Assembly ResolveRef(Object TypeLib) + { + try + { + // Create the TypeLibConverter. + ITypeLibConverter TLBConv = new TypeLibConverter(); + + // Convert the typelib. + return TLBConv.ConvertTypeLibToAssembly(TypeLib, + Marshal.GetTypeLibName((ITypeLib)TypeLib) + ".dll", + 0, + new ImporterCallback(), + null, + null, + null, + null); + } + catch(Exception) +// catch + { + return null; + } + } + } +#endif // FEATURE_COMINTEROP && !FEATURE_CORECLR +} + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs b/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs new file mode 100644 index 0000000000..b916778019 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: This exception is thrown when the marshaller encounters a signature +** that has an invalid MarshalAs CA for a given argument or is not +** supported. +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Runtime.Serialization; + + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class MarshalDirectiveException : SystemException { + public MarshalDirectiveException() + : base(Environment.GetResourceString("Arg_MarshalDirectiveException")) { + SetErrorCode(__HResults.COR_E_MARSHALDIRECTIVE); + } + + public MarshalDirectiveException(String message) + : base(message) { + SetErrorCode(__HResults.COR_E_MARSHALDIRECTIVE); + } + + public MarshalDirectiveException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.COR_E_MARSHALDIRECTIVE); + } + + protected MarshalDirectiveException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/NativeBuffer.cs b/src/mscorlib/src/System/Runtime/InteropServices/NativeBuffer.cs new file mode 100644 index 0000000000..94261621c3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/NativeBuffer.cs @@ -0,0 +1,175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// Wrapper for access to the native heap. Dispose to free the memory. Try to use with using statements. + /// Does not allocate zero size buffers, and will free the existing native buffer if capacity is dropped to zero. + /// </summary> + /// <remarks> + /// Suggested use through P/Invoke: define DllImport arguments that take a byte buffer as SafeHandle. + /// + /// Using SafeHandle will ensure that the buffer will not get collected during a P/Invoke. + /// (Notably AddRef and ReleaseRef will be called by the interop layer.) + /// + /// This class is not threadsafe, changing the capacity or disposing on multiple threads risks duplicate heap + /// handles or worse. + /// </remarks> + internal class NativeBuffer : IDisposable + { + [System.Security.SecurityCritical] + private readonly static SafeHandle s_emptyHandle; + [System.Security.SecurityCritical] + private SafeHeapHandle _handle; + private ulong _capacity; + + [System.Security.SecuritySafeCritical] + static NativeBuffer() + { + s_emptyHandle = new EmptySafeHandle(); + } + + /// <summary> + /// Create a buffer with at least the specified initial capacity in bytes. + /// </summary> + public NativeBuffer(ulong initialMinCapacity = 0) + { + EnsureByteCapacity(initialMinCapacity); + } + + protected unsafe void* VoidPointer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Security.SecurityCritical] + get + { + return _handle == null ? null : _handle.DangerousGetHandle().ToPointer(); + } + } + + protected unsafe byte* BytePointer + { + [System.Security.SecurityCritical] + get + { + return (byte*)VoidPointer; + } + } + + /// <summary> + /// Get the handle for the buffer. + /// </summary> + [System.Security.SecuritySafeCritical] + public SafeHandle GetHandle() + { + // Marshalling code will throw on null for SafeHandle + return _handle ?? s_emptyHandle; + } + + /// <summary> + /// The capacity of the buffer in bytes. + /// </summary> + public ulong ByteCapacity + { + get { return _capacity; } + } + + /// <summary> + /// Ensure capacity in bytes is at least the given minimum. + /// </summary> + /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception> + /// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to set <paramref name="nameof(minCapacity)"/> to a value that is larger than the maximum addressable memory.</exception> + [System.Security.SecuritySafeCritical] + public void EnsureByteCapacity(ulong minCapacity) + { + if (_capacity < minCapacity) + { + Resize(minCapacity); + _capacity = minCapacity; + } + } + + public unsafe byte this[ulong index] + { + [System.Security.SecuritySafeCritical] + get + { + if (index >= _capacity) throw new ArgumentOutOfRangeException(); + return BytePointer[index]; + } + [System.Security.SecuritySafeCritical] + set + { + if (index >= _capacity) throw new ArgumentOutOfRangeException(); + BytePointer[index] = value; + } + } + + [System.Security.SecuritySafeCritical] + private unsafe void Resize(ulong byteLength) + { + if (byteLength == 0) + { + ReleaseHandle(); + return; + } + + if (_handle == null) + { + _handle = new SafeHeapHandle(byteLength); + } + else + { + _handle.Resize(byteLength); + } + } + + [System.Security.SecuritySafeCritical] + private void ReleaseHandle() + { + if (_handle != null) + { + _capacity = 0; + _handle = null; + } + } + + /// <summary> + /// Release the backing buffer + /// </summary> + [System.Security.SecuritySafeCritical] + public virtual void Free() + { + ReleaseHandle(); + } + + [System.Security.SecuritySafeCritical] + public void Dispose() + { + Free(); + } + + [System.Security.SecurityCritical] + private sealed class EmptySafeHandle : SafeHandle + { + public EmptySafeHandle() : base(IntPtr.Zero, true) { } + + public override bool IsInvalid + { + [System.Security.SecurityCritical] + get + { return true; } + } + + [System.Security.SecurityCritical] + protected override bool ReleaseHandle() + { + return true; + } + } + } +}
\ No newline at end of file diff --git a/src/mscorlib/src/System/Runtime/InteropServices/NativeCallableAttribute.cs b/src/mscorlib/src/System/Runtime/InteropServices/NativeCallableAttribute.cs new file mode 100644 index 0000000000..706ef80019 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/NativeCallableAttribute.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** Any method marked with NativeCallableAttribute can be directly called from +** native code.The function token can be loaded to a local variable using LDFTN and +** passed as a callback to native method. +=============================================================================*/ + +using System; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + + [AttributeUsage(AttributeTargets.Method)] + public sealed class NativeCallableAttribute : Attribute + { + public NativeCallableAttribute() + { + } + // Optional. If omitted , compiler will choose one for you. + public CallingConvention CallingConvention; + // Optional. If omitted, then the method is native callable, but no EAT is emitted. + public string EntryPoint; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/NativeMethods.cs b/src/mscorlib/src/System/Runtime/InteropServices/NativeMethods.cs new file mode 100644 index 0000000000..82cd4fa963 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/NativeMethods.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +/*============================================================ +** +** +** Purpose: part of ComEventHelpers APIs which allow binding +** managed delegates to COM's connection point based events. +** +**/ +#if FEATURE_COMINTEROP + +namespace System.Runtime.InteropServices { + + internal static class NativeMethods { + + [ + System.Security.SuppressUnmanagedCodeSecurity, + DllImport("oleaut32.dll", PreserveSig = false), + System.Security.SecurityCritical + ] + internal static extern void VariantClear(IntPtr variant); + + [ + System.Security.SuppressUnmanagedCodeSecurity, + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("00020400-0000-0000-C000-000000000046") + ] + internal interface IDispatch { + + [System.Security.SecurityCritical] + void GetTypeInfoCount(out uint pctinfo); + + [System.Security.SecurityCritical] + void GetTypeInfo(uint iTInfo, int lcid, out IntPtr info); + + [System.Security.SecurityCritical] + void GetIDsOfNames( + ref Guid iid, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] + string[] names, + uint cNames, + int lcid, + [Out] + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] + int[] rgDispId); + + [System.Security.SecurityCritical] + void Invoke( + int dispIdMember, + ref Guid riid, + int lcid, + ComTypes.INVOKEKIND wFlags, + ref ComTypes.DISPPARAMS pDispParams, + IntPtr pvarResult, + IntPtr pExcepInfo, + IntPtr puArgErr); + } + } +} + +#endif diff --git a/src/mscorlib/src/System/Runtime/InteropServices/NonPortable.cs b/src/mscorlib/src/System/Runtime/InteropServices/NonPortable.cs new file mode 100644 index 0000000000..408f56c8e2 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/NonPortable.cs @@ -0,0 +1,209 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Dummy implementations of non-portable interop methods that just throw PlatformNotSupportedException + +namespace System.Runtime.InteropServices +{ + public static partial class Marshal + { + [System.Security.SecurityCritical] + public static int GetHRForException(Exception e) + { + return (e != null) ? e.HResult : 0; + } + + [System.Security.SecurityCriticalAttribute] + public static int AddRef(System.IntPtr pUnk) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static bool AreComObjectsAvailableForCleanup() + { + return false; + } + + [System.Security.SecurityCriticalAttribute] + public static System.IntPtr CreateAggregatedObject(System.IntPtr pOuter, object o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static System.IntPtr CreateAggregatedObject<T>(System.IntPtr pOuter, T o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static object CreateWrapperOfType(object o, System.Type t) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static TWrapper CreateWrapperOfType<T, TWrapper>(T o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static int FinalReleaseComObject(object o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static System.IntPtr GetComInterfaceForObject(object o, System.Type T) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static System.IntPtr GetComInterfaceForObject(object o, System.Type T, System.Runtime.InteropServices.CustomQueryInterfaceMode mode) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static System.IntPtr GetComInterfaceForObject<T, TInterface>(T o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static System.IntPtr GetIUnknownForObject(object o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static void GetNativeVariantForObject(object obj, System.IntPtr pDstNativeVariant) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static void GetNativeVariantForObject<T>(T obj, System.IntPtr pDstNativeVariant) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static object GetObjectForIUnknown(System.IntPtr pUnk) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static object GetObjectForNativeVariant(System.IntPtr pSrcNativeVariant) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static T GetObjectForNativeVariant<T>(System.IntPtr pSrcNativeVariant) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static object[] GetObjectsForNativeVariants(System.IntPtr aSrcNativeVariant, int cVars) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static T[] GetObjectsForNativeVariants<T>(System.IntPtr aSrcNativeVariant, int cVars) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static int GetStartComSlot(System.Type t) + { + throw new PlatformNotSupportedException(); + } + + public static System.Type GetTypeFromCLSID(System.Guid clsid) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static string GetTypeInfoName(System.Runtime.InteropServices.ComTypes.ITypeInfo typeInfo) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static object GetUniqueObjectForIUnknown(System.IntPtr unknown) + { + throw new PlatformNotSupportedException(); + } + + public static bool IsComObject(object o) + { + return false; + } + + [System.Security.SecurityCriticalAttribute] + public static int QueryInterface(System.IntPtr pUnk, ref System.Guid iid, out System.IntPtr ppv) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static int Release(System.IntPtr pUnk) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static int ReleaseComObject(object o) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static void ZeroFreeBSTR(System.IntPtr s) + { + throw new PlatformNotSupportedException(); + } + } + + public class DispatchWrapper + { + public DispatchWrapper(object obj) + { + throw new PlatformNotSupportedException(); + } + + public object WrappedObject + { + get + { + throw new PlatformNotSupportedException(); + } + } + } + + public static class ComEventsHelper + { + [System.Security.SecurityCriticalAttribute] + public static void Combine(object rcw, System.Guid iid, int dispid, System.Delegate d) + { + throw new PlatformNotSupportedException(); + } + + [System.Security.SecurityCriticalAttribute] + public static System.Delegate Remove(object rcw, System.Guid iid, int dispid, System.Delegate d) + { + throw new PlatformNotSupportedException(); + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ObjectCreationDelegate.cs b/src/mscorlib/src/System/Runtime/InteropServices/ObjectCreationDelegate.cs new file mode 100644 index 0000000000..f011253e1e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/ObjectCreationDelegate.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** Delegate: ObjectCreationDelegate +** +** +** Purpose: Delegate called to create a classic COM object as an alternative to +** CoCreateInstance. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + // Delegate called when a managed object wishes to instantiate its unmanaged + // portion. The IUnknown of the managed object (the aggregator) is passed as a + // parameter and the delegate should return the IUnknown of the unmanaged object + // (the aggregatee). Both are passed as int's to avoid any marshalling. +[System.Runtime.InteropServices.ComVisible(true)] + public delegate IntPtr ObjectCreationDelegate(IntPtr aggregator); +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/PInvokeMap.cs b/src/mscorlib/src/System/Runtime/InteropServices/PInvokeMap.cs new file mode 100644 index 0000000000..f47165544a --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/PInvokeMap.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// PInvokeMap is an enum that defines the PInvoke attributes. These +// values are defined in CorHdr.h. +// +// +namespace System.Runtime.InteropServices { + using System.Runtime.InteropServices; + using System; + + // This Enum matchs the CorPinvokeMap defined in CorHdr.h + [Serializable] + internal enum PInvokeMap + { + NoMangle = 0x0001, // Pinvoke is to use the member name as specified. + CharSetMask = 0x0006, // Heuristic used in data type & name mapping. + CharSetNotSpec = 0x0000, + CharSetAnsi = 0x0002, + CharSetUnicode = 0x0004, + CharSetAuto = 0x0006, + + PinvokeOLE = 0x0020, // Heuristic: pinvoke will return hresult, with return value becoming the retval param. Not relevant for fields. + SupportsLastError = 0x0040, // Information about target function. Not relevant for fields. + + BestFitMask = 0x0030, + BestFitEnabled = 0x0010, + BestFitDisabled = 0x0020, + BestFitUseAsm = 0x0030, + + ThrowOnUnmappableCharMask = 0x3000, + ThrowOnUnmappableCharEnabled = 0x1000, + ThrowOnUnmappableCharDisabled = 0x2000, + ThrowOnUnmappableCharUseAsm = 0x3000, + + // None of the calling convention flags is relevant for fields. + CallConvMask = 0x0700, + CallConvWinapi = 0x0100, // Pinvoke will use native callconv appropriate to target windows platform. + CallConvCdecl = 0x0200, + CallConvStdcall = 0x0300, + CallConvThiscall = 0x0400, // In M9, pinvoke will raise exception. + CallConvFastcall = 0x0500, + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/RegistrationServices.cs b/src/mscorlib/src/System/Runtime/InteropServices/RegistrationServices.cs new file mode 100644 index 0000000000..7d3670d877 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/RegistrationServices.cs @@ -0,0 +1,1087 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: This class provides services for registering and unregistering +** a managed server for use by COM. +** +** +** +** +** Change the way how to register and unregister a managed server +** +=============================================================================*/ +namespace System.Runtime.InteropServices { + + using System; + using System.Collections; + using System.IO; + using System.Reflection; + using System.Security; + using System.Security.Permissions; + using System.Text; + using System.Threading; + using Microsoft.Win32; + using System.Runtime.CompilerServices; + using System.Globalization; + using System.Runtime.Versioning; + using System.Diagnostics.Contracts; + + [Flags] + public enum RegistrationClassContext + { + + + InProcessServer = 0x1, + InProcessHandler = 0x2, + LocalServer = 0x4, + InProcessServer16 = 0x8, + RemoteServer = 0x10, + InProcessHandler16 = 0x20, + Reserved1 = 0x40, + Reserved2 = 0x80, + Reserved3 = 0x100, + Reserved4 = 0x200, + NoCodeDownload = 0x400, + Reserved5 = 0x800, + NoCustomMarshal = 0x1000, + EnableCodeDownload = 0x2000, + NoFailureLog = 0x4000, + DisableActivateAsActivator = 0x8000, + EnableActivateAsActivator = 0x10000, + FromDefaultContext = 0x20000 + } + + + [Flags] + public enum RegistrationConnectionType + { + SingleUse = 0, + MultipleUse = 1, + MultiSeparate = 2, + Suspended = 4, + Surrogate = 8, + } + + [Guid("475E398F-8AFA-43a7-A3BE-F4EF8D6787C9")] + [ClassInterface(ClassInterfaceType.None)] +[System.Runtime.InteropServices.ComVisible(true)] + public class RegistrationServices : IRegistrationServices + { + #region Constants + + private const String strManagedCategoryGuid = "{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}"; + private const String strDocStringPrefix = ""; + private const String strManagedTypeThreadingModel = "Both"; + private const String strComponentCategorySubKey = "Component Categories"; + private const String strManagedCategoryDescription = ".NET Category"; + private const String strImplementedCategoriesSubKey = "Implemented Categories"; + private const String strMsCorEEFileName = "mscoree.dll"; + private const String strRecordRootName = "Record"; + private const String strClsIdRootName = "CLSID"; + private const String strTlbRootName = "TypeLib"; + private static Guid s_ManagedCategoryGuid = new Guid(strManagedCategoryGuid); + + #endregion + + + #region IRegistrationServices + + [System.Security.SecurityCritical] // auto-generated_required + public virtual bool RegisterAssembly(Assembly assembly, AssemblyRegistrationFlags flags) + { + // Validate the arguments. + if (assembly == null) + throw new ArgumentNullException("assembly"); + + if (assembly.ReflectionOnly) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsmLoadedForReflectionOnly")); + Contract.EndContractBlock(); + + RuntimeAssembly rtAssembly = assembly as RuntimeAssembly; + if (rtAssembly == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly")); + + // Retrieve the assembly names. + String strAsmName = assembly.FullName; + if (strAsmName == null) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NoAsmName")); + + // Retrieve the assembly codebase. + String strAsmCodeBase = null; + if ((flags & AssemblyRegistrationFlags.SetCodeBase) != 0) + { + strAsmCodeBase = rtAssembly.GetCodeBase(false); + if (strAsmCodeBase == null) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NoAsmCodeBase")); + } + + // Go through all the registerable types in the assembly and register them. + Type[] aTypes = GetRegistrableTypesInAssembly(assembly); + int NumTypes = aTypes.Length; + + String strAsmVersion = rtAssembly.GetVersion().ToString(); + + // Retrieve the runtime version used to build the assembly. + String strRuntimeVersion = assembly.ImageRuntimeVersion; + + for (int cTypes = 0; cTypes < NumTypes; cTypes++) + { + if (IsRegisteredAsValueType(aTypes[cTypes])) + RegisterValueType(aTypes[cTypes], strAsmName, strAsmVersion, strAsmCodeBase, strRuntimeVersion); + else if (TypeRepresentsComType(aTypes[cTypes])) + RegisterComImportedType(aTypes[cTypes], strAsmName, strAsmVersion, strAsmCodeBase, strRuntimeVersion); + else + RegisterManagedType(aTypes[cTypes], strAsmName, strAsmVersion, strAsmCodeBase, strRuntimeVersion); + + CallUserDefinedRegistrationMethod(aTypes[cTypes], true); + } + + // If this assembly has the PIA attribute, then register it as a PIA. + Object[] aPIAAttrs = assembly.GetCustomAttributes(typeof(PrimaryInteropAssemblyAttribute), false); + int NumPIAAttrs = aPIAAttrs.Length; + for (int cPIAAttrs = 0; cPIAAttrs < NumPIAAttrs; cPIAAttrs++) + RegisterPrimaryInteropAssembly(rtAssembly, strAsmCodeBase, (PrimaryInteropAssemblyAttribute)aPIAAttrs[cPIAAttrs]); + + // Return value indicating if we actually registered any types. + if (aTypes.Length > 0 || NumPIAAttrs > 0) + return true; + else + return false; + } + + [System.Security.SecurityCritical] // auto-generated_required + public virtual bool UnregisterAssembly(Assembly assembly) + { + // Validate the arguments. + if (assembly == null) + throw new ArgumentNullException("assembly"); + + if (assembly.ReflectionOnly) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsmLoadedForReflectionOnly")); + Contract.EndContractBlock(); + + RuntimeAssembly rtAssembly = assembly as RuntimeAssembly; + if (rtAssembly == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly")); + + bool bAllVersionsGone = true; + + // Go through all the registrable types in the assembly and register them. + Type[] aTypes = GetRegistrableTypesInAssembly(assembly); + int NumTypes = aTypes.Length; + + // Retrieve the assembly version + String strAsmVersion = rtAssembly.GetVersion().ToString(); + for (int cTypes = 0;cTypes < NumTypes;cTypes++) + { + CallUserDefinedRegistrationMethod(aTypes[cTypes], false); + + if (IsRegisteredAsValueType(aTypes[cTypes])) + { + if (!UnregisterValueType(aTypes[cTypes], strAsmVersion)) + bAllVersionsGone = false; + } + else if (TypeRepresentsComType(aTypes[cTypes])) + { + if (!UnregisterComImportedType(aTypes[cTypes], strAsmVersion)) + bAllVersionsGone = false; + } + else + { + if (!UnregisterManagedType(aTypes[cTypes], strAsmVersion)) + bAllVersionsGone = false; + } + } + + // If this assembly has the PIA attribute, then unregister it as a PIA. + Object[] aPIAAttrs = assembly.GetCustomAttributes(typeof(PrimaryInteropAssemblyAttribute),false); + int NumPIAAttrs = aPIAAttrs.Length; + if (bAllVersionsGone) + { + for (int cPIAAttrs = 0;cPIAAttrs < NumPIAAttrs;cPIAAttrs++) + UnregisterPrimaryInteropAssembly(assembly, (PrimaryInteropAssemblyAttribute)aPIAAttrs[cPIAAttrs]); + } + + // Return value indicating if we actually un-registered any types. + if (aTypes.Length > 0 || NumPIAAttrs > 0) + return true; + else + return false; + } + + [System.Security.SecurityCritical] // auto-generated_required + public virtual Type[] GetRegistrableTypesInAssembly(Assembly assembly) + { + // Validate the arguments. + if (assembly == null) + throw new ArgumentNullException("assembly"); + Contract.EndContractBlock(); + + if (!(assembly is RuntimeAssembly)) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"), "assembly"); + + // Retrieve the list of types in the assembly. + Type[] aTypes = assembly.GetExportedTypes(); + int NumTypes = aTypes.Length; + + // Create an array list that will be filled in. + ArrayList TypeList = new ArrayList(); + + // Register all the types that require registration. + for (int cTypes = 0; cTypes < NumTypes; cTypes++) + { + Type CurrentType = aTypes[cTypes]; + if (TypeRequiresRegistration(CurrentType)) + TypeList.Add(CurrentType); + } + + // Copy the array list to an array and return it. + Type[] RetArray = new Type[TypeList.Count]; + TypeList.CopyTo(RetArray); + return RetArray; + } + + [System.Security.SecurityCritical] // auto-generated_required + public virtual String GetProgIdForType(Type type) + { + return Marshal.GenerateProgIdForType(type); + } + + [System.Security.SecurityCritical] // auto-generated_required + public virtual void RegisterTypeForComClients(Type type, ref Guid g) + { +#if FEATURE_COMINTEROP_MANAGED_ACTIVATION + if(type == null) + throw new ArgumentNullException("type"); + Contract.EndContractBlock(); + if((type as RuntimeType) == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"),"type"); + if(!TypeRequiresRegistration(type)) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeMustBeComCreatable"),"type"); + + // Call the native method to do CoRegisterClassObject + RegisterTypeForComClientsNative(type, ref g); +#else // FEATURE_COMINTEROP_MANAGED_ACTIVATION + throw new NotImplementedException("CoreCLR_REMOVED -- managed activation removed"); +#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION + } + + public virtual Guid GetManagedCategoryGuid() + { + return s_ManagedCategoryGuid; + } + + [System.Security.SecurityCritical] // auto-generated_required + public virtual bool TypeRequiresRegistration(Type type) + { + return TypeRequiresRegistrationHelper(type); + } + + [System.Security.SecuritySafeCritical] // auto-generated + public virtual bool TypeRepresentsComType(Type type) + { + // If the type is not a COM import, then it does not represent a COM type. + if (!type.IsCOMObject) + return false; + + // If it is marked as tdImport, then it represents a COM type directly. + if (type.IsImport) + return true; + + // If the type is derived from a tdImport class and has the same GUID as the + // imported class, then it represents a COM type. + Type baseComImportType = GetBaseComImportType(type); + Contract.Assert(baseComImportType != null, "baseComImportType != null"); + if (Marshal.GenerateGuidForType(type) == Marshal.GenerateGuidForType(baseComImportType)) + return true; + + return false; + } + + #endregion + + + #region Public methods not on IRegistrationServices + [System.Security.SecurityCritical] // auto-generated_required + [ComVisible(false)] + public virtual int RegisterTypeForComClients(Type type, RegistrationClassContext classContext, RegistrationConnectionType flags) + { +#if FEATURE_COMINTEROP_MANAGED_ACTIVATION + if (type == null) + throw new ArgumentNullException("type"); + Contract.EndContractBlock(); + if ((type as RuntimeType) == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"),"type"); + if (!TypeRequiresRegistration(type)) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeMustBeComCreatable"),"type"); + + // Call the native method to do CoRegisterClassObject + return RegisterTypeForComClientsExNative(type, classContext, flags); +#else // FEATURE_COMINTEROP_MANAGED_ACTIVATION + throw new NotImplementedException("CoreCLR_REMOVED -- managed activation removed"); +#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION + } + + [System.Security.SecurityCritical] // auto-generated_required + [ComVisible(false)] + public virtual void UnregisterTypeForComClients(int cookie) + { + // Call the native method to do CoRevokeClassObject. + CoRevokeClassObject(cookie); + } + + #endregion + + + #region Internal helpers + + [System.Security.SecurityCritical] // auto-generated_required + internal static bool TypeRequiresRegistrationHelper(Type type) + { + // If the type is not a class or a value class, then it does not get registered. + if (!type.IsClass && !type.IsValueType) + return false; + + // If the type is abstract then it does not get registered. + if (type.IsAbstract) + return false; + + // If the does not have a public default constructor then is not creatable from COM so + // it does not require registration unless it is a value class. + if (!type.IsValueType && type.GetConstructor(BindingFlags.Instance | BindingFlags.Public,null,Array.Empty<Type>(),null) == null) + return false; + + // All other conditions are met so check to see if the type is visible from COM. + return Marshal.IsTypeVisibleFromCom(type); + } + + #endregion + + + #region Private helpers + + [System.Security.SecurityCritical] // auto-generated + private void RegisterValueType(Type type, String strAsmName, String strAsmVersion, String strAsmCodeBase, String strRuntimeVersion) + { + // Retrieve some information that will be used during the registration process. + String strRecordId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + + // Create the HKEY_CLASS_ROOT\Record key. + using (RegistryKey RecordRootKey = Registry.ClassesRoot.CreateSubKey(strRecordRootName)) + { + // Create the HKEY_CLASS_ROOT\Record\<RecordID> key. + using (RegistryKey RecordKey = RecordRootKey.CreateSubKey(strRecordId)) + { + // Create the HKEY_CLASS_ROOT\Record\<RecordId>\<version> key. + using (RegistryKey RecordVersionKey = RecordKey.CreateSubKey(strAsmVersion)) + { + // Set the class value. + RecordVersionKey.SetValue("Class", type.FullName); + + // Set the assembly value. + RecordVersionKey.SetValue("Assembly", strAsmName); + + // Set the runtime version value. + RecordVersionKey.SetValue("RuntimeVersion", strRuntimeVersion); + + // Set the assembly code base value if a code base was specified. + if (strAsmCodeBase != null) + RecordVersionKey.SetValue("CodeBase", strAsmCodeBase); + } + } + } + } + + [System.Security.SecurityCritical] // auto-generated + private void RegisterManagedType(Type type, String strAsmName, String strAsmVersion, String strAsmCodeBase, String strRuntimeVersion) + { + // + // Retrieve some information that will be used during the registration process. + // + + String strDocString = strDocStringPrefix + type.FullName; + String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + String strProgId = GetProgIdForType(type); + + + // + // Write the actual type information in the registry. + // + + if (strProgId != String.Empty) + { + // Create the HKEY_CLASS_ROOT\<wzProgId> key. + using (RegistryKey TypeNameKey = Registry.ClassesRoot.CreateSubKey(strProgId)) + { + TypeNameKey.SetValue("", strDocString); + + // Create the HKEY_CLASS_ROOT\<wzProgId>\CLSID key. + using (RegistryKey ProgIdClsIdKey = TypeNameKey.CreateSubKey("CLSID")) + { + ProgIdClsIdKey.SetValue("", strClsId); + } + } + } + + // Create the HKEY_CLASS_ROOT\CLSID key. + using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.CreateSubKey(strClsIdRootName)) + { + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID> key. + using (RegistryKey ClsIdKey = ClsIdRootKey.CreateSubKey(strClsId)) + { + ClsIdKey.SetValue("", strDocString); + + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32 key. + using (RegistryKey InProcServerKey = ClsIdKey.CreateSubKey("InprocServer32")) + { + InProcServerKey.SetValue("", strMsCorEEFileName); + InProcServerKey.SetValue("ThreadingModel", strManagedTypeThreadingModel); + InProcServerKey.SetValue("Class", type.FullName); + InProcServerKey.SetValue("Assembly", strAsmName); + InProcServerKey.SetValue("RuntimeVersion", strRuntimeVersion); + if (strAsmCodeBase != null) + InProcServerKey.SetValue("CodeBase", strAsmCodeBase); + + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32\<Version> subkey + using (RegistryKey VersionSubKey = InProcServerKey.CreateSubKey(strAsmVersion)) + { + VersionSubKey.SetValue("Class", type.FullName); + VersionSubKey.SetValue("Assembly", strAsmName); + VersionSubKey.SetValue("RuntimeVersion", strRuntimeVersion); + if (strAsmCodeBase != null) + VersionSubKey.SetValue("CodeBase", strAsmCodeBase); + } + + if (strProgId != String.Empty) + { + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\ProdId key. + using (RegistryKey ProgIdKey = ClsIdKey.CreateSubKey("ProgId")) + { + ProgIdKey.SetValue("", strProgId); + } + } + } + + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\Implemented Categories\<Managed Category Guid> key. + using (RegistryKey CategoryKey = ClsIdKey.CreateSubKey(strImplementedCategoriesSubKey)) + { + using (RegistryKey ManagedCategoryKey = CategoryKey.CreateSubKey(strManagedCategoryGuid)) {} + } + } + } + + + // + // Ensure that the managed category exists. + // + + EnsureManagedCategoryExists(); + } + + [System.Security.SecurityCritical] // auto-generated + private void RegisterComImportedType(Type type, String strAsmName, String strAsmVersion, String strAsmCodeBase, String strRuntimeVersion) + { + // Retrieve some information that will be used during the registration process. + String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + + // Create the HKEY_CLASS_ROOT\CLSID key. + using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.CreateSubKey(strClsIdRootName)) + { + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID> key. + using (RegistryKey ClsIdKey = ClsIdRootKey.CreateSubKey(strClsId)) + { + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InProcServer32 key. + using (RegistryKey InProcServerKey = ClsIdKey.CreateSubKey("InprocServer32")) + { + // Set the class value. + InProcServerKey.SetValue("Class", type.FullName); + + // Set the assembly value. + InProcServerKey.SetValue("Assembly", strAsmName); + + // Set the runtime version value. + InProcServerKey.SetValue("RuntimeVersion", strRuntimeVersion); + + // Set the assembly code base value if a code base was specified. + if (strAsmCodeBase != null) + InProcServerKey.SetValue("CodeBase", strAsmCodeBase); + + // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32\<Version> subkey + using (RegistryKey VersionSubKey = InProcServerKey.CreateSubKey(strAsmVersion)) + { + VersionSubKey.SetValue("Class", type.FullName); + VersionSubKey.SetValue("Assembly", strAsmName); + VersionSubKey.SetValue("RuntimeVersion", strRuntimeVersion); + if (strAsmCodeBase != null) + VersionSubKey.SetValue("CodeBase", strAsmCodeBase); + } + } + } + } + } + + [System.Security.SecurityCritical] // auto-generated + private bool UnregisterValueType(Type type, String strAsmVersion) + { + bool bAllVersionsGone = true; + + // Try to open the HKEY_CLASS_ROOT\Record key. + String strRecordId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + + using (RegistryKey RecordRootKey = Registry.ClassesRoot.OpenSubKey(strRecordRootName, true)) + { + if (RecordRootKey != null) + { + // Open the HKEY_CLASS_ROOT\Record\{RecordId} key. + using (RegistryKey RecordKey = RecordRootKey.OpenSubKey(strRecordId,true)) + { + if (RecordKey != null) + { + using (RegistryKey VersionSubKey = RecordKey.OpenSubKey(strAsmVersion,true)) + { + if (VersionSubKey != null) + { + // Delete the values we created. + VersionSubKey.DeleteValue("Assembly",false); + VersionSubKey.DeleteValue("Class",false); + VersionSubKey.DeleteValue("CodeBase",false); + VersionSubKey.DeleteValue("RuntimeVersion",false); + + // delete the version sub key if no value or subkeys under it + if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0)) + RecordKey.DeleteSubKey(strAsmVersion); + } + } + + // If there are sub keys left then there are versions left. + if (RecordKey.SubKeyCount != 0) + bAllVersionsGone = false; + + // If there are no other values or subkeys then we can delete the HKEY_CLASS_ROOT\Record\{RecordId}. + if ((RecordKey.SubKeyCount == 0) && (RecordKey.ValueCount == 0)) + RecordRootKey.DeleteSubKey(strRecordId); + } + } + + // If there are no other values or subkeys then we can delete the HKEY_CLASS_ROOT\Record. + if ((RecordRootKey.SubKeyCount == 0) && (RecordRootKey.ValueCount == 0)) + Registry.ClassesRoot.DeleteSubKey(strRecordRootName); + } + } + + return bAllVersionsGone; + } + + // UnregisterManagedType + // + // Return : + // true: All versions are gone. + // false: Some versions are still left in registry + [System.Security.SecurityCritical] // auto-generated + private bool UnregisterManagedType(Type type,String strAsmVersion) + { + bool bAllVersionsGone = true; + + // + // Create the CLSID string. + // + + String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + String strProgId = GetProgIdForType(type); + + + // + // Remove the entries under HKEY_CLASS_ROOT\CLSID key. + // + + using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.OpenSubKey(strClsIdRootName, true)) + { + if (ClsIdRootKey != null) + { + // + // Remove the entries under HKEY_CLASS_ROOT\CLSID\<CLSID> key. + // + + using (RegistryKey ClsIdKey = ClsIdRootKey.OpenSubKey(strClsId, true)) + { + if (ClsIdKey != null) + { + // + // Remove the entries in the HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32 key. + // + + using (RegistryKey InProcServerKey = ClsIdKey.OpenSubKey("InprocServer32", true)) + { + if (InProcServerKey != null) + { + // + // Remove the entries in HKEY_CLASS_ROOT\CLSID\<CLSID>\InprocServer32\<Version> + // + + using (RegistryKey VersionSubKey = InProcServerKey.OpenSubKey(strAsmVersion, true)) + { + if (VersionSubKey != null) + { + // Delete the values we created + VersionSubKey.DeleteValue("Assembly",false); + VersionSubKey.DeleteValue("Class",false); + VersionSubKey.DeleteValue("RuntimeVersion",false); + VersionSubKey.DeleteValue("CodeBase",false); + + // If there are no other values or subkeys then we can delete the VersionSubKey. + if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0)) + InProcServerKey.DeleteSubKey(strAsmVersion); + } + } + + // If there are sub keys left then there are versions left. + if (InProcServerKey.SubKeyCount != 0) + bAllVersionsGone = false; + + // If there are no versions left, then delete the threading model and default value. + if (bAllVersionsGone) + { + InProcServerKey.DeleteValue("",false); + InProcServerKey.DeleteValue("ThreadingModel",false); + } + + InProcServerKey.DeleteValue("Assembly",false); + InProcServerKey.DeleteValue("Class",false); + InProcServerKey.DeleteValue("RuntimeVersion",false); + InProcServerKey.DeleteValue("CodeBase",false); + + // If there are no other values or subkeys then we can delete the InProcServerKey. + if ((InProcServerKey.SubKeyCount == 0) && (InProcServerKey.ValueCount == 0)) + ClsIdKey.DeleteSubKey("InprocServer32"); + } + } + + // remove HKEY_CLASS_ROOT\CLSID\<CLSID>\ProgId + // and HKEY_CLASS_ROOT\CLSID\<CLSID>\Implemented Category + // only when all versions are removed + if (bAllVersionsGone) + { + // Delete the value we created. + ClsIdKey.DeleteValue("",false); + + if (strProgId != String.Empty) + { + // + // Remove the entries in the HKEY_CLASS_ROOT\CLSID\<CLSID>\ProgId key. + // + + using (RegistryKey ProgIdKey = ClsIdKey.OpenSubKey("ProgId", true)) + { + if (ProgIdKey != null) + { + // Delete the value we created. + ProgIdKey.DeleteValue("",false); + + // If there are no other values or subkeys then we can delete the ProgIdSubKey. + if ((ProgIdKey.SubKeyCount == 0) && (ProgIdKey.ValueCount == 0)) + ClsIdKey.DeleteSubKey("ProgId"); + } + } + } + + + // + // Remove entries in the HKEY_CLASS_ROOT\CLSID\<CLSID>\Implemented Categories\<Managed Category Guid> key. + // + + using (RegistryKey CategoryKey = ClsIdKey.OpenSubKey(strImplementedCategoriesSubKey, true)) + { + if (CategoryKey != null) + { + using (RegistryKey ManagedCategoryKey = CategoryKey.OpenSubKey(strManagedCategoryGuid, true)) + { + if (ManagedCategoryKey != null) + { + // If there are no other values or subkeys then we can delete the ManagedCategoryKey. + if ((ManagedCategoryKey.SubKeyCount == 0) && (ManagedCategoryKey.ValueCount == 0)) + CategoryKey.DeleteSubKey(strManagedCategoryGuid); + } + } + + // If there are no other values or subkeys then we can delete the CategoryKey. + if ((CategoryKey.SubKeyCount == 0) && (CategoryKey.ValueCount == 0)) + ClsIdKey.DeleteSubKey(strImplementedCategoriesSubKey); + } + } + } + + // If there are no other values or subkeys then we can delete the ClsIdKey. + if ((ClsIdKey.SubKeyCount == 0) && (ClsIdKey.ValueCount == 0)) + ClsIdRootKey.DeleteSubKey(strClsId); + } + } + + // If there are no other values or subkeys then we can delete the CLSID key. + if ((ClsIdRootKey.SubKeyCount == 0) && (ClsIdRootKey.ValueCount == 0)) + Registry.ClassesRoot.DeleteSubKey(strClsIdRootName); + } + + + // + // Remove the entries under HKEY_CLASS_ROOT\<wzProgId> key. + // + + if (bAllVersionsGone) + { + if (strProgId != String.Empty) + { + using (RegistryKey TypeNameKey = Registry.ClassesRoot.OpenSubKey(strProgId, true)) + { + if (TypeNameKey != null) + { + // Delete the values we created. + TypeNameKey.DeleteValue("",false); + + + // + // Remove the entries in the HKEY_CLASS_ROOT\<wzProgId>\CLSID key. + // + + using (RegistryKey ProgIdClsIdKey = TypeNameKey.OpenSubKey("CLSID", true)) + { + if (ProgIdClsIdKey != null) + { + // Delete the values we created. + ProgIdClsIdKey.DeleteValue("",false); + + // If there are no other values or subkeys then we can delete the ProgIdClsIdKey. + if ((ProgIdClsIdKey.SubKeyCount == 0) && (ProgIdClsIdKey.ValueCount == 0)) + TypeNameKey.DeleteSubKey("CLSID"); + } + } + + // If there are no other values or subkeys then we can delete the TypeNameKey. + if ((TypeNameKey.SubKeyCount == 0) && (TypeNameKey.ValueCount == 0)) + Registry.ClassesRoot.DeleteSubKey(strProgId); + } + } + } + } + } + + return bAllVersionsGone; + } + + // UnregisterComImportedType + // Return: + // true: All version information are gone. + // false: There are still some version left in registry + [System.Security.SecurityCritical] // auto-generated + private bool UnregisterComImportedType(Type type, String strAsmVersion) + { + bool bAllVersionsGone = true; + + String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + + // Try to open the HKEY_CLASS_ROOT\CLSID key. + using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.OpenSubKey(strClsIdRootName, true)) + { + if (ClsIdRootKey != null) + { + // Try to open the HKEY_CLASS_ROOT\CLSID\<CLSID> key. + using (RegistryKey ClsIdKey = ClsIdRootKey.OpenSubKey(strClsId, true)) + { + if (ClsIdKey != null) + { + // Try to open the HKEY_CLASS_ROOT\CLSID\<CLSID>\InProcServer32 key. + using (RegistryKey InProcServerKey = ClsIdKey.OpenSubKey("InprocServer32", true)) + { + if (InProcServerKey != null) + { + // Delete the values we created. + InProcServerKey.DeleteValue("Assembly",false); + InProcServerKey.DeleteValue("Class",false); + InProcServerKey.DeleteValue("RuntimeVersion",false); + InProcServerKey.DeleteValue("CodeBase",false); + + // Try to open the entries in HKEY_CLASS_ROOT\CLSID\<CLSID>\InProcServer32\<Version> + using (RegistryKey VersionSubKey = InProcServerKey.OpenSubKey(strAsmVersion,true)) + { + if (VersionSubKey != null) + { + // Delete the value we created + VersionSubKey.DeleteValue("Assembly",false); + VersionSubKey.DeleteValue("Class",false); + VersionSubKey.DeleteValue("RuntimeVersion",false); + VersionSubKey.DeleteValue("CodeBase",false); + + // If there are no other values or subkeys then we can delete the VersionSubKey + if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0)) + InProcServerKey.DeleteSubKey(strAsmVersion); + } + } + + // If there are sub keys left then there are versions left. + if (InProcServerKey.SubKeyCount != 0) + bAllVersionsGone = false; + + // If there are no other values or subkeys then we can delete the InProcServerKey. + if ((InProcServerKey.SubKeyCount == 0) && (InProcServerKey.ValueCount == 0)) + ClsIdKey.DeleteSubKey("InprocServer32"); + } + } + + // If there are no other values or subkeys then we can delete the ClsIdKey. + if ((ClsIdKey.SubKeyCount == 0) && (ClsIdKey.ValueCount == 0)) + ClsIdRootKey.DeleteSubKey(strClsId); + } + } + + // If there are no other values or subkeys then we can delete the CLSID key. + if ((ClsIdRootKey.SubKeyCount == 0) && (ClsIdRootKey.ValueCount == 0)) + Registry.ClassesRoot.DeleteSubKey(strClsIdRootName); + } + } + + return bAllVersionsGone; + } + + [System.Security.SecurityCritical] // auto-generated + private void RegisterPrimaryInteropAssembly(RuntimeAssembly assembly, String strAsmCodeBase, PrimaryInteropAssemblyAttribute attr) + { + // Validate that the PIA has a strong name. + if (assembly.GetPublicKey().Length == 0) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_PIAMustBeStrongNamed")); + + String strTlbId = "{" + Marshal.GetTypeLibGuidForAssembly(assembly).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + String strVersion = attr.MajorVersion.ToString("x", CultureInfo.InvariantCulture) + "." + attr.MinorVersion.ToString("x", CultureInfo.InvariantCulture); + + // Create the HKEY_CLASS_ROOT\TypeLib key. + using (RegistryKey TypeLibRootKey = Registry.ClassesRoot.CreateSubKey(strTlbRootName)) + { + // Create the HKEY_CLASS_ROOT\TypeLib\<TLBID> key. + using (RegistryKey TypeLibKey = TypeLibRootKey.CreateSubKey(strTlbId)) + { + // Create the HKEY_CLASS_ROOT\TypeLib\<TLBID>\<Major.Minor> key. + using (RegistryKey VersionSubKey = TypeLibKey.CreateSubKey(strVersion)) + { + // Create the HKEY_CLASS_ROOT\TypeLib\<TLBID>\PrimaryInteropAssembly key. + VersionSubKey.SetValue("PrimaryInteropAssemblyName", assembly.FullName); + if (strAsmCodeBase != null) + VersionSubKey.SetValue("PrimaryInteropAssemblyCodeBase", strAsmCodeBase); + } + } + } + } + + [System.Security.SecurityCritical] // auto-generated + private void UnregisterPrimaryInteropAssembly(Assembly assembly, PrimaryInteropAssemblyAttribute attr) + { + String strTlbId = "{" + Marshal.GetTypeLibGuidForAssembly(assembly).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + String strVersion = attr.MajorVersion.ToString("x", CultureInfo.InvariantCulture) + "." + attr.MinorVersion.ToString("x", CultureInfo.InvariantCulture); + + // Try to open the HKEY_CLASS_ROOT\TypeLib key. + using (RegistryKey TypeLibRootKey = Registry.ClassesRoot.OpenSubKey(strTlbRootName, true)) + { + if (TypeLibRootKey != null) + { + // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID> key. + using (RegistryKey TypeLibKey = TypeLibRootKey.OpenSubKey(strTlbId, true)) + { + if (TypeLibKey != null) + { + // Try to open the HKEY_CLASS_ROOT\TypeLib<TLBID>\<Major.Minor> key. + using (RegistryKey VersionSubKey = TypeLibKey.OpenSubKey(strVersion, true)) + { + if (VersionSubKey != null) + { + // Delete the values we created. + VersionSubKey.DeleteValue("PrimaryInteropAssemblyName",false); + VersionSubKey.DeleteValue("PrimaryInteropAssemblyCodeBase",false); + + // If there are no other values or subkeys then we can delete the VersionKey. + if ((VersionSubKey.SubKeyCount == 0) && (VersionSubKey.ValueCount == 0)) + TypeLibKey.DeleteSubKey(strVersion); + } + } + + // If there are no other values or subkeys then we can delete the TypeLibKey. + if ((TypeLibKey.SubKeyCount == 0) && (TypeLibKey.ValueCount == 0)) + TypeLibRootKey.DeleteSubKey(strTlbId); + } + } + + // If there are no other values or subkeys then we can delete the TypeLib key. + if ((TypeLibRootKey.SubKeyCount == 0) && (TypeLibRootKey.ValueCount == 0)) + Registry.ClassesRoot.DeleteSubKey(strTlbRootName); + } + } + } + + private void EnsureManagedCategoryExists() + { + if (!ManagedCategoryExists()) + { + // Create the HKEY_CLASS_ROOT\Component Category key. + using (RegistryKey ComponentCategoryKey = Registry.ClassesRoot.CreateSubKey(strComponentCategorySubKey)) + { + // Create the HKEY_CLASS_ROOT\Component Category\<Managed Category Guid> key. + using (RegistryKey ManagedCategoryKey = ComponentCategoryKey.CreateSubKey(strManagedCategoryGuid)) + { + ManagedCategoryKey.SetValue("0", strManagedCategoryDescription); + } + } + } + } + + private static bool ManagedCategoryExists() + { + using (RegistryKey componentCategoryKey = Registry.ClassesRoot.OpenSubKey(strComponentCategorySubKey, +#if FEATURE_MACL + RegistryKeyPermissionCheck.ReadSubTree)) +#else + false)) +#endif + { + if (componentCategoryKey == null) + return false; + using (RegistryKey managedCategoryKey = componentCategoryKey.OpenSubKey(strManagedCategoryGuid, +#if FEATURE_MACL + RegistryKeyPermissionCheck.ReadSubTree)) +#else + false)) +#endif + { + if (managedCategoryKey == null) + return false; + object value = managedCategoryKey.GetValue("0"); + if (value == null || value.GetType() != typeof(string)) + return false; + string stringValue = (string)value; + if (stringValue != strManagedCategoryDescription) + return false; + } + } + + return true; + } + + [System.Security.SecurityCritical] // auto-generated + private void CallUserDefinedRegistrationMethod(Type type, bool bRegister) + { + bool bFunctionCalled = false; + + // Retrieve the attribute type to use to determine if a function is the requested user defined + // registration function. + Type RegFuncAttrType = null; + if(bRegister) + RegFuncAttrType = typeof(ComRegisterFunctionAttribute); + else + RegFuncAttrType = typeof(ComUnregisterFunctionAttribute); + + for(Type currType = type; !bFunctionCalled && currType != null; currType = currType.BaseType) + { + // Retrieve all the methods. + MethodInfo[] aMethods = currType.GetMethods(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static); + int NumMethods = aMethods.Length; + + // Go through all the methods and check for the ComRegisterMethod custom attribute. + for(int cMethods = 0;cMethods < NumMethods;cMethods++) + { + MethodInfo CurrentMethod = aMethods[cMethods]; + + // Check to see if the method has the custom attribute. + if(CurrentMethod.GetCustomAttributes(RegFuncAttrType, true).Length != 0) + { + // Check to see if the method is static before we call it. + if(!CurrentMethod.IsStatic) + { + if(bRegister) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NonStaticComRegFunction",CurrentMethod.Name,currType.Name)); + else + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NonStaticComUnRegFunction",CurrentMethod.Name,currType.Name)); + } + + // Finally check that the signature is string ret void. + ParameterInfo[] aParams = CurrentMethod.GetParameters(); + if (CurrentMethod.ReturnType != typeof(void) || + aParams == null || + aParams.Length != 1 || + (aParams[0].ParameterType != typeof(String) && aParams[0].ParameterType != typeof(Type))) + { + if(bRegister) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_InvalidComRegFunctionSig",CurrentMethod.Name,currType.Name)); + else + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_InvalidComUnRegFunctionSig",CurrentMethod.Name,currType.Name)); + } + + // There can only be one register and one unregister function per type. + if(bFunctionCalled) + { + if(bRegister) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MultipleComRegFunctions",currType.Name)); + else + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MultipleComUnRegFunctions",currType.Name)); + } + + // The function is valid so set up the arguments to call it. + Object[] objs = new Object[1]; + if(aParams[0].ParameterType == typeof(String)) + { + // We are dealing with the string overload of the function. + objs[0] = "HKEY_CLASSES_ROOT\\CLSID\\{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + } + else + { + // We are dealing with the type overload of the function. + objs[0] = type; + } + + // Invoke the COM register function. + CurrentMethod.Invoke(null, objs); + + // Mark the function as having been called. + bFunctionCalled = true; + } + } + } + } + + private Type GetBaseComImportType(Type type) + { + for (; type != null && !type.IsImport; type = type.BaseType); + return type; + } + + private bool IsRegisteredAsValueType(Type type) + { + if (!type.IsValueType) + return false; + + return true; + } + + #endregion + + + #region FCalls and DllImports + +#if FEATURE_COMINTEROP_MANAGED_ACTIVATION + // GUID versioning can be controlled by using the GuidAttribute or + // letting the runtime generate it based on type and assembly strong name. + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void RegisterTypeForComClientsNative(Type type,ref Guid g); + + // GUID versioning can be controlled by using the GuidAttribute or + // letting the runtime generate it based on type and assembly strong name. + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern int RegisterTypeForComClientsExNative(Type t, RegistrationClassContext clsContext, RegistrationConnectionType flags); +#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION + + [DllImport(Win32Native.OLE32,CharSet=CharSet.Auto,PreserveSig=false)] + private static extern void CoRevokeClassObject(int cookie); + #endregion + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/RuntimeEnvironment.cs b/src/mscorlib/src/System/Runtime/InteropServices/RuntimeEnvironment.cs new file mode 100644 index 0000000000..d722843ae8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/RuntimeEnvironment.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Runtime information +** +** +=============================================================================*/ + +using System; +using System.Text; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using System.Reflection; +using Microsoft.Win32; +using System.Runtime.Versioning; +using StackCrawlMark = System.Threading.StackCrawlMark; + +namespace System.Runtime.InteropServices { +[System.Runtime.InteropServices.ComVisible(true)] +#if FEATURE_CORECLR + static +#endif + public class RuntimeEnvironment { + +#if !FEATURE_CORECLR + // This should have been a static class, but wasn't as of v3.5. Clearly, this is + // broken. We'll keep this in V4 for binary compat, but marked obsolete as error + // so migrated source code gets fixed. On Silverlight, this type exists but is + // not public. + [Obsolete("Do not create instances of the RuntimeEnvironment class. Call the static methods directly on this type instead", true)] + public RuntimeEnvironment() + { + // Should not have been instantiable - here for binary compatibility in V4. + } +#endif + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern String GetModuleFileName(); + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern String GetDeveloperPath(); + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern String GetHostBindingFile(); + +#if !FEATURE_CORECLR + [System.Security.SecurityCritical] // auto-generated + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern void _GetSystemVersion(StringHandleOnStack retVer); +#endif //!FEATURE_CORECLR + + public static bool FromGlobalAccessCache(Assembly a) + { + return a.GlobalAssemblyCache; + } + +#if !FEATURE_CORECLR + [System.Security.SecuritySafeCritical] // public member +#endif + [MethodImpl (MethodImplOptions.NoInlining)] + public static String GetSystemVersion() + { +#if FEATURE_CORECLR + + return Assembly.GetExecutingAssembly().ImageRuntimeVersion; + +#else // FEATURE_CORECLR + + String ver = null; + _GetSystemVersion(JitHelpers.GetStringHandleOnStack(ref ver)); + return ver; + +#endif // FEATURE_CORECLR + + } + + [System.Security.SecuritySafeCritical] // auto-generated + public static String GetRuntimeDirectory() + { + String dir = GetRuntimeDirectoryImpl(); + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, dir).Demand(); + return dir; + } + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern String GetRuntimeDirectoryImpl(); + + // Returns the system ConfigurationFile + public static String SystemConfigurationFile { + [System.Security.SecuritySafeCritical] // auto-generated + get { + StringBuilder sb = new StringBuilder(Path.MaxPath); + sb.Append(GetRuntimeDirectory()); + sb.Append(AppDomainSetup.RuntimeConfigurationFile); + String path = sb.ToString(); + + // Do security check + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, path).Demand(); + + return path; + } + } + +#if FEATURE_COMINTEROP + [System.Security.SecurityCritical] + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern IntPtr GetRuntimeInterfaceImpl( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid clsid, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); + + // + // This function does the equivalent of calling GetInterface(clsid, riid) on the + // ICLRRuntimeInfo representing this runtime. See MetaHost.idl for a list of + // CLSIDs and IIDs supported by this method. + // + // Returns unmanaged pointer to requested interface on success. Throws + // COMException with failed HR if there is a QI failure. + // + [System.Security.SecurityCritical] // do not allow partial trust callers + [ComVisible(false)] + public static IntPtr GetRuntimeInterfaceAsIntPtr(Guid clsid, Guid riid) + { + return GetRuntimeInterfaceImpl(clsid, riid); + } + + // + // This function does the equivalent of calling GetInterface(clsid, riid) on the + // ICLRRuntimeInfo representing this runtime. See MetaHost.idl for a list of + // CLSIDs and IIDs supported by this method. + // + // Returns an RCW to requested interface on success. Throws + // COMException with failed HR if there is a QI failure. + // + [System.Security.SecurityCritical] // do not allow partial trust callers + [ComVisible(false)] + public static object GetRuntimeInterfaceAsObject(Guid clsid, Guid riid) + { + IntPtr p = IntPtr.Zero; + try { + p = GetRuntimeInterfaceImpl(clsid, riid); + return Marshal.GetObjectForIUnknown(p); + } finally { + if(p != IntPtr.Zero) { + Marshal.Release(p); + } + } + } + +#endif // FEATURE_COMINTEROP + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs b/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs new file mode 100644 index 0000000000..b418d914ed --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Exception class for all Structured Exception Handling code. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + using System.Runtime.InteropServices; + using System; + using System.Runtime.Serialization; + // Exception for Structured Exception Handler exceptions. + // + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class SEHException : ExternalException { + public SEHException() + : base() { + SetErrorCode(__HResults.E_FAIL); + } + + public SEHException(String message) + : base(message) { + SetErrorCode(__HResults.E_FAIL); + } + + public SEHException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.E_FAIL); + } + + protected SEHException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + + // Exceptions can be resumable, meaning a filtered exception + // handler can correct the problem that caused the exception, + // and the code will continue from the point that threw the + // exception. + // + // Resumable exceptions aren't implemented in this version, + // but this method exists and always returns false. + // + public virtual bool CanResume() + { + return false; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs new file mode 100644 index 0000000000..0f4caa21a1 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: This exception is thrown when the runtime rank of a safe array +** is different than the array rank specified in the metadata. +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Runtime.Serialization; + + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class SafeArrayRankMismatchException : SystemException { + public SafeArrayRankMismatchException() + : base(Environment.GetResourceString("Arg_SafeArrayRankMismatchException")) { + SetErrorCode(__HResults.COR_E_SAFEARRAYRANKMISMATCH); + } + + public SafeArrayRankMismatchException(String message) + : base(message) { + SetErrorCode(__HResults.COR_E_SAFEARRAYRANKMISMATCH); + } + + public SafeArrayRankMismatchException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.COR_E_SAFEARRAYRANKMISMATCH); + } + + protected SafeArrayRankMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs new file mode 100644 index 0000000000..a5711c1ade --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** Purpose: This exception is thrown when the runtime type of an array +** is different than the safe array sub type specified in the +** metadata. +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using System.Runtime.Serialization; + + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class SafeArrayTypeMismatchException : SystemException { + public SafeArrayTypeMismatchException() + : base(Environment.GetResourceString("Arg_SafeArrayTypeMismatchException")) { + SetErrorCode(__HResults.COR_E_SAFEARRAYTYPEMISMATCH); + } + + public SafeArrayTypeMismatchException(String message) + : base(message) { + SetErrorCode(__HResults.COR_E_SAFEARRAYTYPEMISMATCH); + } + + public SafeArrayTypeMismatchException(String message, Exception inner) + : base(message, inner) { + SetErrorCode(__HResults.COR_E_SAFEARRAYTYPEMISMATCH); + } + + protected SafeArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs new file mode 100644 index 0000000000..a659daf2b5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs @@ -0,0 +1,415 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Purpose: Unsafe code that uses pointers should use +** SafePointer to fix subtle lifetime problems with the +** underlying resource. +** +===========================================================*/ + +// Design points: +// *) Avoid handle-recycling problems (including ones triggered via +// resurrection attacks) for all accesses via pointers. This requires tying +// together the lifetime of the unmanaged resource with the code that reads +// from that resource, in a package that uses synchronization to enforce +// the correct semantics during finalization. We're using SafeHandle's +// ref count as a gate on whether the pointer can be dereferenced because that +// controls the lifetime of the resource. +// +// *) Keep the penalties for using this class small, both in terms of space +// and time. Having multiple threads reading from a memory mapped file +// will already require 2 additional interlocked operations. If we add in +// a "current position" concept, that requires additional space in memory and +// synchronization. Since the position in memory is often (but not always) +// something that can be stored on the stack, we can save some memory by +// excluding it from this object. However, avoiding the need for +// synchronization is a more significant win. This design allows multiple +// threads to read and write memory simultaneously without locks (as long as +// you don't write to a region of memory that overlaps with what another +// thread is accessing). +// +// *) Space-wise, we use the following memory, including SafeHandle's fields: +// Object Header MT* handle int bool bool <2 pad bytes> length +// On 32 bit platforms: 24 bytes. On 64 bit platforms: 40 bytes. +// (We can safe 4 bytes on x86 only by shrinking SafeHandle) +// +// *) Wrapping a SafeHandle would have been a nice solution, but without an +// ordering between critical finalizable objects, it would have required +// changes to each SafeHandle subclass to opt in to being usable from a +// SafeBuffer (or some clever exposure of SafeHandle's state fields and a +// way of forcing ReleaseHandle to run even after the SafeHandle has been +// finalized with a ref count > 1). We can use less memory and create fewer +// objects by simply inserting a SafeBuffer into the class hierarchy. +// +// *) In an ideal world, we could get marshaling support for SafeBuffer that +// would allow us to annotate a P/Invoke declaration, saying this parameter +// specifies the length of the buffer, and the units of that length are X. +// P/Invoke would then pass that size parameter to SafeBuffer. +// [DllImport(...)] +// static extern SafeMemoryHandle AllocCharBuffer(int numChars); +// If we could put an attribute on the SafeMemoryHandle saying numChars is +// the element length, and it must be multiplied by 2 to get to the byte +// length, we can simplify the usage model for SafeBuffer. +// +// *) This class could benefit from a constraint saying T is a value type +// containing no GC references. + +// Implementation notes: +// *) The Initialize method must be called before you use any instance of +// a SafeBuffer. To avoid race conditions when storing SafeBuffers in statics, +// you either need to take a lock when publishing the SafeBuffer, or you +// need to create a local, initialize the SafeBuffer, then assign to the +// static variable (perhaps using Interlocked.CompareExchange). Of course, +// assignments in a static class constructor are under a lock implicitly. + + +namespace System.Runtime.InteropServices +{ +using System; +using System.Security.Permissions; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; +using Microsoft.Win32.SafeHandles; +using System.Diagnostics.Contracts; + + + [System.Security.SecurityCritical] + public abstract unsafe class SafeBuffer : SafeHandleZeroOrMinusOneIsInvalid + { + // Steal UIntPtr.MaxValue as our uninitialized value. + private static readonly UIntPtr Uninitialized = (UIntPtr.Size == 4) ? + ((UIntPtr) UInt32.MaxValue) : ((UIntPtr) UInt64.MaxValue); + + private UIntPtr _numBytes; + + protected SafeBuffer(bool ownsHandle) : base(ownsHandle) + { + _numBytes = Uninitialized; + } + + /// <summary> + /// Specifies the size of the region of memory, in bytes. Must be + /// called before using the SafeBuffer. + /// </summary> + /// <param name="numBytes">Number of valid bytes in memory.</param> + [CLSCompliant(false)] + public void Initialize(ulong numBytes) + { + if (numBytes < 0) + throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (IntPtr.Size == 4 && numBytes > UInt32.MaxValue) + throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_AddressSpace")); + Contract.EndContractBlock(); + + if (numBytes >= (ulong)Uninitialized) + throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_UIntPtrMax-1")); + + _numBytes = (UIntPtr) numBytes; + } + + /// <summary> + /// Specifies the the size of the region in memory, as the number of + /// elements in an array. Must be called before using the SafeBuffer. + /// </summary> + [CLSCompliant(false)] + public void Initialize(uint numElements, uint sizeOfEachElement) + { + if (numElements < 0) + throw new ArgumentOutOfRangeException("numElements", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (sizeOfEachElement < 0) + throw new ArgumentOutOfRangeException("sizeOfEachElement", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + + if (IntPtr.Size == 4 && numElements * sizeOfEachElement > UInt32.MaxValue) + throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_AddressSpace")); + Contract.EndContractBlock(); + + if (numElements * sizeOfEachElement >= (ulong)Uninitialized) + throw new ArgumentOutOfRangeException("numElements", Environment.GetResourceString("ArgumentOutOfRange_UIntPtrMax-1")); + + _numBytes = checked((UIntPtr) (numElements * sizeOfEachElement)); + } + + /// <summary> + /// Specifies the the size of the region in memory, as the number of + /// elements in an array. Must be called before using the SafeBuffer. + /// </summary> + [CLSCompliant(false)] + public void Initialize<T>(uint numElements) where T : struct + { + Initialize(numElements, Marshal.AlignedSizeOf<T>()); + } + + // Callers should ensure that they check whether the pointer ref param + // is null when AcquirePointer returns. If it is not null, they must + // call ReleasePointer in a CER. This method calls DangerousAddRef + // & exposes the pointer. Unlike Read, it does not alter the "current + // position" of the pointer. Here's how to use it: + // + // byte* pointer = null; + // RuntimeHelpers.PrepareConstrainedRegions(); + // try { + // safeBuffer.AcquirePointer(ref pointer); + // // Use pointer here, with your own bounds checking + // } + // finally { + // if (pointer != null) + // safeBuffer.ReleasePointer(); + // } + // + // Note: If you cast this byte* to a T*, you have to worry about + // whether your pointer is aligned. Additionally, you must take + // responsibility for all bounds checking with this pointer. + /// <summary> + /// Obtain the pointer from a SafeBuffer for a block of code, + /// with the express responsibility for bounds checking and calling + /// ReleasePointer later within a CER to ensure the pointer can be + /// freed later. This method either completes successfully or + /// throws an exception and returns with pointer set to null. + /// </summary> + /// <param name="pointer">A byte*, passed by reference, to receive + /// the pointer from within the SafeBuffer. You must set + /// pointer to null before calling this method.</param> + [CLSCompliant(false)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public void AcquirePointer(ref byte* pointer) + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + } + finally + { + bool junk = false; + DangerousAddRef(ref junk); + pointer = (byte*)handle; + } + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public void ReleasePointer() + { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + DangerousRelease(); + } + + /// <summary> + /// Read a value type from memory at the given offset. This is + /// equivalent to: return *(T*)(bytePtr + byteOffset); + /// </summary> + /// <typeparam name="T">The value type to read</typeparam> + /// <param name="byteOffset">Where to start reading from memory. You + /// may have to consider alignment.</param> + /// <returns>An instance of T read from memory.</returns> + [CLSCompliant(false)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public T Read<T>(ulong byteOffset) where T : struct { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = Marshal.SizeOfType(typeof(T)); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, sizeofT); + + // return *(T*) (_ptr + byteOffset); + T value; + bool mustCallRelease = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + DangerousAddRef(ref mustCallRelease); + + GenericPtrToStructure<T>(ptr, out value, sizeofT); + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + return value; + } + + [CLSCompliant(false)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public void ReadArray<T>(ulong byteOffset, T[] array, int index, int count) + where T : struct + { + if (array == null) + throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); + if (index < 0) + throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (count < 0) + throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (array.Length - index < count) + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); + Contract.EndContractBlock(); + + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = Marshal.SizeOfType(typeof(T)); + uint alignedSizeofT = Marshal.AlignedSizeOf<T>(); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, checked((ulong)(alignedSizeofT * count))); + + bool mustCallRelease = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + DangerousAddRef(ref mustCallRelease); + + for (int i = 0; i < count; i++) + unsafe { GenericPtrToStructure<T>(ptr + alignedSizeofT * i, out array[i + index], sizeofT); } + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + } + + /// <summary> + /// Write a value type to memory at the given offset. This is + /// equivalent to: *(T*)(bytePtr + byteOffset) = value; + /// </summary> + /// <typeparam name="T">The type of the value type to write to memory.</typeparam> + /// <param name="byteOffset">The location in memory to write to. You + /// may have to consider alignment.</param> + /// <param name="value">The value type to write to memory.</param> + [CLSCompliant(false)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public void Write<T>(ulong byteOffset, T value) where T : struct { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = Marshal.SizeOfType(typeof(T)); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, sizeofT); + + // *((T*) (_ptr + byteOffset)) = value; + bool mustCallRelease = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + DangerousAddRef(ref mustCallRelease); + GenericStructureToPtr(ref value, ptr, sizeofT); + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + } + + [CLSCompliant(false)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + public void WriteArray<T>(ulong byteOffset, T[] array, int index, int count) + where T : struct + { + if (array == null) + throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); + if (index < 0) + throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (count < 0) + throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (array.Length - index < count) + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); + Contract.EndContractBlock(); + + if (_numBytes == Uninitialized) + throw NotInitialized(); + + uint sizeofT = Marshal.SizeOfType(typeof(T)); + uint alignedSizeofT = Marshal.AlignedSizeOf<T>(); + byte* ptr = (byte*)handle + byteOffset; + SpaceCheck(ptr, checked((ulong)(alignedSizeofT * count))); + + bool mustCallRelease = false; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + DangerousAddRef(ref mustCallRelease); + for (int i = 0; i < count; i++) + unsafe { GenericStructureToPtr(ref array[i + index], ptr + alignedSizeofT * i, sizeofT); } + } + finally + { + if (mustCallRelease) + DangerousRelease(); + } + } + + + /// <summary> + /// Returns the number of bytes in the memory region. + /// </summary> + [CLSCompliant(false)] + public ulong ByteLength { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get { + if (_numBytes == Uninitialized) + throw NotInitialized(); + + return (ulong) _numBytes; + } + } + + /* No indexer. The perf would be misleadingly bad. People should use + * AcquirePointer and ReleasePointer instead. */ + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private void SpaceCheck(byte* ptr, ulong sizeInBytes) + { + if ((ulong)_numBytes < sizeInBytes) + NotEnoughRoom(); + if ((ulong)(ptr - (byte*) handle) > ((ulong)_numBytes) - sizeInBytes) + NotEnoughRoom(); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static void NotEnoughRoom() + { + throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static InvalidOperationException NotInitialized() + { + Contract.Assert(false, "Uninitialized SafeBuffer! Someone needs to call Initialize before using this instance!"); + return new InvalidOperationException(Environment.GetResourceString("InvalidOperation_MustCallInitialize")); + } + + // FCALL limitations mean we can't have generic FCALL methods. However, we can pass + // TypedReferences to FCALL methods. + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal static void GenericPtrToStructure<T>(byte* ptr, out T structure, uint sizeofT) where T : struct + { + structure = default(T); // Dummy assignment to silence the compiler + PtrToStructureNative(ptr, __makeref(structure), sizeofT); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static extern void PtrToStructureNative(byte* ptr, /*out T*/ TypedReference structure, uint sizeofT); + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal static void GenericStructureToPtr<T>(ref T structure, byte* ptr, uint sizeofT) where T : struct + { + StructureToPtrNative(__makeref(structure), ptr, sizeofT); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static extern void StructureToPtrNative(/*ref T*/ TypedReference structure, byte* ptr, uint sizeofT); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeHandle.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeHandle.cs new file mode 100644 index 0000000000..c26852874d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeHandle.cs @@ -0,0 +1,317 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** A specially designed handle wrapper to ensure we never leak +** an OS handle. The runtime treats this class specially during +** P/Invoke marshaling and finalization. Users should write +** subclasses of SafeHandle for each distinct handle type. +** +** +===========================================================*/ + +namespace System.Runtime.InteropServices { + +using System; +using System.Reflection; +using System.Threading; +using System.Security.Permissions; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.IO; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; + +/* + Problems addressed by the SafeHandle class: + 1) Critical finalization - ensure we never leak OS resources in SQL. Done + without running truly arbitrary & unbounded amounts of managed code. + 2) Reduced graph promotion - during finalization, keep object graph small + 3) GC.KeepAlive behavior - P/Invoke vs. finalizer thread race conditions (HandleRef) + 4) Elimination of security race conditions w/ explicit calls to Close (HandleProtector) + 5) Enforcement of the above via the type system - Don't use IntPtr anymore. + 6) Allows the handle lifetime to be controlled externally via a boolean. + + Subclasses of SafeHandle will implement the ReleaseHandle abstract method + used to execute any code required to free the handle. This method will be + prepared as a constrained execution region at instance construction time + (along with all the methods in its statically determinable call graph). This + implies that we won't get any inconvenient jit allocation errors or rude + thread abort interrupts while releasing the handle but the user must still + write careful code to avoid injecting fault paths of their own (see the CER + spec for more details). In particular, any sub-methods you call should be + decorated with a reliability contract of the appropriate level. In most cases + this should be: + ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success) + Also, any P/Invoke methods should use the SuppressUnmanagedCodeSecurity + attribute to avoid a runtime security check that can also inject failures + (even if the check is guaranteed to pass). + + The GC will run ReleaseHandle methods after any normal finalizers have been + run for objects that were collected at the same time. This ensures classes + like FileStream can run a normal finalizer to flush out existing buffered + data. This is key - it means adding this class to a class like FileStream does + not alter our current semantics w.r.t. finalization today. + + Subclasses must also implement the IsInvalid property so that the + infrastructure can tell when critical finalization is actually required. + Again, this method is prepared ahead of time. It's envisioned that direct + subclasses of SafeHandle will provide an IsInvalid implementation that suits + the general type of handle they support (null is invalid, -1 is invalid etc.) + and then these classes will be further derived for specific safe handle types. + + Most classes using SafeHandle should not provide a finalizer. If they do + need to do so (ie, for flushing out file buffers, needing to write some data + back into memory, etc), then they can provide a finalizer that will be + guaranteed to run before the SafeHandle's critical finalizer. + + Note that SafeHandle's ReleaseHandle is called from a constrained execution + region, and is eagerly prepared before we create your class. This means you + should only call methods with an appropriate reliability contract from your + ReleaseHandle method. + + Subclasses are expected to be written as follows (note that + SuppressUnmanagedCodeSecurity should always be used on any P/Invoke methods + invoked as part of ReleaseHandle, in order to switch the security check from + runtime to jit time and thus remove a possible failure path from the + invocation of the method): + + internal sealed MySafeHandleSubclass : SafeHandle { + // Called by P/Invoke when returning SafeHandles + private MySafeHandleSubclass() : base(IntPtr.Zero, true) + { + } + + // If & only if you need to support user-supplied handles + internal MySafeHandleSubclass(IntPtr preexistingHandle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + SetHandle(preexistingHandle); + } + + // Do not provide a finalizer - SafeHandle's critical finalizer will + // call ReleaseHandle for you. + + public override bool IsInvalid { + get { return handle == IntPtr.Zero; } + } + + override protected bool ReleaseHandle() + { + return MyNativeMethods.CloseHandle(handle); + } + } + + Then elsewhere to create one of these SafeHandles, define a method + with the following type of signature (CreateFile follows this model). + Note that when returning a SafeHandle like this, P/Invoke will call your + class's default constructor. Also, you probably want to define CloseHandle + somewhere, and remember to apply a reliability contract to it. + + [SuppressUnmanagedCodeSecurity] + internal static class MyNativeMethods { + [DllImport("kernel32")] + private static extern MySafeHandleSubclass CreateHandle(int someState); + + [DllImport("kernel32", SetLastError=true), ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private static extern bool CloseHandle(IntPtr handle); + } + + Drawbacks with this implementation: + 1) Requires some magic to run the critical finalizer. + 2) Requires more memory than just an IntPtr. + 3) If you use DangerousAddRef and forget to call DangerousRelease, you can leak a SafeHandle. Use CER's & don't do that. + */ + + +// This class should not be serializable - it's a handle. We require unmanaged +// code permission to subclass SafeHandle to prevent people from writing a +// subclass and suddenly being able to run arbitrary native code with the +// same signature as CloseHandle. This is technically a little redundant, but +// we'll do this to ensure we've cut off all attack vectors. Similarly, all +// methods have a link demand to ensure untrusted code cannot directly edit +// or alter a handle. +[System.Security.SecurityCritical] // auto-generated_required +#if !FEATURE_CORECLR +[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode=true)] +#endif +public abstract class SafeHandle : CriticalFinalizerObject, IDisposable +{ + // ! Do not add or rearrange fields as the EE depends on this layout. + //------------------------------------------------------------------ +#if DEBUG + // FxCop thinks this field is marshaled and so it raises a CA2101 error unless + // we specify this. In practice this is never presented to Win32. + [MarshalAs(UnmanagedType.LPWStr)] + private String _stackTrace; // Where we allocated this SafeHandle. +#endif + protected IntPtr handle; // this must be protected so derived classes can use out params. + private int _state; // Combined ref count and closed/disposed flags (so we can atomically modify them). + private bool _ownsHandle; // Whether we can release this handle. +#pragma warning disable 414 + private bool _fullyInitialized; // Whether constructor completed. +#pragma warning restore 414 + + // Creates a SafeHandle class. Users must then set the Handle property. + // To prevent the SafeHandle from being freed, write a subclass that + // doesn't define a finalizer. + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected SafeHandle(IntPtr invalidHandleValue, bool ownsHandle) + { + handle = invalidHandleValue; + _state = 4; // Ref count 1 and not closed or disposed. + _ownsHandle = ownsHandle; + + if (!ownsHandle) + GC.SuppressFinalize(this); + +#if DEBUG + if (BCLDebug.SafeHandleStackTracesEnabled) + _stackTrace = Environment.GetStackTrace(null, false); + else + _stackTrace = "For a stack trace showing who allocated this SafeHandle, set SafeHandleStackTraces to 1 and rerun your app."; +#endif + + // Set this last to prevent SafeHandle's finalizer from freeing an + // invalid handle. This means we don't have to worry about + // ThreadAbortExceptions interrupting this constructor or the managed + // constructors on subclasses that call this constructor. + _fullyInitialized = true; + } + +#if FEATURE_CORECLR + // Migrating InheritanceDemands requires this default ctor, so we can mark it critical + protected SafeHandle() + { + BCLDebug.Assert(false, "SafeHandle's protected default ctor should never be used!"); + throw new NotImplementedException(); + } +#endif + + [System.Security.SecuritySafeCritical] // auto-generated + ~SafeHandle() + { + Dispose(false); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + extern void InternalFinalize(); + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected void SetHandle(IntPtr handle) { + this.handle = handle; + } + + // This method is necessary for getting an IntPtr out of a SafeHandle. + // Used to tell whether a call to create the handle succeeded by comparing + // the handle against a known invalid value, and for backwards + // compatibility to support the handle properties returning IntPtrs on + // many of our Framework classes. + // Note that this method is dangerous for two reasons: + // 1) If the handle has been marked invalid with SetHandleasInvalid, + // DangerousGetHandle will still return the original handle value. + // 2) The handle returned may be recycled at any point. At best this means + // the handle might stop working suddenly. At worst, if the handle or + // the resource the handle represents is exposed to untrusted code in + // any way, this can lead to a handle recycling security attack (i.e. an + // untrusted caller can query data on the handle you've just returned + // and get back information for an entirely unrelated resource). + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public IntPtr DangerousGetHandle() + { + return handle; + } + + public bool IsClosed { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get { return (_state & 1) == 1; } + } + + public abstract bool IsInvalid { + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + get; + } + + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public void Close() { + Dispose(true); + } + + [System.Security.SecuritySafeCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + public void Dispose() { + Dispose(true); + } + + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected virtual void Dispose(bool disposing) + { + if (disposing) + InternalDispose(); + else + InternalFinalize(); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private extern void InternalDispose(); + + // This should only be called for cases when you know for a fact that + // your handle is invalid and you want to record that information. + // An example is calling a syscall and getting back ERROR_INVALID_HANDLE. + // This method will normally leak handles! + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void SetHandleAsInvalid(); + + // Implement this abstract method in your derived class to specify how to + // free the handle. Be careful not write any code that's subject to faults + // in this method (the runtime will prepare the infrastructure for you so + // that no jit allocations etc. will occur, but don't allocate memory unless + // you can deal with the failure and still free the handle). + // The boolean returned should be true for success and false if the runtime + // should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that + // MDA is enabled. + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected abstract bool ReleaseHandle(); + + // Add a reason why this handle should not be relinquished (i.e. have + // ReleaseHandle called on it). This method has dangerous in the name since + // it must always be used carefully (e.g. called within a CER) to avoid + // leakage of the handle. It returns a boolean indicating whether the + // increment was actually performed to make it easy for program logic to + // back out in failure cases (i.e. is a call to DangerousRelease needed). + // It is passed back via a ref parameter rather than as a direct return so + // that callers need not worry about the atomicity of calling the routine + // and assigning the return value to a variable (the variable should be + // explicitly set to false prior to the call). The only failure cases are + // when the method is interrupted prior to processing by a thread abort or + // when the handle has already been (or is in the process of being) + // released. + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void DangerousAddRef(ref bool success); + + // Partner to DangerousAddRef. This should always be successful when used in + // a correct manner (i.e. matching a successful DangerousAddRef and called + // from a region such as a CER where a thread abort cannot interrupt + // processing). In the same way that unbalanced DangerousAddRef calls can + // cause resource leakage, unbalanced DangerousRelease calls may cause + // invalid handle states to become visible to other threads. This + // constitutes a potential security hole (via handle recycling) as well as a + // correctness problem -- so don't ever expose Dangerous* calls out to + // untrusted code. + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern void DangerousRelease(); +} +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeHeapHandle.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeHeapHandle.cs new file mode 100644 index 0000000000..b0c422d0c0 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeHeapHandle.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// Handle for heap memory that allows tracking of capacity and reallocating. + /// </summary> + [System.Security.SecurityCritical] + internal sealed class SafeHeapHandle : SafeBuffer + { + /// <summary> + /// Allocate a buffer of the given size if requested. + /// </summary> + /// <param name="byteLength">Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit.</param> + /// <exception cref="OutOfMemoryException">Thrown if the requested memory size cannot be allocated.</exception> + /// <exception cref="ArgumentOutOfRangeException">Thrown if size is greater than the maximum memory size.</exception> + public SafeHeapHandle(ulong byteLength) : base(ownsHandle: true) + { + Resize(byteLength); + } + + public override bool IsInvalid + { + [System.Security.SecurityCritical] + get + { return handle == IntPtr.Zero; } + } + + /// <summary> + /// Resize the buffer to the given size if requested. + /// </summary> + /// <param name="byteLength">Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit.</param> + /// <exception cref="OutOfMemoryException">Thrown if the requested memory size cannot be allocated.</exception> + /// <exception cref="ArgumentOutOfRangeException">Thrown if size is greater than the maximum memory size.</exception> + public void Resize(ulong byteLength) + { + if (IsClosed) throw new ObjectDisposedException("SafeHeapHandle"); + + ulong originalLength = 0; + if (handle == IntPtr.Zero) + { + handle = Marshal.AllocHGlobal((IntPtr)byteLength); + } + else + { + originalLength = ByteLength; + + // This may or may not be the same handle, may realloc in place. If the + // handle changes Windows will deal with the old handle, trying to free it will + // cause an error. + handle = Marshal.ReAllocHGlobal(pv: handle, cb: (IntPtr)byteLength); + } + + if (handle == IntPtr.Zero) + { + // Only real plausible answer + throw new OutOfMemoryException(); + } + + if (byteLength > originalLength) + { + // Add pressure + ulong addedBytes = byteLength - originalLength; + if (addedBytes > long.MaxValue) + { + GC.AddMemoryPressure(long.MaxValue); + GC.AddMemoryPressure((long)(addedBytes - long.MaxValue)); + } + else + { + GC.AddMemoryPressure((long)addedBytes); + } + } + else + { + // Shrank or did nothing, release pressure if needed + RemoveMemoryPressure(originalLength - byteLength); + } + + Initialize(byteLength); + } + + private void RemoveMemoryPressure(ulong removedBytes) + { + if (removedBytes == 0) return; + + if (removedBytes > long.MaxValue) + { + GC.RemoveMemoryPressure(long.MaxValue); + GC.RemoveMemoryPressure((long)(removedBytes - long.MaxValue)); + } + else + { + GC.RemoveMemoryPressure((long)removedBytes); + } + } + + [System.Security.SecurityCritical] + protected override bool ReleaseHandle() + { + IntPtr handle = this.handle; + this.handle = IntPtr.Zero; + + if (handle != IntPtr.Zero) + { + RemoveMemoryPressure(ByteLength); + Marshal.FreeHGlobal(handle); + } + + return true; + } + } +}
\ No newline at end of file diff --git a/src/mscorlib/src/System/Runtime/InteropServices/StringBuffer.cs b/src/mscorlib/src/System/Runtime/InteropServices/StringBuffer.cs new file mode 100644 index 0000000000..15b1b6ae8e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/StringBuffer.cs @@ -0,0 +1,402 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// Native buffer that deals in char size increments. Dispose to free memory. Allows buffers larger + /// than a maximum size string to enable working with very large string arrays. Always makes ordinal + /// comparisons. + /// + /// A more performant replacement for StringBuilder when performing native interop. + /// </summary> + /// <remarks> + /// Suggested use through P/Invoke: define DllImport arguments that take a character buffer as IntPtr. + /// NativeStringBuffer has an implicit conversion to IntPtr. + /// </remarks> + internal class StringBuffer : NativeBuffer + { + private uint _length; + + /// <summary> + /// Instantiate the buffer with capacity for at least the specified number of characters. Capacity + /// includes the trailing null character. + /// </summary> + public StringBuffer(uint initialCapacity = 0) + : base(initialCapacity * (ulong)sizeof(char)) + { + } + + /// <summary> + /// Instantiate the buffer with a copy of the specified string. + /// </summary> + public StringBuffer(string initialContents) + : base(0) + { + // We don't pass the count of bytes to the base constructor, appending will + // initialize to the correct size for the specified initial contents. + if (initialContents != null) + { + Append(initialContents); + } + } + + /// <summary> + /// Instantiate the buffer with a copy of the specified StringBuffer. + /// </summary> + public StringBuffer(StringBuffer initialContents) + : base(0) + { + // We don't pass the count of bytes to the base constructor, appending will + // initialize to the correct size for the specified initial contents. + if (initialContents != null) + { + Append(initialContents); + } + } + + /// <summary> + /// Get/set the character at the given index. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to index outside of the buffer length.</exception> + public unsafe char this[uint index] + { + [System.Security.SecuritySafeCritical] + get + { + if (index >= _length) throw new ArgumentOutOfRangeException("index"); + return CharPointer[index]; + } + [System.Security.SecuritySafeCritical] + set + { + if (index >= _length) throw new ArgumentOutOfRangeException("index"); + CharPointer[index] = value; + } + } + + /// <summary> + /// Character capacity of the buffer. Includes the count for the trailing null character. + /// </summary> + public uint CharCapacity + { + [System.Security.SecuritySafeCritical] + get + { + ulong byteCapacity = ByteCapacity; + ulong charCapacity = byteCapacity == 0 ? 0 : byteCapacity / sizeof(char); + return charCapacity > uint.MaxValue ? uint.MaxValue : (uint)charCapacity; + } + } + + /// <summary> + /// Ensure capacity in characters is at least the given minimum. Capacity includes space for the trailing + /// null, which is not part of the Length. + /// </summary> + /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception> + [System.Security.SecuritySafeCritical] + public void EnsureCharCapacity(uint minCapacity) + { + EnsureByteCapacity(minCapacity * (ulong)sizeof(char)); + } + + /// <summary> + /// The logical length of the buffer in characters. (Does not include the final null, which is auto appended.) Will automatically attempt to increase capacity. + /// This is where the usable data ends. + /// </summary> + /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception> + /// <exception cref="ArgumentOutOfRangeException">Thrown if the set size in bytes is uint.MaxValue (as space is implicitly reservced for the trailing null).</exception> + public unsafe uint Length + { + get { return _length; } + [System.Security.SecuritySafeCritical] + set + { + if (value == uint.MaxValue) throw new ArgumentOutOfRangeException("Length"); + + // Null terminate + EnsureCharCapacity(value + 1); + CharPointer[value] = '\0'; + + _length = value; + } + } + + /// <summary> + /// For use when the native api null terminates but doesn't return a length. + /// If no null is found, the length will not be changed. + /// </summary> + [System.Security.SecuritySafeCritical] + public unsafe void SetLengthToFirstNull() + { + char* buffer = CharPointer; + uint capacity = CharCapacity; + for (uint i = 0; i < capacity; i++) + { + if (buffer[i] == '\0') + { + _length = i; + break; + } + } + } + + internal unsafe char* CharPointer + { + [System.Security.SecurityCritical] + get + { + return (char*)VoidPointer; + } + } + + /// <summary> + /// True if the buffer contains the given character. + /// </summary> + [System.Security.SecurityCritical] + public unsafe bool Contains(char value) + { + char* start = CharPointer; + uint length = _length; + + for (uint i = 0; i < length; i++) + { + if (*start++ == value) return true; + } + + return false; + } + + /// <summary> + /// Returns true if the buffer starts with the given string. + /// </summary> + [System.Security.SecuritySafeCritical] + public bool StartsWith(string value) + { + if (value == null) throw new ArgumentNullException("value"); + if (_length < (uint)value.Length) return false; + return SubstringEquals(value, startIndex: 0, count: value.Length); + } + + /// <summary> + /// Returns true if the specified StringBuffer substring equals the given value. + /// </summary> + /// <param name="value">The value to compare against the specified substring.</param> + /// <param name="startIndex">Start index of the sub string.</param> + /// <param name="count">Length of the substring, or -1 to check all remaining.</param> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range + /// of the buffer's length. + /// </exception> + [System.Security.SecuritySafeCritical] + public unsafe bool SubstringEquals(string value, uint startIndex = 0, int count = -1) + { + if (value == null) return false; + if (count < -1) throw new ArgumentOutOfRangeException("count"); + if (startIndex > _length) throw new ArgumentOutOfRangeException("startIndex"); + + uint realCount = count == -1 ? _length - startIndex : (uint)count; + if (checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException("count"); + + int length = value.Length; + + // Check the substring length against the input length + if (realCount != (uint)length) return false; + + fixed (char* valueStart = value) + { + char* bufferStart = CharPointer + startIndex; + for (int i = 0; i < length; i++) + { + // Note that indexing in this case generates faster code than trying to copy the pointer and increment it + if (*bufferStart++ != valueStart[i]) return false; + } + } + + return true; + } + + /// <summary> + /// Append the given string. + /// </summary> + /// <param name="value">The string to append.</param> + /// <param name="startIndex">The index in the input string to start appending from.</param> + /// <param name="count">The count of characters to copy from the input string, or -1 for all remaining.</param> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range + /// of <paramref name="value"/> characters. + /// </exception> + [System.Security.SecuritySafeCritical] + public void Append(string value, int startIndex = 0, int count = -1) + { + CopyFrom( + bufferIndex: _length, + source: value, + sourceIndex: startIndex, + count: count); + } + + /// <summary> + /// Append the given buffer. + /// </summary> + /// <param name="value">The buffer to append.</param> + /// <param name="startIndex">The index in the input buffer to start appending from.</param> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="startIndex"/> is outside the range of <paramref name="value"/> characters. + /// </exception> + public void Append(StringBuffer value, uint startIndex = 0) + { + if (value == null) throw new ArgumentNullException("value"); + if (value.Length == 0) return; + value.CopyTo( + bufferIndex: startIndex, + destination: this, + destinationIndex: _length, + count: value.Length); + } + + /// <summary> + /// Append the given buffer. + /// </summary> + /// <param name="value">The buffer to append.</param> + /// <param name="startIndex">The index in the input buffer to start appending from.</param> + /// <param name="count">The count of characters to copy from the buffer string.</param> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range + /// of <paramref name="value"/> characters. + /// </exception> + public void Append(StringBuffer value, uint startIndex, uint count) + { + if (value == null) throw new ArgumentNullException("value"); + if (count == 0) return; + value.CopyTo( + bufferIndex: startIndex, + destination: this, + destinationIndex: _length, + count: count); + } + + /// <summary> + /// Copy contents to the specified buffer. Destination index must be within current destination length. + /// Will grow the destination buffer if needed. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="bufferIndex"/> or <paramref name="destinationIndex"/> or <paramref name="count"/> are outside the range + /// of <paramref name="value"/> characters. + /// </exception> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="destination"/> is null.</exception> + [System.Security.SecuritySafeCritical] + public unsafe void CopyTo(uint bufferIndex, StringBuffer destination, uint destinationIndex, uint count) + { + if (destination == null) throw new ArgumentNullException("destination"); + if (destinationIndex > destination._length) throw new ArgumentOutOfRangeException("destinationIndex"); + if (bufferIndex >= _length) throw new ArgumentOutOfRangeException("bufferIndex"); + if (_length < checked(bufferIndex + count)) throw new ArgumentOutOfRangeException("count"); + + if (count == 0) return; + uint lastIndex = checked(destinationIndex + count); + if (destination._length < lastIndex) destination.Length = lastIndex; + + Buffer.MemoryCopy( + source: CharPointer + bufferIndex, + destination: destination.CharPointer + destinationIndex, + destinationSizeInBytes: checked((long)(destination.ByteCapacity - (destinationIndex * sizeof(char)))), + sourceBytesToCopy: checked((long)count * sizeof(char))); + } + + /// <summary> + /// Copy contents from the specified string into the buffer at the given index. Start index must be within the current length of + /// the buffer, will grow as necessary. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="bufferIndex"/> or <paramref name="sourceIndex"/> or <paramref name="count"/> are outside the range + /// of <paramref name="value"/> characters. + /// </exception> + /// <exception cref="ArgumentNullException">Thrown if <paramref name="source"/> is null.</exception> + [System.Security.SecuritySafeCritical] + public unsafe void CopyFrom(uint bufferIndex, string source, int sourceIndex = 0, int count = -1) + { + if (source == null) throw new ArgumentNullException("source"); + if (bufferIndex > _length) throw new ArgumentOutOfRangeException("bufferIndex"); + if (sourceIndex < 0 || sourceIndex >= source.Length) throw new ArgumentOutOfRangeException("sourceIndex"); + if (count == -1) count = source.Length - sourceIndex; + if (count < 0 || source.Length - count < sourceIndex) throw new ArgumentOutOfRangeException("count"); + + if (count == 0) return; + uint lastIndex = bufferIndex + (uint)count; + if (_length < lastIndex) Length = lastIndex; + + fixed (char* content = source) + { + Buffer.MemoryCopy( + source: content + sourceIndex, + destination: CharPointer + bufferIndex, + destinationSizeInBytes: checked((long)(ByteCapacity - (bufferIndex * sizeof(char)))), + sourceBytesToCopy: (long)count * sizeof(char)); + } + } + + /// <summary> + /// Trim the specified values from the end of the buffer. If nothing is specified, nothing is trimmed. + /// </summary> + [System.Security.SecuritySafeCritical] + public unsafe void TrimEnd(char[] values) + { + if (values == null || values.Length == 0 || _length == 0) return; + + char* end = CharPointer + _length - 1; + + while (_length > 0 && Array.IndexOf(values, *end) >= 0) + { + Length = _length - 1; + end--; + } + } + + /// <summary> + /// String representation of the entire buffer. If the buffer is larger than the maximum size string (int.MaxValue) this will throw. + /// </summary> + /// <exception cref="InvalidOperationException">Thrown if the buffer is too big to fit into a string.</exception> + [System.Security.SecuritySafeCritical] + public unsafe override string ToString() + { + if (_length == 0) return string.Empty; + if (_length > int.MaxValue) throw new InvalidOperationException(); + return new string(CharPointer, startIndex: 0, length: (int)_length); + } + + /// <summary> + /// Get the given substring in the buffer. + /// </summary> + /// <param name="count">Count of characters to take, or remaining characters from <paramref name="startIndex"/> if -1.</param> + /// <exception cref="ArgumentOutOfRangeException"> + /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range of the buffer's length + /// or count is greater than the maximum string size (int.MaxValue). + /// </exception> + [System.Security.SecuritySafeCritical] + public unsafe string Substring(uint startIndex, int count = -1) + { + if (startIndex > (_length == 0 ? 0 : _length - 1)) throw new ArgumentOutOfRangeException("startIndex"); + if (count < -1) throw new ArgumentOutOfRangeException("count"); + + uint realCount = count == -1 ? _length - startIndex : (uint)count; + if (realCount > int.MaxValue || checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException("count"); + if (realCount == 0) return string.Empty; + + // The buffer could be bigger than will fit into a string, but the substring might fit. As the starting + // index might be bigger than int we need to index ourselves. + return new string(value: CharPointer + startIndex, startIndex: 0, length: (int)realCount); + } + + [System.Security.SecuritySafeCritical] + public override void Free() + { + base.Free(); + _length = 0; + } + } +}
\ No newline at end of file diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventItfInfo.cs b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventItfInfo.cs new file mode 100644 index 0000000000..29b094c9e8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventItfInfo.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.TCEAdapterGen { + + using System; + using System.Reflection; + using System.Collections; + + internal class EventItfInfo + { + public EventItfInfo(String strEventItfName, + String strSrcItfName, + String strEventProviderName, + RuntimeAssembly asmImport, + RuntimeAssembly asmSrcItf) + { + m_strEventItfName = strEventItfName; + m_strSrcItfName = strSrcItfName; + m_strEventProviderName = strEventProviderName; + m_asmImport = asmImport; + m_asmSrcItf = asmSrcItf; + } + + public Type GetEventItfType() + { + Type t = m_asmImport.GetType(m_strEventItfName, true, false); + if (t != null && !t.IsVisible) + t = null; + return t; + } + + public Type GetSrcItfType() + { + Type t = m_asmSrcItf.GetType(m_strSrcItfName, true, false); + if (t != null && !t.IsVisible) + t = null; + return t; + } + + public String GetEventProviderName() + { + return m_strEventProviderName; + } + + private String m_strEventItfName; + private String m_strSrcItfName; + private String m_strEventProviderName; + private RuntimeAssembly m_asmImport; + private RuntimeAssembly m_asmSrcItf; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventProviderWriter.cs b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventProviderWriter.cs new file mode 100644 index 0000000000..58f70e57b7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventProviderWriter.cs @@ -0,0 +1,773 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.TCEAdapterGen { + using System.Runtime.InteropServices.ComTypes; + using ubyte = System.Byte; + using System; + using System.Reflection; + using System.Reflection.Emit; + using System.Collections; + using System.Threading; + using System.Diagnostics.Contracts; + + internal class EventProviderWriter + { + private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + + private readonly Type[] MonitorEnterParamTypes = new Type[] { typeof(Object), Type.GetType("System.Boolean&") }; + + public EventProviderWriter( ModuleBuilder OutputModule, String strDestTypeName, Type EventItfType, Type SrcItfType, Type SinkHelperType ) + { + m_OutputModule = OutputModule; + m_strDestTypeName = strDestTypeName; + m_EventItfType = EventItfType; + m_SrcItfType = SrcItfType; + m_SinkHelperType = SinkHelperType; + } + + public Type Perform() + { + // Create the event provider class. + TypeBuilder OutputTypeBuilder = m_OutputModule.DefineType( + m_strDestTypeName, + TypeAttributes.Sealed | TypeAttributes.NotPublic, + typeof(Object), + new Type[]{m_EventItfType, typeof(IDisposable)} + ); + + // Create the event source field. + FieldBuilder fbCPC = OutputTypeBuilder.DefineField( + "m_ConnectionPointContainer", + typeof(IConnectionPointContainer), + FieldAttributes.Private + ); + + // Create array of event sink helpers. + FieldBuilder fbSinkHelper = OutputTypeBuilder.DefineField( + "m_aEventSinkHelpers", + typeof(ArrayList), + FieldAttributes.Private + ); + + // Define the connection point field. + FieldBuilder fbEventCP = OutputTypeBuilder.DefineField( + "m_ConnectionPoint", + typeof(IConnectionPoint), + FieldAttributes.Private + ); + + // Define the InitXXX method. + MethodBuilder InitSrcItfMethodBuilder = + DefineInitSrcItfMethod( OutputTypeBuilder, m_SrcItfType, fbSinkHelper, fbEventCP, fbCPC ); + + // Process all the methods in the event interface. + MethodInfo[] aMethods = TCEAdapterGenerator.GetNonPropertyMethods(m_SrcItfType); + for ( int cMethods = 0; cMethods < aMethods.Length; cMethods++ ) + { + if ( m_SrcItfType == aMethods[cMethods].DeclaringType ) + { + // Define the add_XXX method. + MethodBuilder AddEventMethodBuilder = DefineAddEventMethod( + OutputTypeBuilder, aMethods[cMethods], m_SinkHelperType, fbSinkHelper, fbEventCP, InitSrcItfMethodBuilder ); + + // Define the remove_XXX method. + MethodBuilder RemoveEventMethodBuilder = DefineRemoveEventMethod( + OutputTypeBuilder, aMethods[cMethods], m_SinkHelperType, fbSinkHelper, fbEventCP ); + } + } + + // Define the constructor. + DefineConstructor( OutputTypeBuilder, fbCPC ); + + // Define the finalize method. + MethodBuilder FinalizeMethod = DefineFinalizeMethod( OutputTypeBuilder, m_SinkHelperType, fbSinkHelper, fbEventCP ); + + // Define the Dispose method. + DefineDisposeMethod( OutputTypeBuilder, FinalizeMethod); + + return OutputTypeBuilder.CreateType(); + } + + private MethodBuilder DefineAddEventMethod( TypeBuilder OutputTypeBuilder, MethodInfo SrcItfMethod, Type SinkHelperClass, FieldBuilder fbSinkHelperArray, FieldBuilder fbEventCP, MethodBuilder mbInitSrcItf ) + { + Type[] aParamTypes; + + // Find the delegate on the event sink helper. + FieldInfo DelegateField = SinkHelperClass.GetField( "m_" + SrcItfMethod.Name + "Delegate" ); + Contract.Assert(DelegateField != null, "Unable to find the field m_" + SrcItfMethod.Name + "Delegate on the sink helper"); + + // Find the cookie on the event sink helper. + FieldInfo CookieField = SinkHelperClass.GetField( "m_dwCookie" ); + Contract.Assert(CookieField != null, "Unable to find the field m_dwCookie on the sink helper"); + + // Retrieve the sink helper's constructor. + ConstructorInfo SinkHelperCons = SinkHelperClass.GetConstructor(EventProviderWriter.DefaultLookup | BindingFlags.NonPublic, null, Array.Empty<Type>(), null ); + Contract.Assert(SinkHelperCons != null, "Unable to find the constructor for the sink helper"); + + // Retrieve the IConnectionPoint.Advise method. + MethodInfo CPAdviseMethod = typeof(IConnectionPoint).GetMethod( "Advise" ); + Contract.Assert(CPAdviseMethod != null, "Unable to find the method ConnectionPoint.Advise"); + + // Retrieve the ArrayList.Add method. + aParamTypes = new Type[1]; + aParamTypes[0] = typeof(Object); + MethodInfo ArrayListAddMethod = typeof(ArrayList).GetMethod( "Add", aParamTypes, null ); + Contract.Assert(ArrayListAddMethod != null, "Unable to find the method ArrayList.Add"); + + // Retrieve the Monitor.Enter() method. + MethodInfo MonitorEnterMethod = typeof(Monitor).GetMethod( "Enter", MonitorEnterParamTypes, null ); + Contract.Assert(MonitorEnterMethod != null, "Unable to find the method Monitor.Enter()"); + + // Retrieve the Monitor.Exit() method. + aParamTypes[0] = typeof(Object); + MethodInfo MonitorExitMethod = typeof(Monitor).GetMethod( "Exit", aParamTypes, null ); + Contract.Assert(MonitorExitMethod != null, "Unable to find the method Monitor.Exit()"); + + // Define the add_XXX method. + Type[] parameterTypes; + parameterTypes = new Type[1]; + parameterTypes[0] = DelegateField.FieldType; + MethodBuilder Meth = OutputTypeBuilder.DefineMethod( + "add_" + SrcItfMethod.Name, + MethodAttributes.Public | MethodAttributes.Virtual, + null, + parameterTypes ); + + ILGenerator il = Meth.GetILGenerator(); + + // Define a label for the m_IFooEventsCP comparision. + Label EventCPNonNullLabel = il.DefineLabel(); + + // Declare the local variables. + LocalBuilder ltSinkHelper = il.DeclareLocal( SinkHelperClass ); + LocalBuilder ltCookie = il.DeclareLocal( typeof(Int32) ); + LocalBuilder ltLockTaken = il.DeclareLocal( typeof(bool) ); + + // Generate the following code: + // try { + il.BeginExceptionBlock(); + + // Generate the following code: + // Monitor.Enter(this, ref lockTaken); + il.Emit(OpCodes.Ldarg, (short)0); + il.Emit(OpCodes.Ldloca_S, ltLockTaken); + il.Emit(OpCodes.Call, MonitorEnterMethod); + + // Generate the following code: + // if ( m_IFooEventsCP != null ) goto EventCPNonNullLabel; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Brtrue, EventCPNonNullLabel ); + + // Generate the following code: + // InitIFooEvents(); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Call, mbInitSrcItf ); + + // Mark this as label to jump to if the CP is not null. + il.MarkLabel( EventCPNonNullLabel ); + + // Generate the following code: + // IFooEvents_SinkHelper SinkHelper = new IFooEvents_SinkHelper; + il.Emit( OpCodes.Newobj, SinkHelperCons ); + il.Emit( OpCodes.Stloc, ltSinkHelper ); + + // Generate the following code: + // dwCookie = 0; + il.Emit( OpCodes.Ldc_I4_0 ); + il.Emit( OpCodes.Stloc, ltCookie ); + + // Generate the following code: + // m_IFooEventsCP.Advise( SinkHelper, dwCookie ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Ldloc, ltSinkHelper ); + il.Emit( OpCodes.Castclass, typeof(Object) ); + il.Emit( OpCodes.Ldloca, ltCookie ); + il.Emit( OpCodes.Callvirt, CPAdviseMethod ); + + // Generate the following code: + // SinkHelper.m_dwCookie = dwCookie; + il.Emit( OpCodes.Ldloc, ltSinkHelper ); + il.Emit( OpCodes.Ldloc, ltCookie ); + il.Emit( OpCodes.Stfld, CookieField ); + + // Generate the following code: + // SinkHelper.m_FooDelegate = d; + il.Emit( OpCodes.Ldloc, ltSinkHelper ); + il.Emit( OpCodes.Ldarg, (short)1 ); + il.Emit( OpCodes.Stfld, DelegateField ); + + // Generate the following code: + // m_aIFooEventsHelpers.Add( SinkHelper ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelperArray ); + il.Emit( OpCodes.Ldloc, ltSinkHelper ); + il.Emit( OpCodes.Castclass, typeof(Object) ); + il.Emit( OpCodes.Callvirt, ArrayListAddMethod ); + il.Emit( OpCodes.Pop ); + + // Generate the following code: + // } finally { + il.BeginFinallyBlock(); + + // Generate the following code: + // if (lockTaken) + // Monitor.Exit(this); + Label skipExit = il.DefineLabel(); + il.Emit( OpCodes.Ldloc, ltLockTaken ); + il.Emit( OpCodes.Brfalse_S, skipExit ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Call, MonitorExitMethod ); + il.MarkLabel(skipExit); + + // Generate the following code: + // } + il.EndExceptionBlock(); + + // Generate the return opcode. + il.Emit( OpCodes.Ret ); + + return Meth; + } + + private MethodBuilder DefineRemoveEventMethod( TypeBuilder OutputTypeBuilder, MethodInfo SrcItfMethod, Type SinkHelperClass, FieldBuilder fbSinkHelperArray, FieldBuilder fbEventCP ) + { + Type[] aParamTypes; + + // Find the delegate on the event sink helper. + FieldInfo DelegateField = SinkHelperClass.GetField( "m_" + SrcItfMethod.Name + "Delegate" ); + Contract.Assert(DelegateField != null, "Unable to find the field m_" + SrcItfMethod.Name + "Delegate on the sink helper"); + + // Find the cookie on the event sink helper. + FieldInfo CookieField = SinkHelperClass.GetField( "m_dwCookie" ); + Contract.Assert(CookieField != null, "Unable to find the field m_dwCookie on the sink helper"); + + // Retrieve the ArrayList.RemoveAt method. + aParamTypes = new Type[1]; + aParamTypes[0] = typeof(Int32); + MethodInfo ArrayListRemoveMethod = typeof(ArrayList).GetMethod( "RemoveAt", aParamTypes, null ); + Contract.Assert(ArrayListRemoveMethod != null, "Unable to find the method ArrayList.RemoveAt()"); + + // Retrieve the ArrayList.Item property get method. + PropertyInfo ArrayListItemProperty = typeof(ArrayList).GetProperty( "Item" ); + Contract.Assert(ArrayListItemProperty != null, "Unable to find the property ArrayList.Item"); + MethodInfo ArrayListItemGetMethod = ArrayListItemProperty.GetGetMethod(); + Contract.Assert(ArrayListItemGetMethod != null, "Unable to find the get method for property ArrayList.Item"); + + // Retrieve the ArrayList.Count property get method. + PropertyInfo ArrayListSizeProperty = typeof(ArrayList).GetProperty( "Count" ); + Contract.Assert(ArrayListSizeProperty != null, "Unable to find the property ArrayList.Count"); + MethodInfo ArrayListSizeGetMethod = ArrayListSizeProperty.GetGetMethod(); + Contract.Assert(ArrayListSizeGetMethod != null, "Unable to find the get method for property ArrayList.Count"); + + // Retrieve the Delegate.Equals() method. + aParamTypes[0] = typeof(Delegate); + MethodInfo DelegateEqualsMethod = typeof(Delegate).GetMethod( "Equals", aParamTypes, null ); + Contract.Assert(DelegateEqualsMethod != null, "Unable to find the method Delegate.Equlals()"); + + // Retrieve the Monitor.Enter() method. + MethodInfo MonitorEnterMethod = typeof(Monitor).GetMethod("Enter", MonitorEnterParamTypes, null); + Contract.Assert(MonitorEnterMethod != null, "Unable to find the method Monitor.Enter()"); + + // Retrieve the Monitor.Exit() method. + aParamTypes[0] = typeof(Object); + MethodInfo MonitorExitMethod = typeof(Monitor).GetMethod( "Exit", aParamTypes, null ); + Contract.Assert(MonitorExitMethod != null, "Unable to find the method Monitor.Exit()"); + + // Retrieve the ConnectionPoint.Unadvise() method. + MethodInfo CPUnadviseMethod = typeof(IConnectionPoint).GetMethod( "Unadvise" ); + Contract.Assert(CPUnadviseMethod != null, "Unable to find the method ConnectionPoint.Unadvise()"); + + // Retrieve the Marshal.ReleaseComObject() method. + MethodInfo ReleaseComObjectMethod = typeof(Marshal).GetMethod( "ReleaseComObject" ); + Contract.Assert(ReleaseComObjectMethod != null, "Unable to find the method Marshal.ReleaseComObject()"); + + // Define the remove_XXX method. + Type[] parameterTypes; + parameterTypes = new Type[1]; + parameterTypes[0] = DelegateField.FieldType; + MethodBuilder Meth = OutputTypeBuilder.DefineMethod( + "remove_" + SrcItfMethod.Name, + MethodAttributes.Public | MethodAttributes.Virtual, + null, + parameterTypes ); + + ILGenerator il = Meth.GetILGenerator(); + + // Declare the local variables. + LocalBuilder ltNumSinkHelpers = il.DeclareLocal( typeof(Int32) ); + LocalBuilder ltSinkHelperCounter = il.DeclareLocal( typeof(Int32) ); + LocalBuilder ltCurrSinkHelper = il.DeclareLocal( SinkHelperClass ); + LocalBuilder ltLockTaken = il.DeclareLocal(typeof(bool)); + + // Generate the labels for the for loop. + Label ForBeginLabel = il.DefineLabel(); + Label ForEndLabel = il.DefineLabel(); + Label FalseIfLabel = il.DefineLabel(); + Label MonitorExitLabel = il.DefineLabel(); + + // Generate the following code: + // try { + il.BeginExceptionBlock(); + + // Generate the following code: + // Monitor.Enter(this, ref lockTaken); + il.Emit(OpCodes.Ldarg, (short)0); + il.Emit(OpCodes.Ldloca_S, ltLockTaken); + il.Emit(OpCodes.Call, MonitorEnterMethod); + + // Generate the following code: + // if ( m_aIFooEventsHelpers == null ) goto ForEndLabel; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelperArray ); + il.Emit( OpCodes.Brfalse, ForEndLabel ); + + // Generate the following code: + // int NumEventHelpers = m_aIFooEventsHelpers.Count; + // int cEventHelpers = 0; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelperArray ); + il.Emit( OpCodes.Callvirt, ArrayListSizeGetMethod ); + il.Emit( OpCodes.Stloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Ldc_I4, 0 ); + il.Emit( OpCodes.Stloc, ltSinkHelperCounter ); + + // Generate the following code: + // if ( 0 >= NumEventHelpers ) goto ForEndLabel; + il.Emit( OpCodes.Ldc_I4, 0 ); + il.Emit( OpCodes.Ldloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Bge, ForEndLabel ); + + // Mark this as the beginning of the for loop's body. + il.MarkLabel( ForBeginLabel ); + + // Generate the following code: + // CurrentHelper = (IFooEvents_SinkHelper)m_aIFooEventsHelpers.Get( cEventHelpers ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelperArray ); + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Callvirt, ArrayListItemGetMethod ); + il.Emit( OpCodes.Castclass, SinkHelperClass ); + il.Emit( OpCodes.Stloc, ltCurrSinkHelper ); + + // Generate the following code: + // if ( CurrentHelper.m_FooDelegate ) + il.Emit( OpCodes.Ldloc, ltCurrSinkHelper ); + il.Emit( OpCodes.Ldfld, DelegateField ); + il.Emit( OpCodes.Ldnull ); + il.Emit( OpCodes.Beq, FalseIfLabel ); + + // Generate the following code: + // if ( CurrentHelper.m_FooDelegate.Equals( d ) ) + il.Emit( OpCodes.Ldloc, ltCurrSinkHelper ); + il.Emit( OpCodes.Ldfld, DelegateField ); + il.Emit( OpCodes.Ldarg, (short)1 ); + il.Emit( OpCodes.Castclass, typeof(Object) ); + il.Emit( OpCodes.Callvirt, DelegateEqualsMethod ); + il.Emit( OpCodes.Ldc_I4, 0xff ); + il.Emit( OpCodes.And ); + il.Emit( OpCodes.Ldc_I4, 0 ); + il.Emit( OpCodes.Beq, FalseIfLabel ); + + // Generate the following code: + // m_aIFooEventsHelpers.RemoveAt( cEventHelpers ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelperArray ); + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Callvirt, ArrayListRemoveMethod ); + + // Generate the following code: + // m_IFooEventsCP.Unadvise( CurrentHelper.m_dwCookie ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Ldloc, ltCurrSinkHelper ); + il.Emit( OpCodes.Ldfld, CookieField ); + il.Emit( OpCodes.Callvirt, CPUnadviseMethod ); + + // Generate the following code: + // if ( NumEventHelpers > 1) break; + il.Emit( OpCodes.Ldloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Ldc_I4, 1 ); + il.Emit( OpCodes.Bgt, ForEndLabel ); + + // Generate the following code: + // Marshal.ReleaseComObject(m_IFooEventsCP); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Call, ReleaseComObjectMethod ); + il.Emit( OpCodes.Pop ); + + // Generate the following code: + // m_IFooEventsCP = null; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldnull ); + il.Emit( OpCodes.Stfld, fbEventCP ); + + // Generate the following code: + // m_aIFooEventsHelpers = null; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldnull ); + il.Emit( OpCodes.Stfld, fbSinkHelperArray ); + + // Generate the following code: + // break; + il.Emit( OpCodes.Br, ForEndLabel ); + + // Mark this as the label to jump to when the if statement is false. + il.MarkLabel( FalseIfLabel ); + + // Generate the following code: + // cEventHelpers++; + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Ldc_I4, 1 ); + il.Emit( OpCodes.Add ); + il.Emit( OpCodes.Stloc, ltSinkHelperCounter ); + + // Generate the following code: + // if ( cEventHelpers < NumEventHelpers ) goto ForBeginLabel; + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Ldloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Blt, ForBeginLabel ); + + // Mark this as the end of the for loop's body. + il.MarkLabel( ForEndLabel ); + + // Generate the following code: + // } finally { + il.BeginFinallyBlock(); + + // Generate the following code: + // if (lockTaken) + // Monitor.Exit(this); + Label skipExit = il.DefineLabel(); + il.Emit(OpCodes.Ldloc, ltLockTaken); + il.Emit(OpCodes.Brfalse_S, skipExit); + il.Emit(OpCodes.Ldarg, (short)0); + il.Emit(OpCodes.Call, MonitorExitMethod); + il.MarkLabel(skipExit); + + // Generate the following code: + // } + il.EndExceptionBlock(); + + // Generate the return opcode. + il.Emit( OpCodes.Ret ); + + return Meth; + } + + private MethodBuilder DefineInitSrcItfMethod( TypeBuilder OutputTypeBuilder, Type SourceInterface, FieldBuilder fbSinkHelperArray, FieldBuilder fbEventCP, FieldBuilder fbCPC ) + { + // Retrieve the constructor info for the array list's default constructor. + ConstructorInfo DefaultArrayListCons = typeof(ArrayList).GetConstructor(EventProviderWriter.DefaultLookup, null, Array.Empty<Type>(), null ); + Contract.Assert(DefaultArrayListCons != null, "Unable to find the constructor for class ArrayList"); + + // Temp byte array for Guid + ubyte[] rgByteGuid = new ubyte[16]; + + // Retrieve the constructor info for the Guid constructor. + Type[] aParamTypes = new Type[1]; + aParamTypes[0] = typeof(Byte[]); + ConstructorInfo ByteArrayGUIDCons = typeof(Guid).GetConstructor(EventProviderWriter.DefaultLookup, null, aParamTypes, null ); + Contract.Assert(ByteArrayGUIDCons != null, "Unable to find the constructor for GUID that accepts a string as argument"); + + // Retrieve the IConnectionPointContainer.FindConnectionPoint() method. + MethodInfo CPCFindCPMethod = typeof(IConnectionPointContainer).GetMethod( "FindConnectionPoint" ); + Contract.Assert(CPCFindCPMethod != null, "Unable to find the method ConnectionPointContainer.FindConnectionPoint()"); + + // Define the Init method itself. + MethodBuilder Meth = OutputTypeBuilder.DefineMethod( + "Init", + MethodAttributes.Private, + null, + null ); + + ILGenerator il = Meth.GetILGenerator(); + + // Declare the local variables. + LocalBuilder ltCP = il.DeclareLocal( typeof(IConnectionPoint) ); + LocalBuilder ltEvGuid = il.DeclareLocal( typeof(Guid) ); + LocalBuilder ltByteArrayGuid = il.DeclareLocal( typeof(Byte[]) ); + + // Generate the following code: + // IConnectionPoint CP = NULL; + il.Emit( OpCodes.Ldnull ); + il.Emit( OpCodes.Stloc, ltCP ); + + // Get unsigned byte array for the GUID of the event interface. + rgByteGuid = SourceInterface.GUID.ToByteArray(); + + // Generate the following code: + // ubyte rgByteArray[] = new ubyte [16]; + il.Emit( OpCodes.Ldc_I4, 0x10 ); + il.Emit( OpCodes.Newarr, typeof(Byte) ); + il.Emit( OpCodes.Stloc, ltByteArrayGuid ); + + // Generate the following code: + // rgByteArray[i] = rgByteGuid[i]; + for (int i = 0; i < 16; i++ ) + { + il.Emit( OpCodes.Ldloc, ltByteArrayGuid ); + il.Emit( OpCodes.Ldc_I4, i ); + il.Emit( OpCodes.Ldc_I4, (int) (rgByteGuid[i]) ); + il.Emit( OpCodes.Stelem_I1); + } + + // Generate the following code: + // EventItfGuid = Guid( ubyte b[] ); + il.Emit( OpCodes.Ldloca, ltEvGuid ); + il.Emit( OpCodes.Ldloc, ltByteArrayGuid ); + il.Emit( OpCodes.Call, ByteArrayGUIDCons ); + + // Generate the following code: + // m_ConnectionPointContainer.FindConnectionPoint( EventItfGuid, CP ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbCPC ); + il.Emit( OpCodes.Ldloca, ltEvGuid ); + il.Emit( OpCodes.Ldloca, ltCP ); + il.Emit( OpCodes.Callvirt, CPCFindCPMethod ); + + // Generate the following code: + // m_ConnectionPoint = (IConnectionPoint)CP; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldloc, ltCP ); + il.Emit( OpCodes.Castclass, typeof(IConnectionPoint) ); + il.Emit( OpCodes.Stfld, fbEventCP ); + + // Generate the following code: + // m_aEventSinkHelpers = new ArrayList; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Newobj, DefaultArrayListCons ); + il.Emit( OpCodes.Stfld, fbSinkHelperArray ); + + // Generate the return opcode. + il.Emit( OpCodes.Ret ); + + return Meth; + } + + private void DefineConstructor( TypeBuilder OutputTypeBuilder, FieldBuilder fbCPC ) + { + // Retrieve the constructor info for the base class's constructor. + ConstructorInfo DefaultBaseClsCons = typeof(Object).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, Array.Empty<Type>(), null ); + Contract.Assert(DefaultBaseClsCons != null, "Unable to find the object's public default constructor"); + + // Define the default constructor. + MethodAttributes ctorAttributes = MethodAttributes.SpecialName | (DefaultBaseClsCons.Attributes & MethodAttributes.MemberAccessMask); + MethodBuilder Cons = OutputTypeBuilder.DefineMethod( + ".ctor", + ctorAttributes, + null, + new Type[]{typeof(Object)} ); + + ILGenerator il = Cons.GetILGenerator(); + + // Generate the call to the base class constructor. + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Call, DefaultBaseClsCons ); + + // Generate the following code: + // m_ConnectionPointContainer = (IConnectionPointContainer)EventSource; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldarg, (short)1 ); + il.Emit( OpCodes.Castclass, typeof(IConnectionPointContainer) ); + il.Emit( OpCodes.Stfld, fbCPC ); + + // Generate the return opcode. + il.Emit( OpCodes.Ret ); + } + + private MethodBuilder DefineFinalizeMethod( TypeBuilder OutputTypeBuilder, Type SinkHelperClass, FieldBuilder fbSinkHelper, FieldBuilder fbEventCP ) + { + // Find the cookie on the event sink helper. + FieldInfo CookieField = SinkHelperClass.GetField( "m_dwCookie" ); + Contract.Assert(CookieField != null, "Unable to find the field m_dwCookie on the sink helper"); + + // Retrieve the ArrayList.Item property get method. + PropertyInfo ArrayListItemProperty = typeof(ArrayList).GetProperty( "Item" ); + Contract.Assert(ArrayListItemProperty != null, "Unable to find the property ArrayList.Item"); + MethodInfo ArrayListItemGetMethod = ArrayListItemProperty.GetGetMethod(); + Contract.Assert(ArrayListItemGetMethod != null, "Unable to find the get method for property ArrayList.Item"); + + // Retrieve the ArrayList.Count property get method. + PropertyInfo ArrayListSizeProperty = typeof(ArrayList).GetProperty( "Count" ); + Contract.Assert(ArrayListSizeProperty != null, "Unable to find the property ArrayList.Count"); + MethodInfo ArrayListSizeGetMethod = ArrayListSizeProperty.GetGetMethod(); + Contract.Assert(ArrayListSizeGetMethod != null, "Unable to find the get method for property ArrayList.Count"); + + // Retrieve the ConnectionPoint.Unadvise() method. + MethodInfo CPUnadviseMethod = typeof(IConnectionPoint).GetMethod( "Unadvise" ); + Contract.Assert(CPUnadviseMethod != null, "Unable to find the method ConnectionPoint.Unadvise()"); + + // Retrieve the Marshal.ReleaseComObject() method. + MethodInfo ReleaseComObjectMethod = typeof(Marshal).GetMethod( "ReleaseComObject" ); + Contract.Assert(ReleaseComObjectMethod != null, "Unable to find the method Marshal.ReleaseComObject()"); + + // Retrieve the Monitor.Enter() method. + MethodInfo MonitorEnterMethod = typeof(Monitor).GetMethod("Enter", MonitorEnterParamTypes, null); + Contract.Assert(MonitorEnterMethod != null, "Unable to find the method Monitor.Enter()"); + + // Retrieve the Monitor.Exit() method. + Type[] aParamTypes = new Type[1]; + aParamTypes[0] = typeof(Object); + MethodInfo MonitorExitMethod = typeof(Monitor).GetMethod( "Exit", aParamTypes, null ); + Contract.Assert(MonitorExitMethod != null, "Unable to find the method Monitor.Exit()"); + + // Define the Finalize method itself. + MethodBuilder Meth = OutputTypeBuilder.DefineMethod( "Finalize", MethodAttributes.Public | MethodAttributes.Virtual, null, null ); + + ILGenerator il = Meth.GetILGenerator(); + + // Declare the local variables. + LocalBuilder ltNumSinkHelpers = il.DeclareLocal( typeof(Int32) ); + LocalBuilder ltSinkHelperCounter = il.DeclareLocal( typeof(Int32) ); + LocalBuilder ltCurrSinkHelper = il.DeclareLocal( SinkHelperClass ); + LocalBuilder ltLockTaken = il.DeclareLocal(typeof(bool)); + + // Generate the following code: + // try { + il.BeginExceptionBlock(); + + // Generate the following code: + // Monitor.Enter(this, ref lockTaken); + il.Emit(OpCodes.Ldarg, (short)0); + il.Emit(OpCodes.Ldloca_S, ltLockTaken); + il.Emit(OpCodes.Call, MonitorEnterMethod); + + // Generate the labels. + Label ForBeginLabel = il.DefineLabel(); + Label ReleaseComObjectLabel = il.DefineLabel(); + Label AfterReleaseComObjectLabel = il.DefineLabel(); + + // Generate the following code: + // if ( m_IFooEventsCP == null ) goto AfterReleaseComObjectLabel; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Brfalse, AfterReleaseComObjectLabel ); + + // Generate the following code: + // int NumEventHelpers = m_aIFooEventsHelpers.Count; + // int cEventHelpers = 0; + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelper ); + il.Emit( OpCodes.Callvirt, ArrayListSizeGetMethod ); + il.Emit( OpCodes.Stloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Ldc_I4, 0 ); + il.Emit( OpCodes.Stloc, ltSinkHelperCounter ); + + // Generate the following code: + // if ( 0 >= NumEventHelpers ) goto ReleaseComObjectLabel; + il.Emit( OpCodes.Ldc_I4, 0 ); + il.Emit( OpCodes.Ldloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Bge, ReleaseComObjectLabel ); + + // Mark this as the beginning of the for loop's body. + il.MarkLabel( ForBeginLabel ); + + // Generate the following code: + // CurrentHelper = (IFooEvents_SinkHelper)m_aIFooEventsHelpers.Get( cEventHelpers ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbSinkHelper ); + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Callvirt, ArrayListItemGetMethod ); + il.Emit( OpCodes.Castclass, SinkHelperClass ); + il.Emit( OpCodes.Stloc, ltCurrSinkHelper ); + + // Generate the following code: + // m_IFooEventsCP.Unadvise( CurrentHelper.m_dwCookie ); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Ldloc, ltCurrSinkHelper ); + il.Emit( OpCodes.Ldfld, CookieField ); + il.Emit( OpCodes.Callvirt, CPUnadviseMethod ); + + // Generate the following code: + // cEventHelpers++; + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Ldc_I4, 1 ); + il.Emit( OpCodes.Add ); + il.Emit( OpCodes.Stloc, ltSinkHelperCounter ); + + // Generate the following code: + // if ( cEventHelpers < NumEventHelpers ) goto ForBeginLabel; + il.Emit( OpCodes.Ldloc, ltSinkHelperCounter ); + il.Emit( OpCodes.Ldloc, ltNumSinkHelpers ); + il.Emit( OpCodes.Blt, ForBeginLabel ); + + // Mark this as the end of the for loop's body. + il.MarkLabel( ReleaseComObjectLabel ); + + // Generate the following code: + // Marshal.ReleaseComObject(m_IFooEventsCP); + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbEventCP ); + il.Emit( OpCodes.Call, ReleaseComObjectMethod ); + il.Emit( OpCodes.Pop ); + + // Mark this as the end of the for loop's body. + il.MarkLabel( AfterReleaseComObjectLabel ); + + // Generate the following code: + // } catch { + il.BeginCatchBlock(typeof(System.Exception)); + il.Emit( OpCodes.Pop ); + + // Generate the following code: + // } finally { + il.BeginFinallyBlock(); + + // Generate the following code: + // if (lockTaken) + // Monitor.Exit(this); + Label skipExit = il.DefineLabel(); + il.Emit(OpCodes.Ldloc, ltLockTaken); + il.Emit(OpCodes.Brfalse_S, skipExit); + il.Emit(OpCodes.Ldarg, (short)0); + il.Emit(OpCodes.Call, MonitorExitMethod); + il.MarkLabel(skipExit); + + // Generate the following code: + // } + il.EndExceptionBlock(); + + // Generate the return opcode. + il.Emit( OpCodes.Ret ); + + return Meth; + } + + private void DefineDisposeMethod( TypeBuilder OutputTypeBuilder, MethodBuilder FinalizeMethod ) + { + // Retrieve the method info for GC.SuppressFinalize(). + MethodInfo SuppressFinalizeMethod = typeof(GC).GetMethod("SuppressFinalize"); + Contract.Assert(SuppressFinalizeMethod != null, "Unable to find the GC.SuppressFinalize"); + + // Define the Finalize method itself. + MethodBuilder Meth = OutputTypeBuilder.DefineMethod( "Dispose", MethodAttributes.Public | MethodAttributes.Virtual, null, null ); + + ILGenerator il = Meth.GetILGenerator(); + + // Generate the following code: + // Finalize() + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Callvirt, FinalizeMethod ); + + // Generate the following code: + // GC.SuppressFinalize() + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Call, SuppressFinalizeMethod ); + + // Generate the return opcode. + il.Emit( OpCodes.Ret ); + } + + private ModuleBuilder m_OutputModule; + private String m_strDestTypeName; + private Type m_EventItfType; + private Type m_SrcItfType; + private Type m_SinkHelperType; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventSinkHelperWriter.cs b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventSinkHelperWriter.cs new file mode 100644 index 0000000000..0367e79bdd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/EventSinkHelperWriter.cs @@ -0,0 +1,297 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.TCEAdapterGen { + using System.Runtime.InteropServices; + using System; + using System.Reflection; + using System.Reflection.Emit; + using System.Collections; + using System.Diagnostics.Contracts; + internal class EventSinkHelperWriter + { + public static readonly String GeneratedTypeNamePostfix = "_SinkHelper"; + + public EventSinkHelperWriter( ModuleBuilder OutputModule, Type InputType, Type EventItfType ) + { + m_InputType = InputType; + m_OutputModule = OutputModule; + m_EventItfType = EventItfType; + } + + public Type Perform() + { + // Create the output Type. + Type[] aInterfaces = new Type[1]; + aInterfaces[0] = m_InputType; + String strFullName = null; + String strNameSpace = NameSpaceExtractor.ExtractNameSpace( m_EventItfType.FullName ); + + if (strNameSpace != "") + strFullName = strNameSpace + "."; + + strFullName += m_InputType.Name + GeneratedTypeNamePostfix; + TypeBuilder OutputTypeBuilder = TCEAdapterGenerator.DefineUniqueType( + strFullName, + TypeAttributes.Sealed | TypeAttributes.Public, + null, + aInterfaces, + m_OutputModule + ); + // Hide the _SinkProvider interface + TCEAdapterGenerator.SetHiddenAttribute(OutputTypeBuilder); + + // Set the class interface to none. + TCEAdapterGenerator.SetClassInterfaceTypeToNone(OutputTypeBuilder); + + // Retrieve the property methods on the input interface and give them a dummy implementation. + MethodInfo[] pMethods = TCEAdapterGenerator.GetPropertyMethods(m_InputType); + foreach (MethodInfo method in pMethods) + { + DefineBlankMethod(OutputTypeBuilder, method); + } + + // Retrieve the non-property methods on the input interface. + MethodInfo[] aMethods = TCEAdapterGenerator.GetNonPropertyMethods(m_InputType); + + // Allocate an array to contain the delegate fields. + FieldBuilder[] afbDelegates = new FieldBuilder[aMethods.Length]; + // Process all the methods on the input interface. + for ( int cMethods = 0; cMethods < aMethods.Length; cMethods++ ) + { + if ( m_InputType == aMethods[cMethods].DeclaringType ) + { + // Retrieve the delegate type from the add_XXX method. + MethodInfo AddMeth = m_EventItfType.GetMethod( "add_" + aMethods[cMethods].Name ); + ParameterInfo[] aParams = AddMeth.GetParameters(); + Contract.Assert(aParams.Length == 1, "All event interface methods must take a single delegate derived type and have a void return type"); + Type DelegateCls = aParams[0].ParameterType; + + // Define the delegate instance field. + afbDelegates[cMethods] = OutputTypeBuilder.DefineField( + "m_" + aMethods[cMethods].Name + "Delegate", + DelegateCls, + FieldAttributes.Public + ); + + // Define the event method itself. + DefineEventMethod( OutputTypeBuilder, aMethods[cMethods], DelegateCls, afbDelegates[cMethods] ); + } + } + + // Create the cookie field. + FieldBuilder fbCookie = OutputTypeBuilder.DefineField( + "m_dwCookie", + typeof(Int32), + FieldAttributes.Public + ); + + // Define the constructor. + DefineConstructor( OutputTypeBuilder, fbCookie, afbDelegates ); + + return OutputTypeBuilder.CreateType(); + } + + private void DefineBlankMethod(TypeBuilder OutputTypeBuilder, MethodInfo Method) + { + ParameterInfo[] PIs = Method.GetParameters(); + Type[] parameters = new Type[PIs.Length]; + for (int i=0; i < PIs.Length; i++) + { + parameters[i] = PIs[i].ParameterType; + } + + MethodBuilder Meth = OutputTypeBuilder.DefineMethod(Method.Name, + Method.Attributes & ~MethodAttributes.Abstract, + Method.CallingConvention, + Method.ReturnType, + parameters); + + ILGenerator il = Meth.GetILGenerator(); + + AddReturn(Method.ReturnType, il, Meth); + + il.Emit(OpCodes.Ret); + } + + private void DefineEventMethod( TypeBuilder OutputTypeBuilder, MethodInfo Method, Type DelegateCls, FieldBuilder fbDelegate ) + { + // Retrieve the method info for the invoke method on the delegate. + MethodInfo DelegateInvokeMethod = DelegateCls.GetMethod( "Invoke" ); + Contract.Assert(DelegateInvokeMethod != null, "Unable to find method Delegate.Invoke()"); + + // Retrieve the return type. + Type ReturnType = Method.ReturnType; + + // Define the actual event method. + ParameterInfo[] paramInfos = Method.GetParameters(); + Type[] parameterTypes; + if (paramInfos != null) + { + parameterTypes = new Type[paramInfos.Length]; + for (int i = 0; i < paramInfos.Length; i++) + { + parameterTypes[i] = paramInfos[i].ParameterType; + } + } + else + parameterTypes = null; + + MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Virtual; + MethodBuilder Meth = OutputTypeBuilder.DefineMethod( Method.Name, + attr, + CallingConventions.Standard, + ReturnType, + parameterTypes); + + // We explicitly do not specify parameter name and attributes since this Type + // is not meant to be exposed to the user. It is only used internally to do the + // connection point to TCE mapping. + + ILGenerator il = Meth.GetILGenerator(); + + // Create the exit branch. + Label ExitLabel = il.DefineLabel(); + + // Generate the code that verifies that the delegate is not null. + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbDelegate ); + il.Emit( OpCodes.Brfalse, ExitLabel ); + + // The delegate is not NULL so we need to invoke it. + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldfld, fbDelegate ); + + // Generate the code to load the arguments before we call invoke. + ParameterInfo[] aParams = Method.GetParameters(); + for ( int cParams = 0; cParams < aParams.Length; cParams++ ) + { + il.Emit( OpCodes.Ldarg, (short)(cParams + 1) ); + } + + // Generate a tail call to invoke. This will cause the callvirt to return + // directly to the caller of the current method instead of actually coming + // back to the current method and returning. This will cause the value returned + // from the call to the COM server to be returned to the caller of this method. + + il.Emit( OpCodes.Callvirt, DelegateInvokeMethod ); + il.Emit( OpCodes.Ret ); + + // This is the label that will be jumped to if no delegate is present. + il.MarkLabel( ExitLabel ); + + AddReturn(ReturnType, il, Meth); + + il.Emit( OpCodes.Ret ); + + } + + private void AddReturn(Type ReturnType, ILGenerator il, MethodBuilder Meth) + { + // Place a dummy return value on the stack before we return. + if ( ReturnType == typeof(void) ) + { + // There is nothing to place on the stack. + } + else if ( ReturnType.IsPrimitive ) + { + switch (System.Type.GetTypeCode(ReturnType)) + { + case TypeCode.Boolean: + case TypeCode.Char: + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + il.Emit( OpCodes.Ldc_I4_0 ); + break; + + case TypeCode.Int64: + case TypeCode.UInt64: + il.Emit( OpCodes.Ldc_I4_0 ); + il.Emit( OpCodes.Conv_I8 ); + break; + + case TypeCode.Single: + il.Emit( OpCodes.Ldc_R4, 0 ); + break; + + case TypeCode.Double: + il.Emit( OpCodes.Ldc_R4, 0 ); + il.Emit( OpCodes.Conv_R8 ); + break; + + default: + // "TypeCode" does not include IntPtr, so special case it. + if ( ReturnType == typeof(IntPtr) ) + il.Emit( OpCodes.Ldc_I4_0 ); + else + Contract.Assert(false, "Unexpected type for Primitive type."); + break; + } + } + else if ( ReturnType.IsValueType ) + { + // Allocate stack space for the return value type. Zero-init. + Meth.InitLocals = true; + LocalBuilder ltRetVal = il.DeclareLocal( ReturnType ); + + // Load the value class on the stack. + il.Emit( OpCodes.Ldloc_S, ltRetVal ); + + } + else + { + // The return type is a normal type. + il.Emit( OpCodes.Ldnull ); + } + } + + private void DefineConstructor( TypeBuilder OutputTypeBuilder, FieldBuilder fbCookie, FieldBuilder[] afbDelegates ) + { + // Retrieve the constructor info for the base classe's constructor. + ConstructorInfo DefaultBaseClsCons = typeof(Object).GetConstructor(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, null, Array.Empty<Type>(), null ); + Contract.Assert(DefaultBaseClsCons != null, "Unable to find the constructor for class " + m_InputType.Name); + + // Define the default constructor. + MethodBuilder Cons = OutputTypeBuilder.DefineMethod( ".ctor", + MethodAttributes.Assembly | MethodAttributes.SpecialName, + CallingConventions.Standard, + null, + null); + + ILGenerator il = Cons.GetILGenerator(); + + // Generate the code to call the constructor of the base class. + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Call, DefaultBaseClsCons ); + + // Generate the code to set the cookie field to 0. + il.Emit( OpCodes.Ldarg, (short)0 ); + il.Emit( OpCodes.Ldc_I4, 0 ); + il.Emit( OpCodes.Stfld, fbCookie ); + + // Generate the code to set all the delegates to NULL. + for ( int cDelegates = 0; cDelegates < afbDelegates.Length; cDelegates++ ) + { + if (afbDelegates[cDelegates] != null) + { + il.Emit( OpCodes.Ldarg,(short)0 ); + il.Emit( OpCodes.Ldnull ); + il.Emit( OpCodes.Stfld, afbDelegates[cDelegates] ); + } + } + + // Emit the return opcode. + il.Emit( OpCodes.Ret ); + + } + + private Type m_InputType; + private Type m_EventItfType; + private ModuleBuilder m_OutputModule; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/NameSpaceExtractor.cs b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/NameSpaceExtractor.cs new file mode 100644 index 0000000000..8018ad4c66 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/NameSpaceExtractor.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.TCEAdapterGen { + + using System; + internal static class NameSpaceExtractor + { + private static char NameSpaceSeperator = '.'; + + public static String ExtractNameSpace(String FullyQualifiedTypeName) + { + int TypeNameStartPos = FullyQualifiedTypeName.LastIndexOf(NameSpaceSeperator); + if (TypeNameStartPos == -1) + return ""; + else + return FullyQualifiedTypeName.Substring(0, TypeNameStartPos); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/TCEAdapterGenerator.cs b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/TCEAdapterGenerator.cs new file mode 100644 index 0000000000..c6e4415246 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TCEAdapterGen/TCEAdapterGenerator.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.TCEAdapterGen { + using System.Runtime.InteropServices; + using System; + using System.Reflection; + using System.Reflection.Emit; + using System.Collections; + using System.Threading; + + internal class TCEAdapterGenerator + { + public void Process(ModuleBuilder ModBldr, ArrayList EventItfList) + { + // Store the input/output module. + m_Module = ModBldr; + + // Generate the TCE adapters for all the event sources. + int NumEvItfs = EventItfList.Count; + for ( int cEventItfs = 0; cEventItfs < NumEvItfs; cEventItfs++ ) + { + // Retrieve the event interface info. + EventItfInfo CurrEventItf = (EventItfInfo)EventItfList[cEventItfs]; + + // Retrieve the information from the event interface info. + Type EventItfType = CurrEventItf.GetEventItfType(); + Type SrcItfType = CurrEventItf.GetSrcItfType(); + String EventProviderName = CurrEventItf.GetEventProviderName(); + + // Generate the sink interface helper. + Type SinkHelperType = new EventSinkHelperWriter( m_Module, SrcItfType, EventItfType ).Perform(); + + // Generate the event provider. + new EventProviderWriter( m_Module, EventProviderName, EventItfType, SrcItfType, SinkHelperType ).Perform(); + } + } + + internal static void SetClassInterfaceTypeToNone(TypeBuilder tb) + { + // Create the ClassInterface(ClassInterfaceType.None) CA builder if we haven't created it yet. + if (s_NoClassItfCABuilder == null) + { + Type []aConsParams = new Type[1]; + aConsParams[0] = typeof(ClassInterfaceType); + ConstructorInfo Cons = typeof(ClassInterfaceAttribute).GetConstructor(aConsParams); + + Object[] aArgs = new Object[1]; + aArgs[0] = ClassInterfaceType.None; + s_NoClassItfCABuilder = new CustomAttributeBuilder(Cons, aArgs); + } + + // Set the class interface type to none. + tb.SetCustomAttribute(s_NoClassItfCABuilder); + } + + internal static TypeBuilder DefineUniqueType(String strInitFullName, TypeAttributes attrs, Type BaseType, Type[] aInterfaceTypes, ModuleBuilder mb) + { + String strFullName = strInitFullName; + int PostFix = 2; + + // Find the first unique name for the type. + for (; mb.GetType(strFullName) != null; strFullName = strInitFullName + "_" + PostFix, PostFix++); + + // Define a type with the determined unique name. + return mb.DefineType(strFullName, attrs, BaseType, aInterfaceTypes); + } + + internal static void SetHiddenAttribute(TypeBuilder tb) + { + if (s_HiddenCABuilder == null) + { + // Hide the type from Object Browsers + Type []aConsParams = new Type[1]; + aConsParams[0] = typeof(TypeLibTypeFlags); + ConstructorInfo Cons = typeof(TypeLibTypeAttribute).GetConstructor(aConsParams); + + Object []aArgs = new Object[1]; + aArgs[0] = TypeLibTypeFlags.FHidden; + s_HiddenCABuilder = new CustomAttributeBuilder(Cons, aArgs); + } + + tb.SetCustomAttribute(s_HiddenCABuilder); + } + + internal static MethodInfo[] GetNonPropertyMethods(Type type) + { + MethodInfo[] aMethods = type.GetMethods(); + ArrayList methods = new ArrayList(aMethods); + + PropertyInfo[] props = type.GetProperties(); + + foreach(PropertyInfo prop in props) + { + MethodInfo[] accessors = prop.GetAccessors(); + foreach (MethodInfo accessor in accessors) + { + for (int i=0; i < methods.Count; i++) + { + if ((MethodInfo)methods[i] == accessor) + methods.RemoveAt(i); + } + } + } + + MethodInfo[] retMethods = new MethodInfo[methods.Count]; + methods.CopyTo(retMethods); + + return retMethods; + } + + internal static MethodInfo[] GetPropertyMethods(Type type) + { + MethodInfo[] aMethods = type.GetMethods(); + ArrayList methods = new ArrayList(); + + PropertyInfo[] props = type.GetProperties(); + + foreach(PropertyInfo prop in props) + { + MethodInfo[] accessors = prop.GetAccessors(); + foreach (MethodInfo accessor in accessors) + { + methods.Add(accessor); + } + } + + MethodInfo[] retMethods = new MethodInfo[methods.Count]; + methods.CopyTo(retMethods); + + return retMethods; + } + + + private ModuleBuilder m_Module = null; + private Hashtable m_SrcItfToSrcItfInfoMap = new Hashtable(); + private static volatile CustomAttributeBuilder s_NoClassItfCABuilder = null; + private static volatile CustomAttributeBuilder s_HiddenCABuilder = null; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs b/src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs new file mode 100644 index 0000000000..e6b148a0a5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/TypeLibConverter.cs @@ -0,0 +1,595 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Component that implements the ITypeLibConverter interface and +** does the actual work of converting a typelib to metadata and +** vice versa. +** +** +=============================================================================*/ +#if !FEATURE_CORECLR // current implementation requires reflection only load +namespace System.Runtime.InteropServices { + + using System; + using System.Diagnostics.Contracts; + using System.Collections; + using System.Collections.Generic; + using System.Threading; + using System.Runtime.InteropServices.TCEAdapterGen; + using System.IO; + using System.Reflection; + using System.Reflection.Emit; + using System.Configuration.Assemblies; + using Microsoft.Win32; + using System.Runtime.CompilerServices; + using System.Globalization; + using System.Security; + using System.Security.Permissions; + using System.Runtime.InteropServices.ComTypes; + using System.Runtime.Versioning; + using WORD = System.UInt16; + using DWORD = System.UInt32; + using _TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; + + [Guid("F1C3BF79-C3E4-11d3-88E7-00902754C43A")] + [ClassInterface(ClassInterfaceType.None)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class TypeLibConverter : ITypeLibConverter + { + private const String s_strTypeLibAssemblyTitlePrefix = "TypeLib "; + private const String s_strTypeLibAssemblyDescPrefix = "Assembly generated from typelib "; + private const int MAX_NAMESPACE_LENGTH = 1024; + + + // + // ITypeLibConverter interface. + // + + [System.Security.SecuritySafeCritical] // auto-generated + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib, + String asmFileName, + int flags, + ITypeLibImporterNotifySink notifySink, + byte[] publicKey, + StrongNameKeyPair keyPair, + bool unsafeInterfaces) + { + return ConvertTypeLibToAssembly(typeLib, + asmFileName, + (unsafeInterfaces + ? TypeLibImporterFlags.UnsafeInterfaces + : 0), + notifySink, + publicKey, + keyPair, + null, + null); + } + + + + + [System.Security.SecuritySafeCritical] // auto-generated + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + public AssemblyBuilder ConvertTypeLibToAssembly([MarshalAs(UnmanagedType.Interface)] Object typeLib, + String asmFileName, + TypeLibImporterFlags flags, + ITypeLibImporterNotifySink notifySink, + byte[] publicKey, + StrongNameKeyPair keyPair, + String asmNamespace, + Version asmVersion) + { + // Validate the arguments. + if (typeLib == null) + throw new ArgumentNullException("typeLib"); + if (asmFileName == null) + throw new ArgumentNullException("asmFileName"); + if (notifySink == null) + throw new ArgumentNullException("notifySink"); + if (String.Empty.Equals(asmFileName)) + throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileName"), "asmFileName"); + if (asmFileName.Length > Path.MaxPath) + throw new ArgumentException(Environment.GetResourceString("IO.PathTooLong"), asmFileName); + if ((flags & TypeLibImporterFlags.PrimaryInteropAssembly) != 0 && publicKey == null && keyPair == null) + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_PIAMustBeStrongNamed")); + Contract.EndContractBlock(); + + ArrayList eventItfInfoList = null; + + // Determine the AssemblyNameFlags + AssemblyNameFlags asmNameFlags = AssemblyNameFlags.None; + + // Retrieve the assembly name from the typelib. + AssemblyName asmName = GetAssemblyNameFromTypelib(typeLib, asmFileName, publicKey, keyPair, asmVersion, asmNameFlags); + + // Create the dynamic assembly that will contain the converted typelib types. + AssemblyBuilder asmBldr = CreateAssemblyForTypeLib(typeLib, asmFileName, asmName, + (flags & TypeLibImporterFlags.PrimaryInteropAssembly) != 0, + (flags & TypeLibImporterFlags.ReflectionOnlyLoading) != 0, + (flags & TypeLibImporterFlags.NoDefineVersionResource) != 0); + + // Define a dynamic module that will contain the contain the imported types. + String strNonQualifiedAsmFileName = Path.GetFileName(asmFileName); + ModuleBuilder modBldr = asmBldr.DefineDynamicModule(strNonQualifiedAsmFileName, strNonQualifiedAsmFileName); + + // If the namespace hasn't been specified, then use the assembly name. + if (asmNamespace == null) + asmNamespace = asmName.Name; + + // Create a type resolve handler that will also intercept resolve ref messages + // on the sink interface to build up a list of referenced assemblies. + TypeResolveHandler typeResolveHandler = new TypeResolveHandler(modBldr, notifySink); + + // Add a listener for the type resolve events. + AppDomain currentDomain = Thread.GetDomain(); + ResolveEventHandler resolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveEvent); + ResolveEventHandler asmResolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveAsmEvent); + ResolveEventHandler ROAsmResolveHandler = new ResolveEventHandler(typeResolveHandler.ResolveROAsmEvent); + currentDomain.TypeResolve += resolveHandler; + currentDomain.AssemblyResolve += asmResolveHandler; + currentDomain.ReflectionOnlyAssemblyResolve += ROAsmResolveHandler; + + // Convert the types contained in the typelib into metadata and add them to the assembly. + nConvertTypeLibToMetadata(typeLib, asmBldr.InternalAssembly, modBldr.InternalModule, asmNamespace, flags, typeResolveHandler, out eventItfInfoList); + + // Update the COM types in the assembly. + UpdateComTypesInAssembly(asmBldr, modBldr); + + // If there are any event sources then generate the TCE adapters. + if (eventItfInfoList.Count > 0) + new TCEAdapterGenerator().Process(modBldr, eventItfInfoList); + + // Remove the listener for the type resolve events. + currentDomain.TypeResolve -= resolveHandler; + currentDomain.AssemblyResolve -= asmResolveHandler; + currentDomain.ReflectionOnlyAssemblyResolve -= ROAsmResolveHandler; + + // We have finished converting the typelib and now have a fully formed assembly. + return asmBldr; + } + + [System.Security.SecuritySafeCritical] // auto-generated + [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] + [return : MarshalAs(UnmanagedType.Interface)] + public Object ConvertAssemblyToTypeLib(Assembly assembly, String strTypeLibName, TypeLibExporterFlags flags, ITypeLibExporterNotifySink notifySink) + { + RuntimeAssembly rtAssembly; + AssemblyBuilder ab = assembly as AssemblyBuilder; + if (ab != null) + rtAssembly = ab.InternalAssembly; + else + rtAssembly = assembly as RuntimeAssembly; + + return nConvertAssemblyToTypeLib(rtAssembly, strTypeLibName, flags, notifySink); + } + + public bool GetPrimaryInteropAssembly(Guid g, Int32 major, Int32 minor, Int32 lcid, out String asmName, out String asmCodeBase) + { + String strTlbId = "{" + g.ToString().ToUpper(CultureInfo.InvariantCulture) + "}"; + String strVersion = major.ToString("x", CultureInfo.InvariantCulture) + "." + minor.ToString("x", CultureInfo.InvariantCulture); + + // Set the two out values to null before we start. + asmName = null; + asmCodeBase = null; + + // Try to open the HKEY_CLASS_ROOT\TypeLib key. + using (RegistryKey TypeLibKey = Registry.ClassesRoot.OpenSubKey("TypeLib", false)) + { + if (TypeLibKey != null) + { + // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID> key. + using (RegistryKey TypeLibSubKey = TypeLibKey.OpenSubKey(strTlbId)) + { + if (TypeLibSubKey != null) + { + // Try to open the HKEY_CLASS_ROOT\TypeLib\<TLBID>\<Major.Minor> key. + using (RegistryKey VersionKey = TypeLibSubKey.OpenSubKey(strVersion, false)) + { + if (VersionKey != null) + { + // Attempt to retrieve the assembly name and codebase under the version key. + asmName = (String)VersionKey.GetValue("PrimaryInteropAssemblyName"); + asmCodeBase = (String)VersionKey.GetValue("PrimaryInteropAssemblyCodeBase"); + } + } + } + } + } + } + + // If the assembly name isn't null, then we found an PIA. + return asmName != null; + } + + + // + // Non native helper methods. + // + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable + private static AssemblyBuilder CreateAssemblyForTypeLib(Object typeLib, String asmFileName, AssemblyName asmName, bool bPrimaryInteropAssembly, bool bReflectionOnly, bool bNoDefineVersionResource) + { + // Retrieve the current app domain. + AppDomain currentDomain = Thread.GetDomain(); + + // Retrieve the directory from the assembly file name. + String dir = null; + if (asmFileName != null) + { + dir = Path.GetDirectoryName(asmFileName); + if (String.IsNullOrEmpty(dir)) + dir = null; + } + + AssemblyBuilderAccess aba; + if (bReflectionOnly) + { + aba = AssemblyBuilderAccess.ReflectionOnly; + } + else + { + aba = AssemblyBuilderAccess.RunAndSave; + } + + // Create the dynamic assembly itself. + AssemblyBuilder asmBldr; + + List<CustomAttributeBuilder> assemblyAttributes = new List<CustomAttributeBuilder>(); +#if !FEATURE_CORECLR + // mscorlib.dll must specify the security rules that assemblies it emits are to use, since by + // default all assemblies will follow security rule set level 2, and we want to make that an + // explicit decision. + ConstructorInfo securityRulesCtor = typeof(SecurityRulesAttribute).GetConstructor(new Type[] { typeof(SecurityRuleSet) }); + CustomAttributeBuilder securityRulesAttribute = + new CustomAttributeBuilder(securityRulesCtor, new object[] { SecurityRuleSet.Level2 }); + assemblyAttributes.Add(securityRulesAttribute); +#endif // !FEATURE_CORECLR + + asmBldr = currentDomain.DefineDynamicAssembly(asmName, aba, dir, false, assemblyAttributes); + + // Set the Guid custom attribute on the assembly. + SetGuidAttributeOnAssembly(asmBldr, typeLib); + + // Set the imported from COM attribute on the assembly and return it. + SetImportedFromTypeLibAttrOnAssembly(asmBldr, typeLib); + + // Set the version information on the typelib. + if (bNoDefineVersionResource) + { + SetTypeLibVersionAttribute(asmBldr, typeLib); + } + else + { + SetVersionInformation(asmBldr, typeLib, asmName); + } + + // If we are generating a PIA, then set the PIA custom attribute. + if (bPrimaryInteropAssembly) + SetPIAAttributeOnAssembly(asmBldr, typeLib); + + return asmBldr; + } + + [System.Security.SecurityCritical] // auto-generated + internal static AssemblyName GetAssemblyNameFromTypelib(Object typeLib, String asmFileName, byte[] publicKey, StrongNameKeyPair keyPair, Version asmVersion, AssemblyNameFlags asmNameFlags) + { + // Extract the name of the typelib. + String strTypeLibName = null; + String strDocString = null; + int dwHelpContext = 0; + String strHelpFile = null; + ITypeLib pTLB = (ITypeLib)typeLib; + pTLB.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile); + + // Retrieve the name to use for the assembly. + if (asmFileName == null) + { + asmFileName = strTypeLibName; + } + else + { + Contract.Assert((asmFileName != null) && (asmFileName.Length > 0), "The assembly file name cannot be an empty string!"); + + String strFileNameNoPath = Path.GetFileName(asmFileName); + String strExtension = Path.GetExtension(asmFileName); + + // Validate that the extension is valid. + bool bExtensionValid = ".dll".Equals(strExtension, StringComparison.OrdinalIgnoreCase); + + // If the extension is not valid then tell the user and quit. + if (!bExtensionValid) + throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileExtension")); + + // The assembly cannot contain the path nor the extension. + asmFileName = strFileNameNoPath.Substring(0, strFileNameNoPath.Length - ".dll".Length); + } + + // If the version information was not specified, then retrieve it from the typelib. + if (asmVersion == null) + { + int major; + int minor; + Marshal.GetTypeLibVersion(pTLB, out major, out minor); + asmVersion = new Version(major, minor, 0, 0); + } + + // Create the assembly name for the imported typelib's assembly. + AssemblyName AsmName = new AssemblyName(); + AsmName.Init( + asmFileName, + publicKey, + null, + asmVersion, + null, + AssemblyHashAlgorithm.None, + AssemblyVersionCompatibility.SameMachine, + null, + asmNameFlags, + keyPair); + + return AsmName; + } + + private static void UpdateComTypesInAssembly(AssemblyBuilder asmBldr, ModuleBuilder modBldr) + { + // Retrieve the AssemblyBuilderData associated with the assembly builder. + AssemblyBuilderData AsmBldrData = asmBldr.m_assemblyData; + + // Go through the types in the module and add them as public COM types. + Type[] aTypes = modBldr.GetTypes(); + int NumTypes = aTypes.Length; + for (int cTypes = 0; cTypes < NumTypes; cTypes++) + AsmBldrData.AddPublicComType(aTypes[cTypes]); + } + + + [System.Security.SecurityCritical] // auto-generated + private static void SetGuidAttributeOnAssembly(AssemblyBuilder asmBldr, Object typeLib) + { + // Retrieve the GuidAttribute constructor. + Type []aConsParams = new Type[1] {typeof(String)}; + ConstructorInfo GuidAttrCons = typeof(GuidAttribute).GetConstructor(aConsParams); + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[1] {Marshal.GetTypeLibGuid((ITypeLib)typeLib).ToString()}; + CustomAttributeBuilder GuidCABuilder = new CustomAttributeBuilder(GuidAttrCons, aArgs); + + // Set the GuidAttribute on the assembly builder. + asmBldr.SetCustomAttribute(GuidCABuilder); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetImportedFromTypeLibAttrOnAssembly(AssemblyBuilder asmBldr, Object typeLib) + { + // Retrieve the ImportedFromTypeLibAttribute constructor. + Type []aConsParams = new Type[1] {typeof(String)}; + ConstructorInfo ImpFromComAttrCons = typeof(ImportedFromTypeLibAttribute).GetConstructor(aConsParams); + + // Retrieve the name of the typelib. + String strTypeLibName = Marshal.GetTypeLibName((ITypeLib)typeLib); + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[1] {strTypeLibName}; + CustomAttributeBuilder ImpFromComCABuilder = new CustomAttributeBuilder(ImpFromComAttrCons, aArgs); + + // Set the ImportedFromTypeLibAttribute on the assembly builder. + asmBldr.SetCustomAttribute(ImpFromComCABuilder); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetTypeLibVersionAttribute(AssemblyBuilder asmBldr, Object typeLib) + { + Type []aConsParams = new Type[2] {typeof(int), typeof(int)}; + ConstructorInfo TypeLibVerCons = typeof(TypeLibVersionAttribute).GetConstructor(aConsParams); + + // Get the typelib version + int major; + int minor; + Marshal.GetTypeLibVersion((ITypeLib)typeLib, out major, out minor); + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[2] {major, minor}; + CustomAttributeBuilder TypeLibVerBuilder = new CustomAttributeBuilder(TypeLibVerCons, aArgs); + + // Set the attribute on the assembly builder. + asmBldr.SetCustomAttribute(TypeLibVerBuilder); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetVersionInformation(AssemblyBuilder asmBldr, Object typeLib, AssemblyName asmName) + { + // Extract the name of the typelib. + String strTypeLibName = null; + String strDocString = null; + int dwHelpContext = 0; + String strHelpFile = null; + ITypeLib pTLB = (ITypeLib)typeLib; + pTLB.GetDocumentation(-1, out strTypeLibName, out strDocString, out dwHelpContext, out strHelpFile); + + // Generate the product name string from the named of the typelib. + String strProductName = String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("TypeLibConverter_ImportedTypeLibProductName"), strTypeLibName); + + // Set the OS version information. + asmBldr.DefineVersionInfoResource(strProductName, asmName.Version.ToString(), null, null, null); + + // Set the TypeLibVersion attribute + SetTypeLibVersionAttribute(asmBldr, typeLib); + } + + [System.Security.SecurityCritical] // auto-generated + private static void SetPIAAttributeOnAssembly(AssemblyBuilder asmBldr, Object typeLib) + { + IntPtr pAttr = IntPtr.Zero; + _TYPELIBATTR Attr; + ITypeLib pTLB = (ITypeLib)typeLib; + int Major = 0; + int Minor = 0; + + // Retrieve the PrimaryInteropAssemblyAttribute constructor. + Type []aConsParams = new Type[2] {typeof(int), typeof(int)}; + ConstructorInfo PIAAttrCons = typeof(PrimaryInteropAssemblyAttribute).GetConstructor(aConsParams); + + // Retrieve the major and minor version from the typelib. + try + { + pTLB.GetLibAttr(out pAttr); + Attr = (_TYPELIBATTR)Marshal.PtrToStructure(pAttr, typeof(_TYPELIBATTR)); + Major = Attr.wMajorVerNum; + Minor = Attr.wMinorVerNum; + } + finally + { + // Release the typelib attributes. + if (pAttr != IntPtr.Zero) + pTLB.ReleaseTLibAttr(pAttr); + } + + // Create an instance of the custom attribute builder. + Object[] aArgs = new Object[2] {Major, Minor}; + CustomAttributeBuilder PIACABuilder = new CustomAttributeBuilder(PIAAttrCons, aArgs); + + // Set the PrimaryInteropAssemblyAttribute on the assembly builder. + asmBldr.SetCustomAttribute(PIACABuilder); + } + + + // + // Native helper methods. + // + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern void nConvertTypeLibToMetadata(Object typeLib, RuntimeAssembly asmBldr, RuntimeModule modBldr, String nameSpace, TypeLibImporterFlags flags, ITypeLibImporterNotifySink notifySink, out ArrayList eventItfInfoList); + + // Must use assembly versioning or GuidAttribute to avoid collisions in typelib export or registration. + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Object nConvertAssemblyToTypeLib(RuntimeAssembly assembly, String strTypeLibName, TypeLibExporterFlags flags, ITypeLibExporterNotifySink notifySink); + + [System.Security.SecurityCritical] // auto-generated + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] + internal extern static void LoadInMemoryTypeByName(RuntimeModule module, String className); + + // + // Helper class called when a resolve type event is fired. + // + + private class TypeResolveHandler : ITypeLibImporterNotifySink + { + public TypeResolveHandler(ModuleBuilder mod, ITypeLibImporterNotifySink userSink) + { + m_Module = mod; + m_UserSink = userSink; + } + + public void ReportEvent(ImporterEventKind eventKind, int eventCode, String eventMsg) + { + m_UserSink.ReportEvent(eventKind, eventCode, eventMsg); + } + + public Assembly ResolveRef(Object typeLib) + { + Contract.Ensures(Contract.Result<Assembly>() != null && Contract.Result<Assembly>() is RuntimeAssembly); + Contract.EndContractBlock(); + + // Call the user sink to resolve the reference. + Assembly asm = m_UserSink.ResolveRef(typeLib); + + if (asm == null) + throw new ArgumentNullException(); + + // Return the resolved assembly. We extract the internal assembly because we are called + // by the VM which accesses fields of the object directly and does not go via those + // delegating properties (the fields are empty if asm is an (external) AssemblyBuilder). + + RuntimeAssembly rtAssembly = asm as RuntimeAssembly; + if (rtAssembly == null) + { + AssemblyBuilder ab = asm as AssemblyBuilder; + if (ab != null) + rtAssembly = ab.InternalAssembly; + } + + if (rtAssembly == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly")); + + // Add the assembly to the list of assemblies. + m_AsmList.Add(rtAssembly); + + return rtAssembly; + } + + [System.Security.SecurityCritical] // auto-generated + public Assembly ResolveEvent(Object sender, ResolveEventArgs args) + { + // We need to load the type in the resolve event so that we will deal with + // cases where we are trying to load the CoClass before the interface has + // been loaded. + try + { + LoadInMemoryTypeByName(m_Module.GetNativeHandle(), args.Name); + return m_Module.Assembly; + } + catch (TypeLoadException e) + { + if (e.ResourceId != System.__HResults.COR_E_TYPELOAD) // type not found + throw; + } + + foreach (RuntimeAssembly asm in m_AsmList) + { + try + { + asm.GetType(args.Name, true, false); + return asm; + } + catch (TypeLoadException e) + { + if (e._HResult != System.__HResults.COR_E_TYPELOAD) // type not found + throw; + } + } + + return null; + } + + public Assembly ResolveAsmEvent(Object sender, ResolveEventArgs args) + { + foreach (RuntimeAssembly asm in m_AsmList) + { + if (String.Compare(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase) == 0) + return asm; + } + + return null; + } + + public Assembly ResolveROAsmEvent(Object sender, ResolveEventArgs args) + { + foreach (RuntimeAssembly asm in m_AsmList) + { + if (String.Compare(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase) == 0) + return asm; + } + + // We failed to find the referenced assembly in our pre-loaded assemblies, so try to load it based on policy. + string asmName = AppDomain.CurrentDomain.ApplyPolicy(args.Name); + return Assembly.ReflectionOnlyLoad(asmName); + } + + private ModuleBuilder m_Module; + private ITypeLibImporterNotifySink m_UserSink; + private List<RuntimeAssembly> m_AsmList = new List<RuntimeAssembly>(); + } + } +} +#endif // !FEATURE_CORECLR // current implementation requires reflection only load + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIBindCtx.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIBindCtx.cs new file mode 100644 index 0000000000..8c6e1bcd64 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIBindCtx.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIBindCtx interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.BIND_OPTS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential)] + + public struct BIND_OPTS + { + public int cbStruct; + public int grfFlags; + public int grfMode; + public int dwTickCountDeadline; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IBindCtx instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("0000000e-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIBindCtx + { + void RegisterObjectBound([MarshalAs(UnmanagedType.Interface)] Object punk); + void RevokeObjectBound([MarshalAs(UnmanagedType.Interface)] Object punk); + void ReleaseBoundObjects(); + void SetBindOptions([In()] ref BIND_OPTS pbindopts); + void GetBindOptions(ref BIND_OPTS pbindopts); + void GetRunningObjectTable(out UCOMIRunningObjectTable pprot); + void RegisterObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey, [MarshalAs(UnmanagedType.Interface)] Object punk); + void GetObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey, [MarshalAs(UnmanagedType.Interface)] out Object ppunk); + void EnumObjectParam(out UCOMIEnumString ppenum); + void RevokeObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIConnectionPoint.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIConnectionPoint.cs new file mode 100644 index 0000000000..b12e1f7a8c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIConnectionPoint.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIConnectionPoint interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IConnectionPoint instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("B196B286-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIConnectionPoint + { + void GetConnectionInterface(out Guid pIID); + void GetConnectionPointContainer(out UCOMIConnectionPointContainer ppCPC); + void Advise([MarshalAs(UnmanagedType.Interface)] Object pUnkSink, out int pdwCookie); + void Unadvise(int dwCookie); + void EnumConnections(out UCOMIEnumConnections ppEnum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIConnectionPointContainer.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIConnectionPointContainer.cs new file mode 100644 index 0000000000..212d643e03 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIConnectionPointContainer.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIConnectionPointContainer interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IConnectionPointContainer instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("B196B284-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIConnectionPointContainer + { + void EnumConnectionPoints(out UCOMIEnumConnectionPoints ppEnum); + void FindConnectionPoint(ref Guid riid, out UCOMIConnectionPoint ppCP); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumConnectionPoints.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumConnectionPoints.cs new file mode 100644 index 0000000000..5180018088 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumConnectionPoints.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIEnumConnectionPoints interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumConnectionPoints instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("B196B285-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIEnumConnectionPoints + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] UCOMIConnectionPoint[] rgelt, out int pceltFetched); + [PreserveSig] + int Skip(int celt); + [PreserveSig] + int Reset(); + void Clone(out UCOMIEnumConnectionPoints ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumConnections.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumConnections.cs new file mode 100644 index 0000000000..87273b34f7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumConnections.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIEnumConnections interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.CONNECTDATA instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct CONNECTDATA + { + [MarshalAs(UnmanagedType.Interface)] + public Object pUnk; + public int dwCookie; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumConnections instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("B196B287-BAB4-101A-B69C-00AA00341D07")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIEnumConnections + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] CONNECTDATA[] rgelt, out int pceltFetched); + [PreserveSig] + int Skip(int celt); + [PreserveSig] + void Reset(); + void Clone(out UCOMIEnumConnections ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumMoniker.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumMoniker.cs new file mode 100644 index 0000000000..40787fcd5c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumMoniker.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIEnumMoniker interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + using DWORD = System.UInt32; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumMoniker instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00000102-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIEnumMoniker + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] UCOMIMoniker[] rgelt, out int pceltFetched); + [PreserveSig] + int Skip(int celt); + [PreserveSig] + int Reset(); + void Clone(out UCOMIEnumMoniker ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumString.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumString.cs new file mode 100644 index 0000000000..9d0552a0df --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumString.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIEnumString interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumString instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00000101-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIEnumString + { + [PreserveSig] + int Next(int celt, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0), Out] String[] rgelt, out int pceltFetched); + [PreserveSig] + int Skip(int celt); + [PreserveSig] + int Reset(); + void Clone(out UCOMIEnumString ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumVARIANT.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumVARIANT.cs new file mode 100644 index 0000000000..0d8585ffdb --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumVARIANT.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIEnumVARIANT interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumVARIANT instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00020404-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIEnumVARIANT + { + [PreserveSig] + int Next(int celt, int rgvar, int pceltFetched); + + [PreserveSig] + int Skip(int celt); + + [PreserveSig] + int Reset(); + + void Clone(int ppenum); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumerable.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumerable.cs new file mode 100644 index 0000000000..81e0133923 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumerable.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: UCOMIEnumerable +** +** +** Purpose: +** This interface is redefined here since the original IEnumerable interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IEnumerable interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices +{ + using System; + using System.Collections; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumerable instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")] + internal interface UCOMIEnumerable + { + [DispId(-4)] + IEnumerator GetEnumerator(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumerator.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumerator.cs new file mode 100644 index 0000000000..af886c46ac --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIEnumerator.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: UCOMIEnumerator +** +** +** Purpose: +** This interface is redefined here since the original IEnumerator interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IEnumerator interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IEnumerator instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")] + internal interface UCOMIEnumerator + { + bool MoveNext(); + Object Current { + get; + } + void Reset(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIExpando.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIExpando.cs new file mode 100644 index 0000000000..2f85c14bc6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIExpando.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: UCOMIExpando +** +** +** Purpose: +** This interface is redefined here since the original IExpando interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IExpando interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices +{ + + using System; + using System.Reflection; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IExpando instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("AFBF15E6-C37C-11d2-B88E-00A0C9B471B8")] + internal interface UCOMIExpando : UCOMIReflect + { + FieldInfo AddField(String name); + PropertyInfo AddProperty(String name); + MethodInfo AddMethod(String name, Delegate method); + void RemoveMember(MemberInfo m); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIMoniker.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIMoniker.cs new file mode 100644 index 0000000000..bd5b6f4916 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIMoniker.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIMoniker interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.FILETIME instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential)] + + public struct FILETIME + { + public int dwLowDateTime; + public int dwHighDateTime; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IMoniker instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("0000000f-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIMoniker + { + // IPersist portion + void GetClassID(out Guid pClassID); + + // IPersistStream portion + [PreserveSig] + int IsDirty(); + void Load(UCOMIStream pStm); + void Save(UCOMIStream pStm, [MarshalAs(UnmanagedType.Bool)] bool fClearDirty); + void GetSizeMax(out Int64 pcbSize); + + // IMoniker portion + void BindToObject(UCOMIBindCtx pbc, UCOMIMoniker pmkToLeft, [In()] ref Guid riidResult, [MarshalAs(UnmanagedType.Interface)] out Object ppvResult); + void BindToStorage(UCOMIBindCtx pbc, UCOMIMoniker pmkToLeft, [In()] ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out Object ppvObj); + void Reduce(UCOMIBindCtx pbc, int dwReduceHowFar, ref UCOMIMoniker ppmkToLeft, out UCOMIMoniker ppmkReduced); + void ComposeWith(UCOMIMoniker pmkRight, [MarshalAs(UnmanagedType.Bool)] bool fOnlyIfNotGeneric, out UCOMIMoniker ppmkComposite); + void Enum([MarshalAs(UnmanagedType.Bool)] bool fForward, out UCOMIEnumMoniker ppenumMoniker); + void IsEqual(UCOMIMoniker pmkOtherMoniker); + void Hash(out int pdwHash); + void IsRunning(UCOMIBindCtx pbc, UCOMIMoniker pmkToLeft, UCOMIMoniker pmkNewlyRunning); + void GetTimeOfLastChange(UCOMIBindCtx pbc, UCOMIMoniker pmkToLeft, out FILETIME pFileTime); + void Inverse(out UCOMIMoniker ppmk); + void CommonPrefixWith(UCOMIMoniker pmkOther, out UCOMIMoniker ppmkPrefix); + void RelativePathTo(UCOMIMoniker pmkOther, out UCOMIMoniker ppmkRelPath); + void GetDisplayName(UCOMIBindCtx pbc, UCOMIMoniker pmkToLeft, [MarshalAs(UnmanagedType.LPWStr)] out String ppszDisplayName); + void ParseDisplayName(UCOMIBindCtx pbc, UCOMIMoniker pmkToLeft, [MarshalAs(UnmanagedType.LPWStr)] String pszDisplayName, out int pchEaten, out UCOMIMoniker ppmkOut); + void IsSystemMoniker(out int pdwMksys); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIPersistFile.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIPersistFile.cs new file mode 100644 index 0000000000..ac465e771f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIPersistFile.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIPersistFile interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + using DWORD = System.UInt32; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IPersistFile instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("0000010b-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIPersistFile + { + // IPersist portion + void GetClassID(out Guid pClassID); + + // IPersistFile portion + [PreserveSig] + int IsDirty(); + void Load([MarshalAs(UnmanagedType.LPWStr)] String pszFileName, int dwMode); + void Save([MarshalAs(UnmanagedType.LPWStr)] String pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember); + void SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] String pszFileName); + void GetCurFile([MarshalAs(UnmanagedType.LPWStr)] out String ppszFileName); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIReflect.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIReflect.cs new file mode 100644 index 0000000000..d6cdd6828e --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIReflect.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*========================================================================== +** +** Interface: UCOMIReflect +** +** +** Purpose: +** This interface is redefined here since the original IReflect interface +** has all its methods marked as ecall's since it is a managed standard +** interface. This interface is used from within the runtime to make a call +** on the COM server directly when it implements the IReflect interface. +** +** +==========================================================================*/ +namespace System.Runtime.InteropServices +{ + using System; + using System.Reflection; + using CultureInfo = System.Globalization.CultureInfo; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IReflect instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("AFBF15E5-C37C-11d2-B88E-00A0C9B471B8")] + internal interface UCOMIReflect + { + MethodInfo GetMethod(String name,BindingFlags bindingAttr,Binder binder, + Type[] types,ParameterModifier[] modifiers); + + MethodInfo GetMethod(String name,BindingFlags bindingAttr); + + MethodInfo[] GetMethods( + BindingFlags bindingAttr); + + FieldInfo GetField( + String name, + BindingFlags bindingAttr); + + FieldInfo[] GetFields( + BindingFlags bindingAttr); + + PropertyInfo GetProperty( + String name, + BindingFlags bindingAttr); + + PropertyInfo GetProperty( + String name, + BindingFlags bindingAttr, + Binder binder, + Type returnType, + Type[] types, + ParameterModifier[] modifiers); + + PropertyInfo[] GetProperties( + BindingFlags bindingAttr); + + MemberInfo[] GetMember( + String name, + BindingFlags bindingAttr); + + MemberInfo[] GetMembers( + BindingFlags bindingAttr); + + Object InvokeMember( + String name, + BindingFlags invokeAttr, + Binder binder, + Object target, + Object[] args, + ParameterModifier[] modifiers, + CultureInfo culture, + String[] namedParameters); + + Type UnderlyingSystemType { + get; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIRunningObjectTable.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIRunningObjectTable.cs new file mode 100644 index 0000000000..8088c0417f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIRunningObjectTable.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIRunningObjectTable interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IRunningObjectTable instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00000010-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIRunningObjectTable + { + void Register(int grfFlags, [MarshalAs(UnmanagedType.Interface)] Object punkObject, UCOMIMoniker pmkObjectName, out int pdwRegister); + void Revoke(int dwRegister); + void IsRunning(UCOMIMoniker pmkObjectName); + void GetObject(UCOMIMoniker pmkObjectName, [MarshalAs(UnmanagedType.Interface)] out Object ppunkObject); + void NoteChangeTime(int dwRegister, ref FILETIME pfiletime); + void GetTimeOfLastChange(UCOMIMoniker pmkObjectName, out FILETIME pfiletime); + void EnumRunning(out UCOMIEnumMoniker ppenumMoniker); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMIStream.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIStream.cs new file mode 100644 index 0000000000..dc2cb53d97 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMIStream.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMIStream interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.STATSTG instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + + public struct STATSTG + { + public String pwcsName; + public int type; + public Int64 cbSize; + public FILETIME mtime; + public FILETIME ctime; + public FILETIME atime; + public int grfMode; + public int grfLocksSupported; + public Guid clsid; + public int grfStateBits; + public int reserved; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IStream instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("0000000c-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMIStream + { + // ISequentialStream portion + void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] Byte[] pv, int cb,IntPtr pcbRead); + void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Byte[] pv, int cb, IntPtr pcbWritten); + + // IStream portion + void Seek(Int64 dlibMove, int dwOrigin, IntPtr plibNewPosition); + void SetSize(Int64 libNewSize); + void CopyTo(UCOMIStream pstm, Int64 cb, IntPtr pcbRead, IntPtr pcbWritten); + void Commit(int grfCommitFlags); + void Revert(); + void LockRegion(Int64 libOffset, Int64 cb, int dwLockType); + void UnlockRegion(Int64 libOffset, Int64 cb, int dwLockType); + void Stat(out STATSTG pstatstg, int grfStatFlag); + void Clone(out UCOMIStream ppstm); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeComp.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeComp.cs new file mode 100644 index 0000000000..0ef1e549a5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeComp.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMITypeComp interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.DESCKIND instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Serializable] + public enum DESCKIND + { + DESCKIND_NONE = 0, + DESCKIND_FUNCDESC = DESCKIND_NONE + 1, + DESCKIND_VARDESC = DESCKIND_FUNCDESC + 1, + DESCKIND_TYPECOMP = DESCKIND_VARDESC + 1, + DESCKIND_IMPLICITAPPOBJ = DESCKIND_TYPECOMP + 1, + DESCKIND_MAX = DESCKIND_IMPLICITAPPOBJ + 1 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.BINDPTR instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)] + + public struct BINDPTR + { + [FieldOffset(0)] + public IntPtr lpfuncdesc; + [FieldOffset(0)] + public IntPtr lpvardesc; + [FieldOffset(0)] + public IntPtr lptcomp; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.ITypeComp instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00020403-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMITypeComp + { + void Bind([MarshalAs(UnmanagedType.LPWStr)] String szName, int lHashVal, Int16 wFlags, out UCOMITypeInfo ppTInfo, out DESCKIND pDescKind, out BINDPTR pBindPtr); + void BindType([MarshalAs(UnmanagedType.LPWStr)] String szName, int lHashVal, out UCOMITypeInfo ppTInfo, out UCOMITypeComp ppTComp); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeInfo.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeInfo.cs new file mode 100644 index 0000000000..e26964f5a3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeInfo.cs @@ -0,0 +1,330 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMITypeInfo interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.TYPEKIND instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Serializable] + public enum TYPEKIND + { + TKIND_ENUM = 0, + TKIND_RECORD = TKIND_ENUM + 1, + TKIND_MODULE = TKIND_RECORD + 1, + TKIND_INTERFACE = TKIND_MODULE + 1, + TKIND_DISPATCH = TKIND_INTERFACE + 1, + TKIND_COCLASS = TKIND_DISPATCH + 1, + TKIND_ALIAS = TKIND_COCLASS + 1, + TKIND_UNION = TKIND_ALIAS + 1, + TKIND_MAX = TKIND_UNION + 1 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.TYPEFLAGS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum TYPEFLAGS : short + { + TYPEFLAG_FAPPOBJECT = 0x1, + TYPEFLAG_FCANCREATE = 0x2, + TYPEFLAG_FLICENSED = 0x4, + TYPEFLAG_FPREDECLID = 0x8, + TYPEFLAG_FHIDDEN = 0x10, + TYPEFLAG_FCONTROL = 0x20, + TYPEFLAG_FDUAL = 0x40, + TYPEFLAG_FNONEXTENSIBLE = 0x80, + TYPEFLAG_FOLEAUTOMATION = 0x100, + TYPEFLAG_FRESTRICTED = 0x200, + TYPEFLAG_FAGGREGATABLE = 0x400, + TYPEFLAG_FREPLACEABLE = 0x800, + TYPEFLAG_FDISPATCHABLE = 0x1000, + TYPEFLAG_FREVERSEBIND = 0x2000, + TYPEFLAG_FPROXY = 0x4000 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IMPLTYPEFLAGS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum IMPLTYPEFLAGS + { + IMPLTYPEFLAG_FDEFAULT = 0x1, + IMPLTYPEFLAG_FSOURCE = 0x2, + IMPLTYPEFLAG_FRESTRICTED = 0x4, + IMPLTYPEFLAG_FDEFAULTVTABLE = 0x8, + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.TYPEATTR instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct TYPEATTR + { + // Constant used with the memid fields. + public const int MEMBER_ID_NIL = unchecked((int)0xFFFFFFFF); + + // Actual fields of the TypeAttr struct. + public Guid guid; + public Int32 lcid; + public Int32 dwReserved; + public Int32 memidConstructor; + public Int32 memidDestructor; + public IntPtr lpstrSchema; + public Int32 cbSizeInstance; + public TYPEKIND typekind; + public Int16 cFuncs; + public Int16 cVars; + public Int16 cImplTypes; + public Int16 cbSizeVft; + public Int16 cbAlignment; + public TYPEFLAGS wTypeFlags; + public Int16 wMajorVerNum; + public Int16 wMinorVerNum; + public TYPEDESC tdescAlias; + public IDLDESC idldescType; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.FUNCDESC instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential)] + public struct FUNCDESC + { + public int memid; //MEMBERID memid; + public IntPtr lprgscode; // /* [size_is(cScodes)] */ SCODE RPC_FAR *lprgscode; + public IntPtr lprgelemdescParam; // /* [size_is(cParams)] */ ELEMDESC __RPC_FAR *lprgelemdescParam; + public FUNCKIND funckind; //FUNCKIND funckind; + public INVOKEKIND invkind; //INVOKEKIND invkind; + public CALLCONV callconv; //CALLCONV callconv; + public Int16 cParams; //short cParams; + public Int16 cParamsOpt; //short cParamsOpt; + public Int16 oVft; //short oVft; + public Int16 cScodes; //short cScodes; + public ELEMDESC elemdescFunc; //ELEMDESC elemdescFunc; + public Int16 wFuncFlags; //WORD wFuncFlags; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IDLFLAG instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum IDLFLAG : short + { + IDLFLAG_NONE = PARAMFLAG.PARAMFLAG_NONE, + IDLFLAG_FIN = PARAMFLAG.PARAMFLAG_FIN, + IDLFLAG_FOUT = PARAMFLAG.PARAMFLAG_FOUT, + IDLFLAG_FLCID = PARAMFLAG.PARAMFLAG_FLCID, + IDLFLAG_FRETVAL = PARAMFLAG.PARAMFLAG_FRETVAL + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.IDLDESC instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct IDLDESC + { + public int dwReserved; + public IDLFLAG wIDLFlags; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.PARAMFLAG instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum PARAMFLAG :short + { + PARAMFLAG_NONE = 0, + PARAMFLAG_FIN = 0x1, + PARAMFLAG_FOUT = 0x2, + PARAMFLAG_FLCID = 0x4, + PARAMFLAG_FRETVAL = 0x8, + PARAMFLAG_FOPT = 0x10, + PARAMFLAG_FHASDEFAULT = 0x20, + PARAMFLAG_FHASCUSTDATA = 0x40 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.PARAMDESC instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct PARAMDESC + { + public IntPtr lpVarValue; + public PARAMFLAG wParamFlags; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.TYPEDESC instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct TYPEDESC + { + public IntPtr lpValue; + public Int16 vt; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.ELEMDESC instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct ELEMDESC + { + public TYPEDESC tdesc; + + [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)] + [ComVisible(false)] + public struct DESCUNION + { + [FieldOffset(0)] + public IDLDESC idldesc; + [FieldOffset(0)] + public PARAMDESC paramdesc; + }; + public DESCUNION desc; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.VARDESC instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct VARDESC + { + public int memid; + public String lpstrSchema; + + [System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)] + [ComVisible(false)] + public struct DESCUNION + { + [FieldOffset(0)] + public int oInst; + [FieldOffset(0)] + public IntPtr lpvarValue; + }; + + public ELEMDESC elemdescVar; + public short wVarFlags; + public VarEnum varkind; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.DISPPARAMS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct DISPPARAMS + { + public IntPtr rgvarg; + public IntPtr rgdispidNamedArgs; + public int cArgs; + public int cNamedArgs; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.EXCEPINFO instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct EXCEPINFO + { + public Int16 wCode; + public Int16 wReserved; + [MarshalAs(UnmanagedType.BStr)] public String bstrSource; + [MarshalAs(UnmanagedType.BStr)] public String bstrDescription; + [MarshalAs(UnmanagedType.BStr)] public String bstrHelpFile; + public int dwHelpContext; + public IntPtr pvReserved; + public IntPtr pfnDeferredFillIn; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.FUNCKIND instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Serializable] + public enum FUNCKIND : int + { + FUNC_VIRTUAL = 0, + FUNC_PUREVIRTUAL = 1, + FUNC_NONVIRTUAL = 2, + FUNC_STATIC = 3, + FUNC_DISPATCH = 4 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.INVOKEKIND instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Serializable] + public enum INVOKEKIND : int + { + INVOKE_FUNC = 0x1, + INVOKE_PROPERTYGET = 0x2, + INVOKE_PROPERTYPUT = 0x4, + INVOKE_PROPERTYPUTREF = 0x8 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.CALLCONV instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Serializable] + public enum CALLCONV : int + { + CC_CDECL =1, + CC_MSCPASCAL=2, + CC_PASCAL =CC_MSCPASCAL, + CC_MACPASCAL=3, + CC_STDCALL =4, + CC_RESERVED =5, + CC_SYSCALL =6, + CC_MPWCDECL =7, + CC_MPWPASCAL=8, + CC_MAX =9 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.FUNCFLAGS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum FUNCFLAGS : short + { + FUNCFLAG_FRESTRICTED= 0x1, + FUNCFLAG_FSOURCE = 0x2, + FUNCFLAG_FBINDABLE = 0x4, + FUNCFLAG_FREQUESTEDIT = 0x8, + FUNCFLAG_FDISPLAYBIND = 0x10, + FUNCFLAG_FDEFAULTBIND = 0x20, + FUNCFLAG_FHIDDEN = 0x40, + FUNCFLAG_FUSESGETLASTERROR= 0x80, + FUNCFLAG_FDEFAULTCOLLELEM= 0x100, + FUNCFLAG_FUIDEFAULT = 0x200, + FUNCFLAG_FNONBROWSABLE = 0x400, + FUNCFLAG_FREPLACEABLE = 0x800, + FUNCFLAG_FIMMEDIATEBIND = 0x1000 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.VARFLAGS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum VARFLAGS : short + { + VARFLAG_FREADONLY =0x1, + VARFLAG_FSOURCE =0x2, + VARFLAG_FBINDABLE =0x4, + VARFLAG_FREQUESTEDIT =0x8, + VARFLAG_FDISPLAYBIND =0x10, + VARFLAG_FDEFAULTBIND =0x20, + VARFLAG_FHIDDEN =0x40, + VARFLAG_FRESTRICTED =0x80, + VARFLAG_FDEFAULTCOLLELEM =0x100, + VARFLAG_FUIDEFAULT =0x200, + VARFLAG_FNONBROWSABLE =0x400, + VARFLAG_FREPLACEABLE =0x800, + VARFLAG_FIMMEDIATEBIND =0x1000 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.ITypeInfo instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00020401-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMITypeInfo + { + void GetTypeAttr(out IntPtr ppTypeAttr); + void GetTypeComp(out UCOMITypeComp ppTComp); + void GetFuncDesc(int index, out IntPtr ppFuncDesc); + void GetVarDesc(int index, out IntPtr ppVarDesc); + void GetNames(int memid, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] String[] rgBstrNames, int cMaxNames, out int pcNames); + void GetRefTypeOfImplType(int index, out int href); + void GetImplTypeFlags(int index, out int pImplTypeFlags); + void GetIDsOfNames([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1), In] String[] rgszNames, int cNames, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] int[] pMemId); + void Invoke([MarshalAs(UnmanagedType.IUnknown)] Object pvInstance, int memid, Int16 wFlags, ref DISPPARAMS pDispParams, out Object pVarResult, out EXCEPINFO pExcepInfo, out int puArgErr); + void GetDocumentation(int index, out String strName, out String strDocString, out int dwHelpContext, out String strHelpFile); + void GetDllEntry(int memid, INVOKEKIND invKind, out String pBstrDllName, out String pBstrName, out Int16 pwOrdinal); + void GetRefTypeInfo(int hRef, out UCOMITypeInfo ppTI); + void AddressOfMember(int memid, INVOKEKIND invKind, out IntPtr ppv); + void CreateInstance([MarshalAs(UnmanagedType.IUnknown)] Object pUnkOuter, ref Guid riid, [MarshalAs(UnmanagedType.IUnknown), Out] out Object ppvObj); + void GetMops(int memid, out String pBstrMops); + void GetContainingTypeLib(out UCOMITypeLib ppTLB, out int pIndex); + void ReleaseTypeAttr(IntPtr pTypeAttr); + void ReleaseFuncDesc(IntPtr pFuncDesc); + void ReleaseVarDesc(IntPtr pVarDesc); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeLib.cs b/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeLib.cs new file mode 100644 index 0000000000..c8b63deff6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UCOMITypeLib.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: UCOMITypeLib interface definition. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices +{ + using System; + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.SYSKIND instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Serializable] + public enum SYSKIND + { + SYS_WIN16 = 0, + SYS_WIN32 = SYS_WIN16 + 1, + SYS_MAC = SYS_WIN32 + 1 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.LIBFLAGS instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] +[Serializable] +[Flags()] + public enum LIBFLAGS : short + { + LIBFLAG_FRESTRICTED = 0x1, + LIBFLAG_FCONTROL = 0x2, + LIBFLAG_FHIDDEN = 0x4, + LIBFLAG_FHASDISKIMAGE = 0x8 + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.TYPELIBATTR instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + [Serializable] + public struct TYPELIBATTR + { + public Guid guid; + public int lcid; + public SYSKIND syskind; + public Int16 wMajorVerNum; + public Int16 wMinorVerNum; + public LIBFLAGS wLibFlags; + } + + [Obsolete("Use System.Runtime.InteropServices.ComTypes.ITypeLib instead. http://go.microsoft.com/fwlink/?linkid=14202", false)] + [Guid("00020402-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface UCOMITypeLib + { + [PreserveSig] + int GetTypeInfoCount(); + void GetTypeInfo(int index, out UCOMITypeInfo ppTI); + void GetTypeInfoType(int index, out TYPEKIND pTKind); + void GetTypeInfoOfGuid(ref Guid guid, out UCOMITypeInfo ppTInfo); + void GetLibAttr(out IntPtr ppTLibAttr); + void GetTypeComp(out UCOMITypeComp ppTComp); + void GetDocumentation(int index, out String strName, out String strDocString, out int dwHelpContext, out String strHelpFile); + [return : MarshalAs(UnmanagedType.Bool)] + bool IsName([MarshalAs(UnmanagedType.LPWStr)] String szNameBuf, int lHashVal); + void FindName([MarshalAs(UnmanagedType.LPWStr)] String szNameBuf, int lHashVal, [MarshalAs(UnmanagedType.LPArray), Out] UCOMITypeInfo[] ppTInfo, [MarshalAs(UnmanagedType.LPArray), Out] int[] rgMemId, ref Int16 pcFound); + [PreserveSig] + void ReleaseTLibAttr(IntPtr pTLibAttr); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/UnknownWrapper.cs b/src/mscorlib/src/System/Runtime/InteropServices/UnknownWrapper.cs new file mode 100644 index 0000000000..69ef2dcc06 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/UnknownWrapper.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Wrapper that is converted to a variant with VT_UNKNOWN. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class UnknownWrapper + { + public UnknownWrapper(Object obj) + { + m_WrappedObject = obj; + } + + public Object WrappedObject + { + get + { + return m_WrappedObject; + } + } + + private Object m_WrappedObject; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Variant.cs b/src/mscorlib/src/System/Runtime/InteropServices/Variant.cs new file mode 100644 index 0000000000..9be1588ac0 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/Variant.cs @@ -0,0 +1,659 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices { + using System.Diagnostics; + + /// <summary> + /// Variant is the basic COM type for late-binding. It can contain any other COM data type. + /// This type definition precisely matches the unmanaged data layout so that the struct can be passed + /// to and from COM calls. + /// </summary> + [StructLayout(LayoutKind.Explicit)] + [System.Security.SecurityCritical] + internal struct Variant { + +#if DEBUG + static Variant() { + // Variant size is the size of 4 pointers (16 bytes) on a 32-bit processor, + // and 3 pointers (24 bytes) on a 64-bit processor. + int variantSize = Marshal.SizeOf(typeof(Variant)); + if (IntPtr.Size == 4) { + BCLDebug.Assert(variantSize == (4 * IntPtr.Size), "variant"); + } else { + BCLDebug.Assert(IntPtr.Size == 8, "variant"); + BCLDebug.Assert(variantSize == (3 * IntPtr.Size), "variant"); + } + } +#endif + + // Most of the data types in the Variant are carried in _typeUnion + [FieldOffset(0)] private TypeUnion _typeUnion; + + // Decimal is the largest data type and it needs to use the space that is normally unused in TypeUnion._wReserved1, etc. + // Hence, it is declared to completely overlap with TypeUnion. A Decimal does not use the first two bytes, and so + // TypeUnion._vt can still be used to encode the type. + [FieldOffset(0)] private Decimal _decimal; + + [StructLayout(LayoutKind.Sequential)] + private struct TypeUnion { + internal ushort _vt; + internal ushort _wReserved1; + internal ushort _wReserved2; + internal ushort _wReserved3; + + internal UnionTypes _unionTypes; + } + + [StructLayout(LayoutKind.Sequential)] + private struct Record { + private IntPtr _record; + private IntPtr _recordInfo; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + [StructLayout(LayoutKind.Explicit)] + private struct UnionTypes { + #region Generated Variant union types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_UnionTypes from: generate_comdispatch.py + + [FieldOffset(0)] internal SByte _i1; + [FieldOffset(0)] internal Int16 _i2; + [FieldOffset(0)] internal Int32 _i4; + [FieldOffset(0)] internal Int64 _i8; + [FieldOffset(0)] internal Byte _ui1; + [FieldOffset(0)] internal UInt16 _ui2; + [FieldOffset(0)] internal UInt32 _ui4; + [FieldOffset(0)] internal UInt64 _ui8; + [FieldOffset(0)] internal Int32 _int; + [FieldOffset(0)] internal UInt32 _uint; + [FieldOffset(0)] internal Int16 _bool; + [FieldOffset(0)] internal Int32 _error; + [FieldOffset(0)] internal Single _r4; + [FieldOffset(0)] internal Double _r8; + [FieldOffset(0)] internal Int64 _cy; + [FieldOffset(0)] internal double _date; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _bstr; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _unknown; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _dispatch; + + // *** END GENERATED CODE *** + + #endregion + + [FieldOffset(0)] internal IntPtr _pvarVal; + [FieldOffset(0)] internal IntPtr _byref; + [FieldOffset(0)] internal Record _record; + } + + /// <summary> + /// Primitive types are the basic COM types. It includes valuetypes like ints, but also reference types + /// like BStrs. It does not include composite types like arrays and user-defined COM types (IUnknown/IDispatch). + /// </summary> + internal static bool IsPrimitiveType(VarEnum varEnum) { + switch(varEnum) { + #region Generated Variant IsPrimitiveType + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_IsPrimitiveType from: generate_comdispatch.py + + case VarEnum.VT_I1: + case VarEnum.VT_I2: + case VarEnum.VT_I4: + case VarEnum.VT_I8: + case VarEnum.VT_UI1: + case VarEnum.VT_UI2: + case VarEnum.VT_UI4: + case VarEnum.VT_UI8: + case VarEnum.VT_INT: + case VarEnum.VT_UINT: + case VarEnum.VT_BOOL: + case VarEnum.VT_R4: + case VarEnum.VT_R8: + case VarEnum.VT_DECIMAL: + case VarEnum.VT_DATE: + case VarEnum.VT_BSTR: + + // *** END GENERATED CODE *** + + #endregion + return true; + } + + return false; + } + + unsafe public void CopyFromIndirect(object value) { + + VarEnum vt = (VarEnum)(((int)this.VariantType) & ~((int)VarEnum.VT_BYREF)); + + if (value == null) { + if (vt == VarEnum.VT_DISPATCH || vt == VarEnum.VT_UNKNOWN || vt == VarEnum.VT_BSTR) { + *(IntPtr*)this._typeUnion._unionTypes._byref = IntPtr.Zero; + } + return; + } + + switch (vt) { + case VarEnum.VT_I1: + *(sbyte*)this._typeUnion._unionTypes._byref = (sbyte)value; + break; + + case VarEnum.VT_UI1: + *(byte*)this._typeUnion._unionTypes._byref = (byte)value; + break; + + case VarEnum.VT_I2: + *(short*)this._typeUnion._unionTypes._byref = (short)value; + break; + + case VarEnum.VT_UI2: + *(ushort*)this._typeUnion._unionTypes._byref = (ushort)value; + break; + + case VarEnum.VT_BOOL: + *(short*)this._typeUnion._unionTypes._byref = (bool)value ? (short)-1 : (short)0; + break; + + case VarEnum.VT_I4: + case VarEnum.VT_INT: + *(int*)this._typeUnion._unionTypes._byref = (int)value; + break; + + case VarEnum.VT_UI4: + case VarEnum.VT_UINT: + *(uint*)this._typeUnion._unionTypes._byref = (uint)value; + break; + + case VarEnum.VT_ERROR: + *(int*)this._typeUnion._unionTypes._byref = ((ErrorWrapper)value).ErrorCode; + break; + + case VarEnum.VT_I8: + *(Int64*)this._typeUnion._unionTypes._byref = (Int64)value; + break; + + case VarEnum.VT_UI8: + *(UInt64*)this._typeUnion._unionTypes._byref = (UInt64)value; + break; + + case VarEnum.VT_R4: + *(float*)this._typeUnion._unionTypes._byref = (float)value; + break; + + case VarEnum.VT_R8: + *(double*)this._typeUnion._unionTypes._byref = (double)value; + break; + + case VarEnum.VT_DATE: + *(double*)this._typeUnion._unionTypes._byref = ((DateTime)value).ToOADate(); + break; + + case VarEnum.VT_UNKNOWN: + *(IntPtr*)this._typeUnion._unionTypes._byref = Marshal.GetIUnknownForObject(value); + break; + + case VarEnum.VT_DISPATCH: + *(IntPtr*)this._typeUnion._unionTypes._byref = Marshal.GetIDispatchForObject(value); + break; + + case VarEnum.VT_BSTR: + *(IntPtr*)this._typeUnion._unionTypes._byref = Marshal.StringToBSTR((string)value); + break; + + case VarEnum.VT_CY: + *(long*)this._typeUnion._unionTypes._byref = decimal.ToOACurrency((decimal)value); + break; + + case VarEnum.VT_DECIMAL: + *(decimal*)this._typeUnion._unionTypes._byref = (decimal)value; + break; + + case VarEnum.VT_VARIANT: + Marshal.GetNativeVariantForObject(value, this._typeUnion._unionTypes._byref); + break; + + default: + throw new ArgumentException("invalid argument type"); + } + } + + /// <summary> + /// Get the managed object representing the Variant. + /// </summary> + /// <returns></returns> + public object ToObject() { + // Check the simple case upfront + if (IsEmpty) { + return null; + } + + switch (VariantType) { + case VarEnum.VT_NULL: return DBNull.Value; + + #region Generated Variant ToObject + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_ToObject from: generate_comdispatch.py + + case VarEnum.VT_I1: return AsI1; + case VarEnum.VT_I2: return AsI2; + case VarEnum.VT_I4: return AsI4; + case VarEnum.VT_I8: return AsI8; + case VarEnum.VT_UI1: return AsUi1; + case VarEnum.VT_UI2: return AsUi2; + case VarEnum.VT_UI4: return AsUi4; + case VarEnum.VT_UI8: return AsUi8; + case VarEnum.VT_INT: return AsInt; + case VarEnum.VT_UINT: return AsUint; + case VarEnum.VT_BOOL: return AsBool; + case VarEnum.VT_ERROR: return AsError; + case VarEnum.VT_R4: return AsR4; + case VarEnum.VT_R8: return AsR8; + case VarEnum.VT_DECIMAL: return AsDecimal; + case VarEnum.VT_CY: return AsCy; + case VarEnum.VT_DATE: return AsDate; + case VarEnum.VT_BSTR: return AsBstr; + case VarEnum.VT_UNKNOWN: return AsUnknown; + case VarEnum.VT_DISPATCH: return AsDispatch; + // VarEnum.VT_VARIANT is handled by Marshal.GetObjectForNativeVariant below + + // *** END GENERATED CODE *** + + #endregion + + default: + try { + unsafe { + fixed (void* pThis = &this) { + return Marshal.GetObjectForNativeVariant((System.IntPtr)pThis); + } + } + } + catch (Exception ex) { + throw new NotImplementedException("Variant.ToObject cannot handle" + VariantType, ex); + } + } + } + + /// <summary> + /// Release any unmanaged memory associated with the Variant + /// </summary> + /// <returns></returns> + public void Clear() { + // We do not need to call OLE32's VariantClear for primitive types or ByRefs + // to safe ourselves the cost of interop transition. + // ByRef indicates the memory is not owned by the VARIANT itself while + // primitive types do not have any resources to free up. + // Hence, only safearrays, BSTRs, interfaces and user types are + // handled differently. + VarEnum vt = VariantType; + if ((vt & VarEnum.VT_BYREF) != 0) { + VariantType = VarEnum.VT_EMPTY; + } else if ( + ((vt & VarEnum.VT_ARRAY) != 0) || + ((vt) == VarEnum.VT_BSTR) || + ((vt) == VarEnum.VT_UNKNOWN) || + ((vt) == VarEnum.VT_DISPATCH) || + ((vt) == VarEnum.VT_VARIANT) || + ((vt) == VarEnum.VT_RECORD) || + ((vt) == VarEnum.VT_VARIANT) + ) { + unsafe { + fixed (void* pThis = &this) { + NativeMethods.VariantClear((IntPtr)pThis); + } + } + BCLDebug.Assert(IsEmpty, "variant"); + } else { + VariantType = VarEnum.VT_EMPTY; + } + } + + public VarEnum VariantType { + get { + return (VarEnum)_typeUnion._vt; + } + set { + _typeUnion._vt = (ushort)value; + } + } + + internal bool IsEmpty { + get { + return _typeUnion._vt == ((ushort)VarEnum.VT_EMPTY); + } + } + + internal bool IsByRef { + get { + return (_typeUnion._vt & ((ushort)VarEnum.VT_BYREF)) != 0; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly")] + public void SetAsNULL() { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_NULL; + } + + #region Generated Variant accessors + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_accessors from: generate_comdispatch.py + + // VT_I1 + + public SByte AsI1 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I1, "variant"); + return _typeUnion._unionTypes._i1; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I1; + _typeUnion._unionTypes._i1 = value; + } + } + + // VT_I2 + + public Int16 AsI2 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I2, "variant"); + return _typeUnion._unionTypes._i2; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I2; + _typeUnion._unionTypes._i2 = value; + } + } + + // VT_I4 + + public Int32 AsI4 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I4, "variant"); + return _typeUnion._unionTypes._i4; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I4; + _typeUnion._unionTypes._i4 = value; + } + } + + // VT_I8 + + public Int64 AsI8 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I8, "variant"); + return _typeUnion._unionTypes._i8; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I8; + _typeUnion._unionTypes._i8 = value; + } + } + + // VT_UI1 + + public Byte AsUi1 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI1, "variant"); + return _typeUnion._unionTypes._ui1; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI1; + _typeUnion._unionTypes._ui1 = value; + } + } + + // VT_UI2 + + public UInt16 AsUi2 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI2, "variant"); + return _typeUnion._unionTypes._ui2; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI2; + _typeUnion._unionTypes._ui2 = value; + } + } + + // VT_UI4 + + public UInt32 AsUi4 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI4, "variant"); + return _typeUnion._unionTypes._ui4; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI4; + _typeUnion._unionTypes._ui4 = value; + } + } + + // VT_UI8 + + public UInt64 AsUi8 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI8, "variant"); + return _typeUnion._unionTypes._ui8; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI8; + _typeUnion._unionTypes._ui8 = value; + } + } + + // VT_INT + + public Int32 AsInt { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_INT, "variant"); + return _typeUnion._unionTypes._int; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_INT; + _typeUnion._unionTypes._int = value; + } + } + + // VT_UINT + + public UInt32 AsUint { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UINT, "variant"); + return _typeUnion._unionTypes._uint; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UINT; + _typeUnion._unionTypes._uint = value; + } + } + + // VT_BOOL + + public bool AsBool { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_BOOL, "variant"); + return _typeUnion._unionTypes._bool != 0; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_BOOL; + _typeUnion._unionTypes._bool = value ? (short)-1 : (short)0; + } + } + + // VT_ERROR + + public Int32 AsError { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_ERROR, "variant"); + return _typeUnion._unionTypes._error; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_ERROR; + _typeUnion._unionTypes._error = value; + } + } + + // VT_R4 + + public Single AsR4 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_R4, "variant"); + return _typeUnion._unionTypes._r4; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_R4; + _typeUnion._unionTypes._r4 = value; + } + } + + // VT_R8 + + public Double AsR8 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_R8, "variant"); + return _typeUnion._unionTypes._r8; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_R8; + _typeUnion._unionTypes._r8 = value; + } + } + + // VT_DECIMAL + + public Decimal AsDecimal { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_DECIMAL, "variant"); + // The first byte of Decimal is unused, but usually set to 0 + Variant v = this; + v._typeUnion._vt = 0; + return v._decimal; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DECIMAL; + _decimal = value; + // _vt overlaps with _decimal, and should be set after setting _decimal + _typeUnion._vt = (ushort)VarEnum.VT_DECIMAL; + } + } + + // VT_CY + + public Decimal AsCy { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_CY, "variant"); + return Decimal.FromOACurrency(_typeUnion._unionTypes._cy); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_CY; + _typeUnion._unionTypes._cy = Decimal.ToOACurrency(value); + } + } + + // VT_DATE + + public DateTime AsDate { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_DATE, "variant"); + return DateTime.FromOADate(_typeUnion._unionTypes._date); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DATE; + _typeUnion._unionTypes._date = value.ToOADate(); + } + } + + // VT_BSTR + + public String AsBstr { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_BSTR, "variant"); + return (string)Marshal.PtrToStringBSTR(this._typeUnion._unionTypes._bstr); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_BSTR; + this._typeUnion._unionTypes._bstr = Marshal.StringToBSTR(value); + } + } + + // VT_UNKNOWN + + public Object AsUnknown { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UNKNOWN, "variant"); + if (_typeUnion._unionTypes._unknown == IntPtr.Zero) + return null; + return Marshal.GetObjectForIUnknown(_typeUnion._unionTypes._unknown); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UNKNOWN; + if (value == null) + _typeUnion._unionTypes._unknown = IntPtr.Zero; + else + _typeUnion._unionTypes._unknown = Marshal.GetIUnknownForObject(value); + } + } + + // VT_DISPATCH + + public Object AsDispatch { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_DISPATCH, "variant"); + if (_typeUnion._unionTypes._dispatch == IntPtr.Zero) + return null; + return Marshal.GetObjectForIUnknown(_typeUnion._unionTypes._dispatch); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DISPATCH; + if (value == null) + _typeUnion._unionTypes._dispatch = IntPtr.Zero; + else + _typeUnion._unionTypes._dispatch = Marshal.GetIDispatchForObject(value); + } + } + + + // *** END GENERATED CODE *** + + internal IntPtr AsByRefVariant + { + get { + BCLDebug.Assert(VariantType == (VarEnum.VT_BYREF | VarEnum.VT_VARIANT), "variant"); + return _typeUnion._unionTypes._pvarVal; + } + } + + #endregion + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/VariantWrapper.cs b/src/mscorlib/src/System/Runtime/InteropServices/VariantWrapper.cs new file mode 100644 index 0000000000..3f5120af39 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/VariantWrapper.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Wrapper that is converted to a variant with VT_BYREF | VT_VARIANT. +** +** +=============================================================================*/ + +namespace System.Runtime.InteropServices { + + using System; + + [Serializable] + + public sealed class VariantWrapper + { + public VariantWrapper(Object obj) + { + m_WrappedObject = obj; + } + + public Object WrappedObject + { + get + { + return m_WrappedObject; + } + } + + private Object m_WrappedObject; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/Attributes.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/Attributes.cs new file mode 100644 index 0000000000..80b24f5529 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/Attributes.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // DefaultInterfaceAttribute marks a WinRT class (or interface group) that has its default interface specified. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + public sealed class DefaultInterfaceAttribute : Attribute + { + private Type m_defaultInterface; + + public DefaultInterfaceAttribute(Type defaultInterface) + { + m_defaultInterface = defaultInterface; + } + + public Type DefaultInterface + { + get { return m_defaultInterface; } + } + } + + // WindowsRuntimeImport is a pseudo custom attribute which causes us to emit the tdWindowsRuntime bit + // onto types which are decorated with the attribute. This is needed to mark Windows Runtime types + // which are redefined in mscorlib.dll and System.Runtime.WindowsRuntime.dll, as the C# compiler does + // not have a built in syntax to mark tdWindowsRuntime. These two assemblies are special as they + // implement the CLR's support for WinRT, so this type is internal as marking tdWindowsRuntime should + // generally be done via winmdexp for user code. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal sealed class WindowsRuntimeImportAttribute : Attribute + { + public WindowsRuntimeImportAttribute() + { } + } + + // This attribute is applied to class interfaces in a generated projection assembly. It is used by Visual Studio + // and other tools to find out what version of a component (eg. Windows) a WinRT class began to implement + // a particular interfaces. + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)] + public sealed class InterfaceImplementedInVersionAttribute : Attribute + { + public InterfaceImplementedInVersionAttribute(Type interfaceType, byte majorVersion, byte minorVersion, byte buildVersion, byte revisionVersion) + { + m_interfaceType = interfaceType; + m_majorVersion = majorVersion; + m_minorVersion = minorVersion; + m_buildVersion = buildVersion; + m_revisionVersion = revisionVersion; + } + + public Type InterfaceType + { + get { return m_interfaceType; } + } + + public byte MajorVersion + { + get { return m_majorVersion; } + } + + public byte MinorVersion + { + get { return m_minorVersion; } + } + + public byte BuildVersion + { + get { return m_buildVersion; } + } + + public byte RevisionVersion + { + get { return m_revisionVersion; } + } + + private Type m_interfaceType; + private byte m_majorVersion; + private byte m_minorVersion; + private byte m_buildVersion; + private byte m_revisionVersion; + } + + // Applies to read-only array parameters + [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)] + public sealed class ReadOnlyArrayAttribute : Attribute + { + public ReadOnlyArrayAttribute() {} + } + + // Applies to write-only array parameters + [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)] + public sealed class WriteOnlyArrayAttribute : Attribute + { + public WriteOnlyArrayAttribute() {} + } + + + + // This attribute is applied on the return value to specify the name of the return value. + // In WindowsRuntime all parameters including return value need to have unique names. + // This is essential in JS as one of the ways to get at the results of a method in JavaScript is via a Dictionary object keyed by parameter name. + [AttributeUsage(AttributeTargets.ReturnValue | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] + public sealed class ReturnValueNameAttribute : Attribute + { + private string m_Name; + public ReturnValueNameAttribute(string name) + { + m_Name = name; + } + + public string Name + { + get { return m_Name; } + } + } + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToCollectionAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToCollectionAdapter.cs new file mode 100644 index 0000000000..5574f3c251 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToCollectionAdapter.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Runtime; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the ICollection interface on WinRT + // objects that support IBindableVector. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not BindableVectorToCollectionAdapter objects. Rather, they are + // of type IBindableVector. No actual BindableVectorToCollectionAdapter object is ever instantiated. + // Thus, you will see a lot of expressions that cast "this" to "IBindableVector". + internal sealed class BindableVectorToCollectionAdapter + { + private BindableVectorToCollectionAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // int Count { get } + [Pure] + [SecurityCritical] + internal int Count() + { + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + uint size = _this.Size; + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)size; + } + + // bool IsSynchronized { get } + [Pure] + [SecurityCritical] + internal bool IsSynchronized() + { + return false; + } + + // object SyncRoot { get } + [Pure] + [SecurityCritical] + internal object SyncRoot() + { + return this; + } + + // void CopyTo(Array array, int index) + [Pure] + [SecurityCritical] + internal void CopyTo(Array array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + + // ICollection expects the destination array to be single-dimensional. + if (array.Rank != 1) + throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported")); + + int destLB = array.GetLowerBound(0); + + int srcLen = Count(); + int destLen = array.GetLength(0); + + if (arrayIndex < destLB) + throw new ArgumentOutOfRangeException("arrayIndex"); + + // Does the dimension in question have sufficient space to copy the expected number of entries? + // We perform this check before valid index check to ensure the exception message is in sync with + // the following snippet that uses regular framework code: + // + // ArrayList list = new ArrayList(); + // list.Add(1); + // Array items = Array.CreateInstance(typeof(object), new int[] { 1 }, new int[] { -1 }); + // list.CopyTo(items, 0); + + if(srcLen > (destLen - (arrayIndex - destLB))) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + if(arrayIndex - destLB > destLen) + throw new ArgumentException(Environment.GetResourceString("Argument_IndexOutOfArrayBounds")); + + // We need to verify the index as we; + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + + for (uint i = 0; i < srcLen; i++) + { + array.SetValue(_this.GetAt(i), i + arrayIndex); + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs new file mode 100644 index 0000000000..73ebf721ee --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Runtime; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IList interface on WinRT + // objects that support IBindableVector. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not BindableVectorToListAdapter objects. Rather, they are + // of type IBindableVector. No actual BindableVectorToListAdapter object is ever instantiated. + // Thus, you will see a lot of expressions that cast "this" to "IBindableVector". + internal sealed class BindableVectorToListAdapter + { + private BindableVectorToListAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // object this[int index] { get } + [SecurityCritical] + internal object Indexer_Get(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + return GetAt(_this, (uint)index); + } + + // object this[int index] { set } + [SecurityCritical] + internal void Indexer_Set(int index, object value) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + SetAt(_this, (uint)index, value); + } + + // int Add(object value) + [SecurityCritical] + internal int Add(object value) + { + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + _this.Append(value); + + uint size = _this.Size; + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)(size - 1); + } + + // bool Contains(object item) + [SecurityCritical] + internal bool Contains(object item) + { + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + + uint index; + return _this.IndexOf(item, out index); + } + + // void Clear() + [SecurityCritical] + internal void Clear() + { + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + _this.Clear(); + } + + // bool IsFixedSize { get } + [Pure] + [SecurityCritical] + internal bool IsFixedSize() + { + return false; + } + + // bool IsReadOnly { get } + [Pure] + [SecurityCritical] + internal bool IsReadOnly() + { + return false; + } + + // int IndexOf(object item) + [SecurityCritical] + internal int IndexOf(object item) + { + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + + uint index; + bool exists = _this.IndexOf(item, out index); + + if (!exists) + return -1; + + if (((uint)Int32.MaxValue) < index) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)index; + } + + // void Insert(int index, object item) + [SecurityCritical] + internal void Insert(int index, object item) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + InsertAtHelper(_this, (uint)index, item); + } + + // bool Remove(object item) + [SecurityCritical] + internal void Remove(object item) + { + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + + uint index; + bool exists = _this.IndexOf(item, out index); + + if (exists) + { + if (((uint)Int32.MaxValue) < index) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + RemoveAtHelper(_this, index); + } + } + + // void RemoveAt(int index) + [SecurityCritical] + internal void RemoveAt(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IBindableVector _this = JitHelpers.UnsafeCast<IBindableVector>(this); + RemoveAtHelper(_this, (uint)index); + } + + // Helpers: + + private static object GetAt(IBindableVector _this, uint index) + { + try + { + return _this.GetAt(index); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + private static void SetAt(IBindableVector _this, uint index, object value) + { + try + { + _this.SetAt(index, value); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + private static void InsertAtHelper(IBindableVector _this, uint index, object item) + { + try + { + _this.InsertAt(index, item); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + private static void RemoveAtHelper(IBindableVector _this, uint index) + { + try + { + _this.RemoveAt(index); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIKeyValuePairImpl.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIKeyValuePairImpl.cs new file mode 100644 index 0000000000..3a52d12234 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIKeyValuePairImpl.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // Provides access to a System.Collections.Generic.KeyValuePair<K, V> via the IKeyValuePair<K, V> WinRT interface. + internal sealed class CLRIKeyValuePairImpl<K, V> : IKeyValuePair<K, V>, + IGetProxyTarget + { + private readonly KeyValuePair<K, V> _pair; + + public CLRIKeyValuePairImpl([In] ref KeyValuePair<K, V> pair) + { + _pair = pair; + } + + // IKeyValuePair<K, V> implementation + [Pure] + public K Key + { + get { return _pair.Key; } + } + + [Pure] + public V Value + { + get { return _pair.Value; } + } + + // Called from the VM to wrap a boxed KeyValuePair with a CLRIKeyValuePairImpl. + internal static object BoxHelper(object pair) + { + Contract.Requires(pair != null); + + KeyValuePair<K, V> unboxedPair = (KeyValuePair<K, V>)pair; + return new CLRIKeyValuePairImpl<K, V>(ref unboxedPair); + } + + // Called from the VM to get a boxed KeyValuePair out of a CLRIKeyValuePairImpl. + internal static object UnboxHelper(object wrapper) + { + Contract.Requires(wrapper != null); + + CLRIKeyValuePairImpl<K, V> reference = (CLRIKeyValuePairImpl<K, V>)wrapper; + return reference._pair; + } + + public override string ToString() + { + return _pair.ToString(); + } + + object IGetProxyTarget.GetTarget() + { + return _pair; + } + + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs new file mode 100644 index 0000000000..c88f13dd0b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs @@ -0,0 +1,555 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + internal class CLRIPropertyValueImpl : IPropertyValue + { + private PropertyType _type; + private Object _data; + + // Numeric scalar types which participate in coersion + private static volatile Tuple<Type, PropertyType>[] s_numericScalarTypes; + + internal CLRIPropertyValueImpl(PropertyType type, Object data) + { + _type = type; + _data = data; + } + + private static Tuple<Type, PropertyType>[] NumericScalarTypes { + get { + if (s_numericScalarTypes == null) { + Tuple<Type, PropertyType>[] numericScalarTypes = new Tuple<Type, PropertyType>[] { + new Tuple<Type, PropertyType>(typeof(Byte), PropertyType.UInt8), + new Tuple<Type, PropertyType>(typeof(Int16), PropertyType.Int16), + new Tuple<Type, PropertyType>(typeof(UInt16), PropertyType.UInt16), + new Tuple<Type, PropertyType>(typeof(Int32), PropertyType.Int32), + new Tuple<Type, PropertyType>(typeof(UInt32), PropertyType.UInt32), + new Tuple<Type, PropertyType>(typeof(Int64), PropertyType.Int64), + new Tuple<Type, PropertyType>(typeof(UInt64), PropertyType.UInt64), + new Tuple<Type, PropertyType>(typeof(Single), PropertyType.Single), + new Tuple<Type, PropertyType>(typeof(Double), PropertyType.Double) + }; + + s_numericScalarTypes = numericScalarTypes; + } + + return s_numericScalarTypes; + } + } + + public PropertyType Type { + [Pure] + get { return _type; } + } + + public bool IsNumericScalar { + [Pure] + get { + return IsNumericScalarImpl(_type, _data); + } + } + + public override string ToString() + { + if (_data != null) + { + return _data.ToString(); + } + else + { + return base.ToString(); + } + } + + [Pure] + public Byte GetUInt8() + { + return CoerceScalarValue<Byte>(PropertyType.UInt8); + } + + [Pure] + public Int16 GetInt16() + { + return CoerceScalarValue<Int16>(PropertyType.Int16); + } + + public UInt16 GetUInt16() + { + return CoerceScalarValue<UInt16>(PropertyType.UInt16); + } + + [Pure] + public Int32 GetInt32() + { + return CoerceScalarValue<Int32>(PropertyType.Int32); + } + + [Pure] + public UInt32 GetUInt32() + { + return CoerceScalarValue<UInt32>(PropertyType.UInt32); + } + + [Pure] + public Int64 GetInt64() + { + return CoerceScalarValue<Int64>(PropertyType.Int64); + } + + [Pure] + public UInt64 GetUInt64() + { + return CoerceScalarValue<UInt64>(PropertyType.UInt64); + } + + [Pure] + public Single GetSingle() + { + return CoerceScalarValue<Single>(PropertyType.Single); + } + + [Pure] + public Double GetDouble() + { + return CoerceScalarValue<Double>(PropertyType.Double); + } + + [Pure] + public char GetChar16() + { + if (this.Type != PropertyType.Char16) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Char16"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (char)_data; + } + + [Pure] + public Boolean GetBoolean() + { + if (this.Type != PropertyType.Boolean) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Boolean"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (bool)_data; + } + + [Pure] + public String GetString() + { + return CoerceScalarValue<String>(PropertyType.String); + } + + [Pure] + public Object GetInspectable() + { + if (this.Type != PropertyType.Inspectable) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Inspectable"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return _data; + } + + + [Pure] + public Guid GetGuid() + { + return CoerceScalarValue<Guid>(PropertyType.Guid); + } + + + [Pure] + public DateTimeOffset GetDateTime() + { + if (this.Type != PropertyType.DateTime) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "DateTime"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (DateTimeOffset)_data; + } + + [Pure] + public TimeSpan GetTimeSpan() + { + if (this.Type != PropertyType.TimeSpan) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "TimeSpan"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (TimeSpan)_data; + } + + [Pure] + [SecuritySafeCritical] + public Point GetPoint() + { + if (this.Type != PropertyType.Point) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Point"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + + return Unbox<Point>(IReferenceFactory.s_pointType); + } + + [Pure] + [SecuritySafeCritical] + public Size GetSize() + { + if (this.Type != PropertyType.Size) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Size"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + + return Unbox<Size>(IReferenceFactory.s_sizeType); + } + + [Pure] + [SecuritySafeCritical] + public Rect GetRect() + { + if (this.Type != PropertyType.Rect) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Rect"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + + return Unbox<Rect>(IReferenceFactory.s_rectType); + } + + [Pure] + public Byte[] GetUInt8Array() + { + return CoerceArrayValue<Byte>(PropertyType.UInt8Array); + } + + [Pure] + public Int16[] GetInt16Array() + { + return CoerceArrayValue<Int16>(PropertyType.Int16Array); + } + + [Pure] + public UInt16[] GetUInt16Array() + { + return CoerceArrayValue<UInt16>(PropertyType.UInt16Array); + } + + [Pure] + public Int32[] GetInt32Array() + { + return CoerceArrayValue<Int32>(PropertyType.Int32Array); + } + + [Pure] + public UInt32[] GetUInt32Array() + { + return CoerceArrayValue<UInt32>(PropertyType.UInt32Array); + } + + [Pure] + public Int64[] GetInt64Array() + { + return CoerceArrayValue<Int64>(PropertyType.Int64Array); + } + + [Pure] + public UInt64[] GetUInt64Array() + { + return CoerceArrayValue<UInt64>(PropertyType.UInt64Array); + } + + [Pure] + public Single[] GetSingleArray() + { + return CoerceArrayValue<Single>(PropertyType.SingleArray); + } + + [Pure] + public Double[] GetDoubleArray() + { + return CoerceArrayValue<Double>(PropertyType.DoubleArray); + } + + [Pure] + public char[] GetChar16Array() + { + if (this.Type != PropertyType.Char16Array) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Char16[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (char[])_data; + } + + [Pure] + public Boolean[] GetBooleanArray() + { + if (this.Type != PropertyType.BooleanArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Boolean[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (bool[])_data; + } + + [Pure] + public String[] GetStringArray() + { + return CoerceArrayValue<String>(PropertyType.StringArray); + } + + [Pure] + public Object[] GetInspectableArray() + { + if (this.Type != PropertyType.InspectableArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Inspectable[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (Object[])_data; + } + + [Pure] + public Guid[] GetGuidArray() + { + return CoerceArrayValue<Guid>(PropertyType.GuidArray); + } + + [Pure] + public DateTimeOffset[] GetDateTimeArray() + { + if (this.Type != PropertyType.DateTimeArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "DateTimeOffset[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (DateTimeOffset[])_data; + } + + [Pure] + public TimeSpan[] GetTimeSpanArray() + { + if (this.Type != PropertyType.TimeSpanArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "TimeSpan[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + return (TimeSpan[])_data; + } + + [Pure] + [SecuritySafeCritical] + public Point[] GetPointArray() + { + if (this.Type != PropertyType.PointArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Point[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + + return UnboxArray<Point>(IReferenceFactory.s_pointType); + } + + [Pure] + [SecuritySafeCritical] + public Size[] GetSizeArray() + { + if (this.Type != PropertyType.SizeArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Size[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + + + return UnboxArray<Size>(IReferenceFactory.s_sizeType); + } + + [Pure] + [SecuritySafeCritical] + public Rect[] GetRectArray() + { + if (this.Type != PropertyType.RectArray) + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Rect[]"), __HResults.TYPE_E_TYPEMISMATCH); + Contract.EndContractBlock(); + + return UnboxArray<Rect>(IReferenceFactory.s_rectType); + } + + private T[] CoerceArrayValue<T>(PropertyType unboxType) { + // If we contain the type being looked for directly, then take the fast-path + if (Type == unboxType) { + return (T[])_data; + } + + // Make sure we have an array to begin with + Array dataArray = _data as Array; + if (dataArray == null) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, typeof(T).MakeArrayType().Name), __HResults.TYPE_E_TYPEMISMATCH); + } + + // Array types are 1024 larger than their equivilent scalar counterpart + BCLDebug.Assert((int)Type > 1024, "Unexpected array PropertyType value"); + PropertyType scalarType = Type - 1024; + + // If we do not have the correct array type, then we need to convert the array element-by-element + // to a new array of the requested type + T[] coercedArray = new T[dataArray.Length]; + for (int i = 0; i < dataArray.Length; ++i) { + try { + coercedArray[i] = CoerceScalarValue<T>(scalarType, dataArray.GetValue(i)); + } catch (InvalidCastException elementCastException) { + Exception e = new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueArrayCoersion", this.Type, typeof(T).MakeArrayType().Name, i, elementCastException.Message), elementCastException); + e.SetErrorCode(elementCastException._HResult); + throw e; + } + } + + return coercedArray; + } + + private T CoerceScalarValue<T>(PropertyType unboxType) + { + // If we are just a boxed version of the requested type, then take the fast path out + if (Type == unboxType) { + return (T)_data; + } + + return CoerceScalarValue<T>(Type, _data); + } + + private static T CoerceScalarValue<T>(PropertyType type, object value) { + // If the property type is neither one of the coercable numeric types nor IInspectable, we + // should not attempt coersion, even if the underlying value is technically convertable + if (!IsCoercable(type, value) && type != PropertyType.Inspectable) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH); + } + + try { + // Try to coerce: + // * String <--> Guid + // * Numeric scalars + if (type == PropertyType.String && typeof(T) == typeof(Guid)) { + return (T)(object)Guid.Parse((string)value); + } + else if (type == PropertyType.Guid && typeof(T) == typeof(String)) { + return (T)(object)((Guid)value).ToString("D", System.Globalization.CultureInfo.InvariantCulture); + } + else { + // Iterate over the numeric scalars, to see if we have a match for one of the known conversions + foreach (Tuple<Type, PropertyType> numericScalar in NumericScalarTypes) { + if (numericScalar.Item1 == typeof(T)) { + return (T)Convert.ChangeType(value, typeof(T), System.Globalization.CultureInfo.InvariantCulture); + } + } + } + } + catch (FormatException) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH); + } + catch (InvalidCastException) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH); + } + catch (OverflowException) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueCoersion", type, value, typeof(T).Name), __HResults.DISP_E_OVERFLOW); + } + + // If the property type is IInspectable, and we have a nested IPropertyValue, then we need + // to pass along the request to coerce the value. + IPropertyValue ipv = value as IPropertyValue; + if (type == PropertyType.Inspectable && ipv != null) { + if (typeof(T) == typeof(Byte)) { + return (T)(object)ipv.GetUInt8(); + } + else if (typeof(T) == typeof(Int16)) { + return (T)(object)ipv.GetInt16(); + } + else if (typeof(T) == typeof(UInt16)) { + return (T)(object)ipv.GetUInt16(); + } + else if (typeof(T) == typeof(Int32)) { + return (T)(object)ipv.GetUInt32(); + } + else if (typeof(T) == typeof(UInt32)) { + return (T)(object)ipv.GetUInt32(); + } + else if (typeof(T) == typeof(Int64)) { + return (T)(object)ipv.GetInt64(); + } + else if (typeof(T) == typeof(UInt64)) { + return (T)(object)ipv.GetUInt64(); + } + else if (typeof(T) == typeof(Single)) { + return (T)(object)ipv.GetSingle(); + } + else if (typeof(T) == typeof(Double)) { + return (T)(object)ipv.GetDouble(); + } + else { + BCLDebug.Assert(false, "T in coersion function wasn't understood as a type that can be coerced - make sure that CoerceScalarValue and NumericScalarTypes are in sync"); + } + } + + // Otherwise, this is an invalid coersion + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH); + } + + private static bool IsCoercable(PropertyType type, object data) { + // String <--> Guid is allowed + if (type == PropertyType.Guid || type == PropertyType.String) { + return true; + } + + // All numeric scalars can also be coerced + return IsNumericScalarImpl(type, data); + } + + private static bool IsNumericScalarImpl(PropertyType type, object data) { + if (data.GetType().IsEnum) { + return true; + } + + foreach (Tuple<Type, PropertyType> numericScalar in NumericScalarTypes) { + if (numericScalar.Item2 == type) { + return true; + } + } + + return false; + } + + // Unbox the data stored in the property value to a structurally equivilent type + [Pure] + [SecurityCritical] + private unsafe T Unbox<T>(Type expectedBoxedType) where T : struct { + Contract.Requires(expectedBoxedType != null); + Contract.Requires(Marshal.SizeOf(expectedBoxedType) == Marshal.SizeOf(typeof(T))); + + if (_data.GetType() != expectedBoxedType) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", _data.GetType(), expectedBoxedType.Name), __HResults.TYPE_E_TYPEMISMATCH); + } + + T unboxed = new T(); + + fixed (byte *pData = &JitHelpers.GetPinningHelper(_data).m_data) { + byte* pUnboxed = (byte*)JitHelpers.UnsafeCastToStackPointer(ref unboxed); + Buffer.Memcpy(pUnboxed, pData, Marshal.SizeOf(unboxed)); + } + + return unboxed; + } + + // Convert the array stored in the property value to a structurally equivilent array type + [Pure] + [SecurityCritical] + private unsafe T[] UnboxArray<T>(Type expectedArrayElementType) where T : struct { + Contract.Requires(expectedArrayElementType != null); + Contract.Requires(Marshal.SizeOf(expectedArrayElementType) == Marshal.SizeOf(typeof(T))); + + Array dataArray = _data as Array; + if (dataArray == null || _data.GetType().GetElementType() != expectedArrayElementType) { + throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", _data.GetType(), expectedArrayElementType.MakeArrayType().Name), __HResults.TYPE_E_TYPEMISMATCH); + } + + T[] converted = new T[dataArray.Length]; + + if (converted.Length > 0) { + fixed (byte * dataPin = &JitHelpers.GetPinningHelper(dataArray).m_data) { + fixed (byte * convertedPin = &JitHelpers.GetPinningHelper(converted).m_data) { + byte *pData = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(dataArray, 0); + byte *pConverted = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(converted, 0); + + Buffer.Memcpy(pConverted, pData, checked(Marshal.SizeOf(typeof(T)) * converted.Length)); + } + } + } + + return converted; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIReferenceImpl.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIReferenceImpl.cs new file mode 100644 index 0000000000..e379d38cf3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIReferenceImpl.cs @@ -0,0 +1,406 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections; +using System.Diagnostics.Contracts; +using System.Reflection; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + internal sealed class CLRIReferenceImpl<T> : CLRIPropertyValueImpl, IReference<T>, IGetProxyTarget + { + private T _value; + + public CLRIReferenceImpl(PropertyType type, T obj) + : base(type, obj) + { + BCLDebug.Assert(obj != null, "Must not be null"); + _value = obj; + } + + public T Value { + get { return _value; } + } + + public override string ToString() + { + if (_value != null) + { + return _value.ToString(); + } + else + { + return base.ToString(); + } + } + + object IGetProxyTarget.GetTarget() + { + return (object)_value; + } + + // We have T in an IReference<T>. Need to QI for IReference<T> with the appropriate GUID, call + // the get_Value property, allocate an appropriately-sized managed object, marshal the native object + // to the managed object, and free the native method. Also we want the return value boxed (aka normal value type boxing). + // + // This method is called by VM. Mark the method with FriendAccessAllowed attribute to ensure that the unreferenced method + // optimization skips it and the code will be saved into NGen image. + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal static Object UnboxHelper(Object wrapper) + { + Contract.Requires(wrapper != null); + IReference<T> reference = (IReference<T>) wrapper; + Contract.Assert(reference != null, "CLRIReferenceImpl::UnboxHelper - QI'ed for IReference<"+typeof(T)+">, but that failed."); + return reference.Value; + } + } + + // T can be any WinRT-compatible type + internal sealed class CLRIReferenceArrayImpl<T> : CLRIPropertyValueImpl, + IGetProxyTarget, + IReferenceArray<T>, + IList // Jupiter data binding needs IList/IEnumerable + { + private T[] _value; + private IList _list; + + public CLRIReferenceArrayImpl(PropertyType type, T[] obj) + : base(type, obj) + { + BCLDebug.Assert(obj != null, "Must not be null"); + + _value = obj; + + _list = (IList) _value; + } + + public T[] Value { + get { return _value; } + } + + public override string ToString() + { + if (_value != null) + { + return _value.ToString(); + } + else + { + return base.ToString(); + } + } + + // + // IEnumerable methods. Used by data-binding in Jupiter when you try to data bind + // against a managed array + // + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_value).GetEnumerator(); + } + + // + // IList & ICollection methods. + // This enables two-way data binding and index access in Jupiter + // + Object IList.this[int index] { + get + { + return _list[index]; + } + + set + { + _list[index] = value; + } + } + + int IList.Add(Object value) + { + return _list.Add(value); + } + + bool IList.Contains(Object value) + { + return _list.Contains(value); + } + + void IList.Clear() + { + _list.Clear(); + } + + bool IList.IsReadOnly + { + get + { + return _list.IsReadOnly; + } + } + + bool IList.IsFixedSize + { + get + { + return _list.IsFixedSize; + } + } + + int IList.IndexOf(Object value) + { + return _list.IndexOf(value); + } + + void IList.Insert(int index, Object value) + { + _list.Insert(index, value); + } + + void IList.Remove(Object value) + { + _list.Remove(value); + } + + void IList.RemoveAt(int index) + { + _list.RemoveAt(index); + } + + void ICollection.CopyTo(Array array, int index) + { + _list.CopyTo(array, index); + } + + int ICollection.Count + { + get + { + return _list.Count; + } + } + + Object ICollection.SyncRoot + { + get + { + return _list.SyncRoot; + } + } + + bool ICollection.IsSynchronized + { + get + { + return _list.IsSynchronized; + } + } + + object IGetProxyTarget.GetTarget() + { + return (object)_value; + } + + // We have T in an IReferenceArray<T>. Need to QI for IReferenceArray<T> with the appropriate GUID, call + // the get_Value property, allocate an appropriately-sized managed object, marshal the native object + // to the managed object, and free the native method. + // + // This method is called by VM. Mark the method with FriendAccessAllowed attribute to ensure that the unreferenced method + // optimization skips it and the code will be saved into NGen image. + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal static Object UnboxHelper(Object wrapper) + { + Contract.Requires(wrapper != null); + IReferenceArray<T> reference = (IReferenceArray<T>)wrapper; + Contract.Assert(reference != null, "CLRIReferenceArrayImpl::UnboxHelper - QI'ed for IReferenceArray<" + typeof(T) + ">, but that failed."); + T[] marshaled = reference.Value; + return marshaled; + } + } + + // For creating instances of Windows Runtime's IReference<T> and IReferenceArray<T>. + internal static class IReferenceFactory + { + internal static readonly Type s_pointType = Type.GetType("Windows.Foundation.Point, " + AssemblyRef.SystemRuntimeWindowsRuntime); + internal static readonly Type s_rectType = Type.GetType("Windows.Foundation.Rect, " + AssemblyRef.SystemRuntimeWindowsRuntime); + internal static readonly Type s_sizeType = Type.GetType("Windows.Foundation.Size, " + AssemblyRef.SystemRuntimeWindowsRuntime); + + [SecuritySafeCritical] + internal static Object CreateIReference(Object obj) + { + Contract.Requires(obj != null, "Null should not be boxed."); + Contract.Ensures(Contract.Result<Object>() != null); + + Type type = obj.GetType(); + + if (type.IsArray) + return CreateIReferenceArray((Array) obj); + + if (type == typeof(int)) + return new CLRIReferenceImpl<int>(PropertyType.Int32, (int)obj); + if (type == typeof(String)) + return new CLRIReferenceImpl<String>(PropertyType.String, (String)obj); + if (type == typeof(byte)) + return new CLRIReferenceImpl<byte>(PropertyType.UInt8, (byte)obj); + if (type == typeof(short)) + return new CLRIReferenceImpl<short>(PropertyType.Int16, (short)obj); + if (type == typeof(ushort)) + return new CLRIReferenceImpl<ushort>(PropertyType.UInt16, (ushort)obj); + if (type == typeof(uint)) + return new CLRIReferenceImpl<uint>(PropertyType.UInt32, (uint)obj); + if (type == typeof(long)) + return new CLRIReferenceImpl<long>(PropertyType.Int64, (long)obj); + if (type == typeof(ulong)) + return new CLRIReferenceImpl<ulong>(PropertyType.UInt64, (ulong)obj); + if (type == typeof(float)) + return new CLRIReferenceImpl<float>(PropertyType.Single, (float)obj); + if (type == typeof(double)) + return new CLRIReferenceImpl<double>(PropertyType.Double, (double)obj); + if (type == typeof(char)) + return new CLRIReferenceImpl<char>(PropertyType.Char16, (char)obj); + if (type == typeof(bool)) + return new CLRIReferenceImpl<bool>(PropertyType.Boolean, (bool)obj); + if (type == typeof(Guid)) + return new CLRIReferenceImpl<Guid>(PropertyType.Guid, (Guid)obj); + if (type == typeof(DateTimeOffset)) + return new CLRIReferenceImpl<DateTimeOffset>(PropertyType.DateTime, (DateTimeOffset)obj); + if (type == typeof(TimeSpan)) + return new CLRIReferenceImpl<TimeSpan>(PropertyType.TimeSpan, (TimeSpan)obj); + if (type == typeof(Object)) + return new CLRIReferenceImpl<Object>(PropertyType.Inspectable, (Object)obj); + if (type == typeof(RuntimeType)) + { // If the type is System.RuntimeType, we want to use System.Type marshaler (it's parent of the type) + return new CLRIReferenceImpl<Type>(PropertyType.Other, (Type)obj); + } + + // Handle arbitrary WinRT-compatible value types, and recognize a few special types. + PropertyType? propType = null; + if (type == s_pointType) + { + propType = PropertyType.Point; + } + else if (type == s_rectType) + { + propType = PropertyType.Rect; + } + else if (type == s_sizeType) + { + propType = PropertyType.Size; + } + else if (type.IsValueType || obj is Delegate) + { + propType = PropertyType.Other; + } + + if (propType.HasValue) + { + Type specificType = typeof(CLRIReferenceImpl<>).MakeGenericType(type); + return Activator.CreateInstance(specificType, new Object[] { propType.Value, obj }); + } + + Contract.Assert(false, "We should not see non-WinRT type here"); + return null; + } + + [SecuritySafeCritical] + internal static Object CreateIReferenceArray(Array obj) + { + Contract.Requires(obj != null); + Contract.Requires(obj.GetType().IsArray); + Contract.Ensures(Contract.Result<Object>() != null); + + Type type = obj.GetType().GetElementType(); + + Contract.Assert(obj.Rank == 1 && obj.GetLowerBound(0) == 0 && !type.IsArray); + + if (type == typeof(int)) + return new CLRIReferenceArrayImpl<int>(PropertyType.Int32Array, (int[])obj); + if (type == typeof(String)) + return new CLRIReferenceArrayImpl<String>(PropertyType.StringArray, (String[])obj); + if (type == typeof(byte)) + return new CLRIReferenceArrayImpl<byte>(PropertyType.UInt8Array, (byte[])obj); + if (type == typeof(short)) + return new CLRIReferenceArrayImpl<short>(PropertyType.Int16Array, (short[])obj); + if (type == typeof(ushort)) + return new CLRIReferenceArrayImpl<ushort>(PropertyType.UInt16Array, (ushort[])obj); + if (type == typeof(uint)) + return new CLRIReferenceArrayImpl<uint>(PropertyType.UInt32Array, (uint[])obj); + if (type == typeof(long)) + return new CLRIReferenceArrayImpl<long>(PropertyType.Int64Array, (long[])obj); + if (type == typeof(ulong)) + return new CLRIReferenceArrayImpl<ulong>(PropertyType.UInt64Array, (ulong[])obj); + if (type == typeof(float)) + return new CLRIReferenceArrayImpl<float>(PropertyType.SingleArray, (float[])obj); + if (type == typeof(double)) + return new CLRIReferenceArrayImpl<double>(PropertyType.DoubleArray, (double[])obj); + if (type == typeof(char)) + return new CLRIReferenceArrayImpl<char>(PropertyType.Char16Array, (char[])obj); + if (type == typeof(bool)) + return new CLRIReferenceArrayImpl<bool>(PropertyType.BooleanArray, (bool[])obj); + if (type == typeof(Guid)) + return new CLRIReferenceArrayImpl<Guid>(PropertyType.GuidArray, (Guid[])obj); + if (type == typeof(DateTimeOffset)) + return new CLRIReferenceArrayImpl<DateTimeOffset>(PropertyType.DateTimeArray, (DateTimeOffset[])obj); + if (type == typeof(TimeSpan)) + return new CLRIReferenceArrayImpl<TimeSpan>(PropertyType.TimeSpanArray, (TimeSpan[])obj); + if (type == typeof(Type)) + { // Note: The array type will be System.Type, not System.RuntimeType + return new CLRIReferenceArrayImpl<Type>(PropertyType.OtherArray, (Type[])obj); + } + + PropertyType? propType = null; + if (type == s_pointType) + { + propType = PropertyType.PointArray; + } + else if (type == s_rectType) + { + propType = PropertyType.RectArray; + } + else if (type == s_sizeType) + { + propType = PropertyType.SizeArray; + } + else if (type.IsValueType) + { + // note that KeyValuePair`2 is a reference type on the WinRT side so the array + // must be wrapped with CLRIReferenceArrayImpl<Object> + if (type.IsGenericType && + type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)) + { + Object[] objArray = new Object[obj.Length]; + for (int i = 0; i < objArray.Length; i++) + { + objArray[i] = obj.GetValue(i); + } + obj = objArray; + } + else + { + propType = PropertyType.OtherArray; + } + } + else if (typeof(Delegate).IsAssignableFrom(type)) + { + propType = PropertyType.OtherArray; + } + + + if (propType.HasValue) + { + // All WinRT value type will be Property.Other + Type specificType = typeof(CLRIReferenceArrayImpl<>).MakeGenericType(type); + return Activator.CreateInstance(specificType, new Object[] { propType.Value, obj }); + } + else + { + // All WinRT reference type (including arbitary managed type) will be PropertyType.ObjectArray + return new CLRIReferenceArrayImpl<Object>(PropertyType.InspectableArray, (Object[])obj); + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs new file mode 100644 index 0000000000..af1381c366 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; + + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + /// <summary> + /// This is a constant map aimed to efficiently support a Split operation (map decomposition). + /// A Split operation returns two non-overlapping, non-empty views of the existing map (or both + /// values are set to NULL). The two views returned should contain roughly the same number of elements. + /// This map is backed by a sorted array. Thus, split operations are O(1) and enumerations are fast; + /// however, look-up in the map are O(log n). + /// </summary> + /// <typeparam name="TKey">Type of objects that act as keys.</typeparam> + /// <typeparam name="TValue">Type of objects that act as entries / values.</typeparam> + [Serializable] + [DebuggerDisplay("Count = {Count}")] + internal sealed class ConstantSplittableMap<TKey, TValue> : IMapView<TKey, TValue> + { + private class KeyValuePairComparator : IComparer<KeyValuePair<TKey, TValue>> + { + private static readonly IComparer<TKey> keyComparator = Comparer<TKey>.Default; + + public Int32 Compare(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y) + { + return keyComparator.Compare(x.Key, y.Key); + } + } // private class KeyValuePairComparator + + + private static readonly KeyValuePairComparator keyValuePairComparator = new KeyValuePairComparator(); + + private readonly KeyValuePair<TKey, TValue>[] items; + private readonly int firstItemIndex; + private readonly int lastItemIndex; + + internal ConstantSplittableMap(IReadOnlyDictionary<TKey, TValue> data) + { + if (data == null) + throw new ArgumentNullException("data"); + Contract.EndContractBlock(); + + this.firstItemIndex = 0; + this.lastItemIndex = data.Count - 1; + this.items = CreateKeyValueArray(data.Count, data.GetEnumerator()); + } + + internal ConstantSplittableMap(IMapView<TKey, TValue> data) + { + if (data == null) + throw new ArgumentNullException("data"); + + if (((UInt32)Int32.MaxValue) < data.Size) + { + Exception e = new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingDictionaryTooLarge")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + + int size = (int)data.Size; + + this.firstItemIndex = 0; + this.lastItemIndex = size - 1; + this.items = CreateKeyValueArray(size, data.GetEnumerator()); + } + + + private ConstantSplittableMap(KeyValuePair<TKey, TValue>[] items, Int32 firstItemIndex, Int32 lastItemIndex) + { + this.items = items; + this.firstItemIndex = firstItemIndex; + this.lastItemIndex = lastItemIndex; + } + + + private KeyValuePair<TKey, TValue>[] CreateKeyValueArray(Int32 count, IEnumerator<KeyValuePair<TKey, TValue>> data) + { + KeyValuePair<TKey, TValue>[] kvArray = new KeyValuePair<TKey, TValue>[count]; + + Int32 i = 0; + while (data.MoveNext()) + kvArray[i++] = data.Current; + + Array.Sort(kvArray, keyValuePairComparator); + + return kvArray; + } + + private KeyValuePair<TKey, TValue>[] CreateKeyValueArray(Int32 count, IEnumerator<IKeyValuePair<TKey, TValue>> data) + { + KeyValuePair<TKey, TValue>[] kvArray = new KeyValuePair<TKey, TValue>[count]; + + Int32 i = 0; + while (data.MoveNext()) + { + IKeyValuePair<TKey, TValue> current = data.Current; + kvArray[i++] = new KeyValuePair<TKey, TValue>(current.Key, current.Value); + } + + Array.Sort(kvArray, keyValuePairComparator); + + return kvArray; + } + + + public int Count { + get { + return lastItemIndex - firstItemIndex + 1; + } + } + + + // [CLSCompliant(false)] + public UInt32 Size { + get { + return (UInt32)(lastItemIndex - firstItemIndex + 1); + } + } + + + public TValue Lookup(TKey key) + { + TValue value; + bool found = TryGetValue(key, out value); + + if (!found) + { + Exception e = new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + + return value; + } + + + public bool HasKey(TKey key) + { + TValue value; + bool hasKey = TryGetValue(key, out value); + return hasKey; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<IKeyValuePair<TKey, TValue>>)this).GetEnumerator(); + } + + public IIterator<IKeyValuePair<TKey, TValue>> First() + { + return new EnumeratorToIteratorAdapter<IKeyValuePair<TKey, TValue>>(GetEnumerator()); + } + + public IEnumerator<IKeyValuePair<TKey, TValue>> GetEnumerator() + { + return new IKeyValuePairEnumerator(items, firstItemIndex, lastItemIndex); + } + + public void Split(out IMapView<TKey, TValue> firstPartition, out IMapView<TKey, TValue> secondPartition) + { + if (Count < 2) + { + firstPartition = null; + secondPartition = null; + return; + } + + int pivot = (Int32)(((Int64)firstItemIndex + (Int64)lastItemIndex) / (Int64)2); + + firstPartition = new ConstantSplittableMap<TKey, TValue>(items, firstItemIndex, pivot); + secondPartition = new ConstantSplittableMap<TKey, TValue>(items, pivot + 1, lastItemIndex); + } + + #region IReadOnlyDictionary members + + public bool ContainsKey(TKey key) + { + KeyValuePair<TKey, TValue> searchKey = new KeyValuePair<TKey, TValue>(key, default(TValue)); + int index = Array.BinarySearch(items, firstItemIndex, Count, searchKey, keyValuePairComparator); + return index >= 0; + } + + public bool TryGetValue(TKey key, out TValue value) + { + KeyValuePair<TKey, TValue> searchKey = new KeyValuePair<TKey, TValue>(key, default(TValue)); + int index = Array.BinarySearch(items, firstItemIndex, Count, searchKey, keyValuePairComparator); + + if (index < 0) + { + value = default(TValue); + return false; + } + + value = items[index].Value; + return true; + } + + public TValue this[TKey key] { + get { + return Lookup(key); + } + } + + public IEnumerable<TKey> Keys { + get { + throw new NotImplementedException("NYI"); + } + } + + public IEnumerable<TValue> Values { + get { + throw new NotImplementedException("NYI"); + } + } + + #endregion IReadOnlyDictionary members + + #region IKeyValuePair Enumerator + + [Serializable] + internal struct IKeyValuePairEnumerator : IEnumerator<IKeyValuePair<TKey, TValue>> + { + private KeyValuePair<TKey, TValue>[] _array; + private int _start; + private int _end; + private int _current; + + internal IKeyValuePairEnumerator(KeyValuePair<TKey, TValue>[] items, int first, int end) + { + Contract.Requires(items != null); + Contract.Requires(first >= 0); + Contract.Requires(end >= 0); + Contract.Requires(first < items.Length); + Contract.Requires(end < items.Length); + + _array = items; + _start = first; + _end = end; + _current = _start - 1; + } + + public bool MoveNext() + { + if (_current < _end) + { + _current++; + return true; + } + return false; + } + + public IKeyValuePair<TKey, TValue> Current { + get { + if (_current < _start) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted)); + if (_current > _end) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded)); + return new CLRIKeyValuePairImpl<TKey, TValue>(ref _array[_current]); + } + } + + Object IEnumerator.Current { + get { + return Current; + } + } + + void IEnumerator.Reset() + { + _current = _start - 1; + } + + public void Dispose() + { + } + } + + #endregion IKeyValuePair Enumerator + + } // internal ConstantSplittableMap<TKey, TValue> + +} // namespace System.Runtime.InteropServices.WindowsRuntime diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CustomPropertyImpl.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CustomPropertyImpl.cs new file mode 100644 index 0000000000..04fe1bf9b2 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CustomPropertyImpl.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Reflection; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.StubHelpers; +using System.Globalization; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // + // ICustomProperty implementation - basically a wrapper of PropertyInfo + // + internal sealed class CustomPropertyImpl : ICustomProperty + { + private PropertyInfo m_property; + + // + // Constructor + // + public CustomPropertyImpl(PropertyInfo propertyInfo) + { + if (propertyInfo == null) + throw new ArgumentNullException("propertyInfo"); + + m_property = propertyInfo; + } + + // + // ICustomProperty interface implementation + // + + public string Name + { + get + { + return m_property.Name; + } + } + + public bool CanRead + { + get + { + // Return false if the getter is not public + return m_property.GetGetMethod() != null; + } + } + + public bool CanWrite + { + get + { + // Return false if the setter is not public + return m_property.GetSetMethod() != null; + } + } + + public object GetValue(object target) + { + return InvokeInternal(target, null, true); + } + + // Unlike normal .Net, Jupiter properties can have at most one indexer parameter. A null + // indexValue here means that the property has an indexer argument and its value is null. + public object GetValue(object target, object indexValue) + { + return InvokeInternal(target, new object[] { indexValue }, true); + } + + public void SetValue(object target, object value) + { + InvokeInternal(target, new object[] { value }, false); + } + + // Unlike normal .Net, Jupiter properties can have at most one indexer parameter. A null + // indexValue here means that the property has an indexer argument and its value is null. + public void SetValue(object target, object value, object indexValue) + { + InvokeInternal(target, new object[] { indexValue, value }, false); + } + + [SecuritySafeCritical] + private object InvokeInternal(object target, object[] args, bool getValue) + { + // Forward to the right object if we are dealing with a proxy + IGetProxyTarget proxy = target as IGetProxyTarget; + if (proxy != null) + { + target = proxy.GetTarget(); + } + + // You can get PropertyInfo for properties with a private getter/public setter (or vice versa) + // even if you pass BindingFlags.Public only. And in this case, passing binding flags to + // GetValue/SetValue won't work as the default binder ignores those values + // Use GetGetMethod/GetSetMethod instead + + // We get non-public accessors just so that we can throw the correct exception. + MethodInfo accessor = getValue ? m_property.GetGetMethod(true) : m_property.GetSetMethod(true); + + if (accessor == null) + throw new ArgumentException(System.Environment.GetResourceString(getValue ? "Arg_GetMethNotFnd" : "Arg_SetMethNotFnd")); + + if (!accessor.IsPublic) + throw new MethodAccessException( + String.Format( + CultureInfo.CurrentCulture, + Environment.GetResourceString("Arg_MethodAccessException_WithMethodName"), + accessor.ToString(), + accessor.DeclaringType.FullName)); + + RuntimeMethodInfo rtMethod = accessor as RuntimeMethodInfo; + if (rtMethod == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeMethodInfo")); + + // We can safely skip access check because this is only used in full trust scenarios. + // And we have already verified that the property accessor is public. + Contract.Assert(AppDomain.CurrentDomain.PermissionSet.IsUnrestricted()); + return rtMethod.UnsafeInvoke(target, BindingFlags.Default, null, args, null); + } + + public Type Type + { + get + { + return m_property.PropertyType; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryKeyCollection.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryKeyCollection.cs new file mode 100644 index 0000000000..c1586ee9ce --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryKeyCollection.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [Serializable] + [DebuggerDisplay("Count = {Count}")] + internal sealed class DictionaryKeyCollection<TKey, TValue> : ICollection<TKey> + { + private readonly IDictionary<TKey, TValue> dictionary; + + public DictionaryKeyCollection(IDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + } + + public void CopyTo(TKey[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + if (array.Length <= index && this.Count > 0) + throw new ArgumentException(Environment.GetResourceString("Arg_IndexOutOfRangeException")); + if (array.Length - index < dictionary.Count) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + int i = index; + foreach (KeyValuePair<TKey, TValue> mapping in dictionary) + { + array[i++] = mapping.Key; + } + } + + public int Count { + get { return dictionary.Count; } + } + + bool ICollection<TKey>.IsReadOnly { + get { return true; } + } + + void ICollection<TKey>.Add(TKey item) + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_KeyCollectionSet")); + } + + void ICollection<TKey>.Clear() + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_KeyCollectionSet")); + } + + public bool Contains(TKey item) + { + return dictionary.ContainsKey(item); + } + + bool ICollection<TKey>.Remove(TKey item) + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_KeyCollectionSet")); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<TKey>)this).GetEnumerator(); + } + + public IEnumerator<TKey> GetEnumerator() + { + return new DictionaryKeyEnumerator<TKey, TValue>(dictionary); + } + } // public class DictionaryKeyCollection<TKey, TValue> + + + [Serializable] + internal sealed class DictionaryKeyEnumerator<TKey, TValue> : IEnumerator<TKey> + { + private readonly IDictionary<TKey, TValue> dictionary; + private IEnumerator<KeyValuePair<TKey, TValue>> enumeration; + + public DictionaryKeyEnumerator(IDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + this.enumeration = dictionary.GetEnumerator(); + } + + void IDisposable.Dispose() + { + enumeration.Dispose(); + } + + public bool MoveNext() + { + return enumeration.MoveNext(); + } + + Object IEnumerator.Current { + get { return ((IEnumerator<TKey>)this).Current; } + } + + public TKey Current { + get { return enumeration.Current.Key; } + } + + public void Reset() + { + enumeration = dictionary.GetEnumerator(); + } + } // class DictionaryKeyEnumerator<TKey, TValue> +} + +// DictionaryKeyCollection.cs diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs new file mode 100644 index 0000000000..fa021b7f3d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IMap`2 interface on managed + // objects that implement IDictionary`2. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not DictionaryToMapAdapter objects. Rather, they are of type + // IDictionary<K, V>. No actual DictionaryToMapAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IDictionary<K, V>". + internal sealed class DictionaryToMapAdapter + { + private DictionaryToMapAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // V Lookup(K key) + [SecurityCritical] + internal V Lookup<K, V>(K key) + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + V value; + bool keyFound = _this.TryGetValue(key, out value); + + if (!keyFound) + { + Exception e = new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + + return value; + } + + // uint Size { get } + [SecurityCritical] + internal uint Size<K, V>() + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + return (uint)_this.Count; + } + + // bool HasKey(K key) + [SecurityCritical] + internal bool HasKey<K, V>(K key) + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + return _this.ContainsKey(key); + } + + // IMapView<K, V> GetView() + [SecurityCritical] + internal IReadOnlyDictionary<K, V> GetView<K, V>() + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + Contract.Assert(_this != null); + + // Note: This dictionary is not really read-only - you could QI for a modifiable + // dictionary. We gain some perf by doing this. We believe this is acceptable. + IReadOnlyDictionary<K, V> roDictionary = _this as IReadOnlyDictionary<K, V>; + if (roDictionary == null) + { + roDictionary = new ReadOnlyDictionary<K, V>(_this); + } + return roDictionary; + } + + // bool Insert(K key, V value) + [SecurityCritical] + internal bool Insert<K, V>(K key, V value) + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + bool replacing = _this.ContainsKey(key); + _this[key] = value; + return replacing; + } + + // void Remove(K key) + [SecurityCritical] + internal void Remove<K, V>(K key) + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + bool removed = _this.Remove(key); + + if (!removed) + { + Exception e = new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + } + + // void Clear() + [SecurityCritical] + internal void Clear<K, V>() + { + IDictionary<K, V> _this = JitHelpers.UnsafeCast<IDictionary<K, V>>(this); + _this.Clear(); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryValueCollection.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryValueCollection.cs new file mode 100644 index 0000000000..03e897a917 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryValueCollection.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; + + +namespace System.Runtime.InteropServices.WindowsRuntime { + [Serializable] + [DebuggerDisplay("Count = {Count}")] + internal sealed class DictionaryValueCollection<TKey, TValue> : ICollection<TValue> + { + private readonly IDictionary<TKey, TValue> dictionary; + + public DictionaryValueCollection(IDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + } + + public void CopyTo(TValue[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + if (array.Length <= index && this.Count > 0) + throw new ArgumentException(Environment.GetResourceString("Arg_IndexOutOfRangeException")); + if (array.Length - index < dictionary.Count) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + int i = index; + foreach (KeyValuePair<TKey, TValue> mapping in dictionary) + { + array[i++] = mapping.Value; + } + } + + public int Count { + get { return dictionary.Count; } + } + + bool ICollection<TValue>.IsReadOnly { + get { return true; } + } + + void ICollection<TValue>.Add(TValue item) + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_ValueCollectionSet")); + } + + void ICollection<TValue>.Clear() + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_ValueCollectionSet")); + } + + public bool Contains(TValue item) + { + EqualityComparer<TValue> comparer = EqualityComparer<TValue>.Default; + foreach (TValue value in this) + if (comparer.Equals(item, value)) + return true; + return false; + } + + bool ICollection<TValue>.Remove(TValue item) + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_ValueCollectionSet")); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<TValue>)this).GetEnumerator(); + } + + public IEnumerator<TValue> GetEnumerator() + { + return new DictionaryValueEnumerator<TKey, TValue>(dictionary); + } + } // public class DictionaryValueCollection<TKey, TValue> + + + [Serializable] + internal sealed class DictionaryValueEnumerator<TKey, TValue> : IEnumerator<TValue> + { + private readonly IDictionary<TKey, TValue> dictionary; + private IEnumerator<KeyValuePair<TKey, TValue>> enumeration; + + public DictionaryValueEnumerator(IDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + this.enumeration = dictionary.GetEnumerator(); + } + + void IDisposable.Dispose() + { + enumeration.Dispose(); + } + + public bool MoveNext() + { + return enumeration.MoveNext(); + } + + Object IEnumerator.Current { + get { return ((IEnumerator<TValue>)this).Current; } + } + + public TValue Current { + get { return enumeration.Current.Value; } + } + + public void Reset() + { + enumeration = dictionary.GetEnumerator(); + } + } // class DictionaryValueEnumerator<TKey, TValue> +} + +// DictionaryValueCollection.cs diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs new file mode 100644 index 0000000000..7329d31ae8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IIterable`1 interface on managed + // objects that implement IEnumerable`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not EnumerableToIterableAdapter objects. Rather, they are of type + // IEnumerable<T>. No actual EnumerableToIterableAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IEnumerable<T>". + internal sealed class EnumerableToIterableAdapter + { + private EnumerableToIterableAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // This method is invoked when First is called on a managed implementation of IIterable<T>. + [System.Security.SecurityCritical] + internal IIterator<T> First_Stub<T>() + { + IEnumerable<T> _this = JitHelpers.UnsafeCast<IEnumerable<T>>(this); + return new EnumeratorToIteratorAdapter<T>(_this.GetEnumerator()); + } + } + + internal sealed class EnumerableToBindableIterableAdapter + { + private EnumerableToBindableIterableAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + internal sealed class NonGenericToGenericEnumerator : IEnumerator<object> + { + private IEnumerator enumerator; + + public NonGenericToGenericEnumerator(IEnumerator enumerator) + { this.enumerator = enumerator; } + + public object Current { get { return enumerator.Current; } } + public bool MoveNext() { return enumerator.MoveNext(); } + public void Reset() { enumerator.Reset(); } + public void Dispose() { } + } + + // This method is invoked when First is called on a managed implementation of IBindableIterable. + [System.Security.SecurityCritical] + internal IBindableIterator First_Stub() + { + IEnumerable _this = JitHelpers.UnsafeCast<IEnumerable>(this); + return new EnumeratorToIteratorAdapter<object>(new NonGenericToGenericEnumerator(_this.GetEnumerator()) ); + } + } + + // Adapter class which holds a managed IEnumerator<T>, exposing it as a Windows Runtime IIterator<T> + internal sealed class EnumeratorToIteratorAdapter<T> : IIterator<T>, IBindableIterator + { + private IEnumerator<T> m_enumerator; + private bool m_firstItem = true; + private bool m_hasCurrent; + + internal EnumeratorToIteratorAdapter(IEnumerator<T> enumerator) + { + Contract.Requires(enumerator != null); + m_enumerator = enumerator; + } + + public T Current + { + get + { + // IEnumerator starts at item -1, while IIterators start at item 0. Therefore, if this is the + // first access to the iterator we need to advance to the first item. + if (m_firstItem) + { + m_firstItem = false; + MoveNext(); + } + + if (!m_hasCurrent) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, null); + } + + return m_enumerator.Current; + } + } + + object IBindableIterator.Current + { + get + { + return (object)((IIterator<T>)this).Current; + } + } + + public bool HasCurrent + { + get + { + // IEnumerator starts at item -1, while IIterators start at item 0. Therefore, if this is the + // first access to the iterator we need to advance to the first item. + if (m_firstItem) + { + m_firstItem = false; + MoveNext(); + } + + return m_hasCurrent; + } + } + + public bool MoveNext() + { + try + { + m_hasCurrent = m_enumerator.MoveNext(); + } + catch (InvalidOperationException e) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_CHANGED_STATE, e); + } + + return m_hasCurrent; + } + + public int GetMany(T[] items) + { + if (items == null) + { + return 0; + } + + int index = 0; + while (index < items.Length && HasCurrent) + { + items[index] = Current; + MoveNext(); + ++index; + } + + if (typeof(T) == typeof(string)) + { + string[] stringItems = items as string[]; + + // Fill the rest of the array with String.Empty to avoid marshaling failure + for (int i = index; i < items.Length; ++i) + stringItems[i] = String.Empty; + } + + return index; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationToken.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationToken.cs new file mode 100644 index 0000000000..e2b53277eb --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationToken.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // Event registration tokens are 64 bit opaque structures returned from WinRT style event adders, in order + // to signify a registration of a particular delegate to an event. The token's only real use is to + // unregister the same delgate from the event at a later time. + public struct EventRegistrationToken + { + internal ulong m_value; + + internal EventRegistrationToken(ulong value) + { + m_value = value; + } + + internal ulong Value + { + get { return m_value; } + } + + public static bool operator ==(EventRegistrationToken left, EventRegistrationToken right) + { + return left.Equals(right); + } + + public static bool operator !=(EventRegistrationToken left, EventRegistrationToken right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (!(obj is EventRegistrationToken)) + { + return false; + } + + return ((EventRegistrationToken)obj).Value == Value; + } + + public override int GetHashCode() + { + return m_value.GetHashCode(); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs new file mode 100644 index 0000000000..03b17d9261 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EventRegistrationTokenTable.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Threading; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // An event registration token table stores mappings from delegates to event tokens, in order to support + // sourcing WinRT style events from managed code. + public sealed class EventRegistrationTokenTable<T> where T : class + { + // Note this dictionary is also used as the synchronization object for this table + private Dictionary<EventRegistrationToken, T> m_tokens = new Dictionary<EventRegistrationToken, T>(); + + // Cached multicast delegate which will invoke all of the currently registered delegates. This + // will be accessed frequently in common coding paterns, so we don't want to calculate it repeatedly. + private volatile T m_invokeList; + + public EventRegistrationTokenTable() + { + // T must be a delegate type, but we cannot constrain on being a delegate. Therefore, we'll do a + // static check at construction time + if (!typeof(Delegate).IsAssignableFrom(typeof(T))) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EventTokenTableRequiresDelegate", typeof(T))); + } + } + + // The InvocationList property provides access to a delegate which will invoke every registered event handler + // in this table. If the property is set, the new value will replace any existing token registrations. + public T InvocationList + { + get + { + return m_invokeList; + } + + set + { + lock (m_tokens) + { + // The value being set replaces any of the existing values + m_tokens.Clear(); + m_invokeList = null; + + if (value != null) + { + AddEventHandlerNoLock(value); + } + } + } + } + + public EventRegistrationToken AddEventHandler(T handler) + { + // Windows Runtime allows null handlers. Assign those a token value of 0 for easy identity + if (handler == null) + { + return new EventRegistrationToken(0); + } + + lock (m_tokens) + { + return AddEventHandlerNoLock(handler); + } + } + + private EventRegistrationToken AddEventHandlerNoLock(T handler) + { + Contract.Requires(handler != null); + + // Get a registration token, making sure that we haven't already used the value. This should be quite + // rare, but in the case it does happen, just keep trying until we find one that's unused. + EventRegistrationToken token = GetPreferredToken(handler); + while (m_tokens.ContainsKey(token)) + { + token = new EventRegistrationToken(token.Value + 1); + } + m_tokens[token] = handler; + + // Update the current invocation list to include the newly added delegate + Delegate invokeList = (Delegate)(object)m_invokeList; + invokeList = MulticastDelegate.Combine(invokeList, (Delegate)(object)handler); + m_invokeList = (T)(object)invokeList; + + return token; + } + + // Get the delegate associated with an event registration token if it exists. Additionally, + // remove the registration from the table at the same time. If the token is not registered, + // Extract returns null and does not modify the table. + [System.Runtime.CompilerServices.FriendAccessAllowed] + internal T ExtractHandler(EventRegistrationToken token) + { + T handler = null; + lock (m_tokens) + { + if (m_tokens.TryGetValue(token, out handler)) + { + RemoveEventHandlerNoLock(token); + } + } + + return handler; + } + + // Generate a token that may be used for a particular event handler. We will frequently be called + // upon to look up a token value given only a delegate to start from. Therefore, we want to make + // an initial token value that is easily determined using only the delegate instance itself. Although + // in the common case this token value will be used to uniquely identify the handler, it is not + // the only possible token that can represent the handler. + // + // This means that both: + // * if there is a handler assigned to the generated initial token value, it is not necessarily + // this handler. + // * if there is no handler assigned to the generated initial token value, the handler may still + // be registered under a different token + // + // Effectively the only reasonable thing to do with this value is either to: + // 1. Use it as a good starting point for generating a token for handler + // 2. Use it as a guess to quickly see if the handler was really assigned this token value + private static EventRegistrationToken GetPreferredToken(T handler) + { + Contract.Requires(handler != null); + + // We want to generate a token value that has the following properties: + // 1. is quickly obtained from the handler instance + // 2. uses bits in the upper 32 bits of the 64 bit value, in order to avoid bugs where code + // may assume the value is realy just 32 bits + // 3. uses bits in the bottom 32 bits of the 64 bit value, in order to ensure that code doesn't + // take a dependency on them always being 0. + // + // The simple algorithm chosen here is to simply assign the upper 32 bits the metadata token of the + // event handler type, and the lower 32 bits the hash code of the handler instance itself. Using the + // metadata token for the upper 32 bits gives us at least a small chance of being able to identify a + // totally corrupted token if we ever come across one in a minidump or other scenario. + // + // The hash code of a unicast delegate is not tied to the method being invoked, so in the case + // of a unicast delegate, the hash code of the target method is used instead of the full delegate + // hash code. + // + // While calculating this initial value will be somewhat more expensive than just using a counter + // for events that have few registrations, it will also gives us a shot at preventing unregistration + // from becoming an O(N) operation. + // + // We should feel free to change this algorithm as other requirements / optimizations become + // available. This implementation is sufficiently random that code cannot simply guess the value to + // take a dependency upon it. (Simply applying the hash-value algorithm directly won't work in the + // case of collisions, where we'll use a different token value). + + uint handlerHashCode = 0; + Delegate[] invocationList = ((Delegate)(object)handler).GetInvocationList(); + if (invocationList.Length == 1) + { + handlerHashCode = (uint)invocationList[0].Method.GetHashCode(); + } + else + { + handlerHashCode = (uint)handler.GetHashCode(); + } + + ulong tokenValue = ((ulong)(uint)typeof(T).MetadataToken << 32) | handlerHashCode; + return new EventRegistrationToken(tokenValue); + } + + public void RemoveEventHandler(EventRegistrationToken token) + { + // The 0 token is assigned to null handlers, so there's nothing to do + if (token.Value == 0) + { + return; + } + + lock (m_tokens) + { + RemoveEventHandlerNoLock(token); + } + } + + public void RemoveEventHandler(T handler) + { + // To match the Windows Runtime behaivor when adding a null handler, removing one is a no-op + if (handler == null) + { + return; + } + + lock (m_tokens) + { + // Fast path - if the delegate is stored with its preferred token, then there's no need to do + // a full search of the table for it. Note that even if we find something stored using the + // preferred token value, it's possible we have a collision and another delegate was using that + // value. Therefore we need to make sure we really have the handler we want before taking the + // fast path. + EventRegistrationToken preferredToken = GetPreferredToken(handler); + T registeredHandler; + if (m_tokens.TryGetValue(preferredToken, out registeredHandler)) + { + if (registeredHandler == handler) + { + RemoveEventHandlerNoLock(preferredToken); + return; + } + } + + // Slow path - we didn't find the delegate with its preferred token, so we need to fall + // back to a search of the table + foreach (KeyValuePair<EventRegistrationToken, T> registration in m_tokens) + { + if (registration.Value == (T)(object)handler) + { + RemoveEventHandlerNoLock(registration.Key); + + // If a delegate has been added multiple times to handle an event, then it + // needs to be removed the same number of times to stop handling the event. + // Stop after the first one we find. + return; + } + } + + // Note that falling off the end of the loop is not an error, as removing a registration + // for a handler that is not currently registered is simply a no-op + } + } + + private void RemoveEventHandlerNoLock(EventRegistrationToken token) + { + T handler; + if (m_tokens.TryGetValue(token, out handler)) + { + m_tokens.Remove(token); + + // Update the current invocation list to remove the delegate + Delegate invokeList = (Delegate)(object)m_invokeList; + invokeList = MulticastDelegate.Remove(invokeList, (Delegate)(object)handler); + m_invokeList = (T)(object)invokeList; + } + } + + public static EventRegistrationTokenTable<T> GetOrCreateEventRegistrationTokenTable(ref EventRegistrationTokenTable<T> refEventTable) + { + if (refEventTable == null) + { + Interlocked.CompareExchange(ref refEventTable, new EventRegistrationTokenTable<T>(), null); + } + return refEventTable; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IActivationFactory.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IActivationFactory.cs new file mode 100644 index 0000000000..0ce01f8653 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IActivationFactory.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Runtime.InteropServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("00000035-0000-0000-C000-000000000046")] + [WindowsRuntimeImport] + public interface IActivationFactory + { + object ActivateInstance(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IClosable.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IClosable.cs new file mode 100644 index 0000000000..847147ade8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IClosable.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + + // Local definition of Windows.Foundation.IClosable + [ComImport] + [Guid("30d5a829-7fa4-4026-83bb-d75bae4ea99e")] + [WindowsRuntimeImport] + internal interface IClosable + { + void Close(); + } + + // Adapter class - converts IClosable.Close calls to Disposable.Dispose + internal sealed class IDisposableToIClosableAdapter + { + private IDisposableToIClosableAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + [SecurityCritical] + public void Close() + { + IDisposable _this = JitHelpers.UnsafeCast<IDisposable>(this); + _this.Dispose(); + } + } + + // Adapter class which converts IDisposable.Dispose calls into IClosable.Close + [SecurityCritical] + internal sealed class IClosableToIDisposableAdapter + { + private IClosableToIDisposableAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + [SecurityCritical] + private void Dispose() + { + IClosable _this = JitHelpers.UnsafeCast<IClosable>(this); + _this.Close(); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ICustomProperty.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ICustomProperty.cs new file mode 100644 index 0000000000..88472a46b8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ICustomProperty.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Diagnostics.Contracts; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")] + [WindowsRuntimeImport] + internal interface ICustomProperty + { + Type Type + { + [Pure] + get; + } + + string Name + { + [Pure] + get; + } + + [Pure] + object GetValue(object target); + + void SetValue(object target, object value); + + [Pure] + object GetValue(object target, object indexValue); + + void SetValue(object target, object value, object indexValue); + + bool CanWrite + { + [Pure] + get; + } + + bool CanRead + { + [Pure] + get; + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ICustomPropertyProvider.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ICustomPropertyProvider.cs new file mode 100644 index 0000000000..143a33e4c7 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ICustomPropertyProvider.cs @@ -0,0 +1,553 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.StubHelpers; +using System.Reflection; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + + // + // ICustomProperty Implementation helpers + // + internal static class ICustomPropertyProviderImpl + { + // + // Creates a ICustomProperty implementation for Jupiter + // Called from ICustomPropertyProvider_GetProperty from within runtime + // + static internal ICustomProperty CreateProperty(object target, string propertyName) + { + Contract.Requires(target != null); + Contract.Requires(propertyName != null); + + IGetProxyTarget proxy = target as IGetProxyTarget; + if (proxy != null) + target = proxy.GetTarget(); + + // Only return public instance/static properties + PropertyInfo propertyInfo = target.GetType().GetProperty( + propertyName, + BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + + if (propertyInfo == null) + return null; + else + return new CustomPropertyImpl(propertyInfo); + } + + // + // Creates a ICustomProperty implementation for Jupiter + // Called from ICustomPropertyProvider_GetIndexedProperty from within runtime + // + [System.Security.SecurityCritical] + static internal unsafe ICustomProperty CreateIndexedProperty(object target, string propertyName, TypeNameNative *pIndexedParamType) + { + Contract.Requires(target != null); + Contract.Requires(propertyName != null); + + Type indexedParamType = null; + SystemTypeMarshaler.ConvertToManaged(pIndexedParamType, ref indexedParamType); + + return CreateIndexedProperty(target, propertyName, indexedParamType); + } + + static internal ICustomProperty CreateIndexedProperty(object target, string propertyName, Type indexedParamType) + { + Contract.Requires(target != null); + Contract.Requires(propertyName != null); + + IGetProxyTarget proxy = target as IGetProxyTarget; + if (proxy != null) + target = proxy.GetTarget(); + + // Only return public instance/static properties + PropertyInfo propertyInfo = target.GetType().GetProperty( + propertyName, + BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, + null, // default binder + null, // ignore return type + new Type[] { indexedParamType }, // indexed parameter type + null // ignore type modifier + ); + + if (propertyInfo == null) + return null; + else + return new CustomPropertyImpl(propertyInfo); + } + + [System.Security.SecurityCritical] + static internal unsafe void GetType(object target, TypeNameNative *pIndexedParamType) + { + IGetProxyTarget proxy = target as IGetProxyTarget; + if (proxy != null) + target = proxy.GetTarget(); + + SystemTypeMarshaler.ConvertToNative(target.GetType(), pIndexedParamType); + } + } + + [Flags] + enum InterfaceForwardingSupport + { + None = 0, + IBindableVector = 0x1, // IBindableVector -> IBindableVector + IVector = 0x2, // IBindableVector -> IVector<T> + IBindableVectorView = 0x4, // IBindableVectorView -> IBindableVectorView + IVectorView = 0x8, // IBindableVectorView -> IVectorView<T> + IBindableIterableOrIIterable= 0x10 // IBindableIterable -> IBindableIterable/IIterable<T> + } + + // + // Interface for data binding code (CustomPropertyImpl) to retreive the target object + // See CustomPropertyImpl.InvokeInternal for details + // + internal interface IGetProxyTarget + { + object GetTarget(); + } + + // + // Proxy that supports data binding on another object + // + // This serves two purposes: + // + // 1. Delegate data binding interfaces to another object + // Note that this proxy implements the native interfaces directly to avoid unnecessary overhead + // (such as the adapter code that addresses behavior differences between IBindableVector & List + // as well as simplify forwarding code (except for IEnumerable) + // + // 2. ICLRServices.CreateManagedReference will hand out ICCW* of a new instance of this object + // and will hold the other object alive + // + // + internal class ICustomPropertyProviderProxy<T1, T2> : IGetProxyTarget, + ICustomQueryInterface, + IEnumerable, // IBindableIterable -> IBindableIterable/IIterable<T> + IBindableVector, // IBindableVector -> IBindableVector/IVector<T> + IBindableVectorView // IBindableVectorView -> IBindableVectorView/IVectorView<T> + { + private object _target; + private InterfaceForwardingSupport _flags; + + internal ICustomPropertyProviderProxy(object target, InterfaceForwardingSupport flags) + { + _target = target; + _flags = flags; + } + + // + // Creates a new instance of ICustomPropertyProviderProxy<T1, T2> and assign appropriate + // flags + // + internal static object CreateInstance(object target) + { + InterfaceForwardingSupport supportFlags = InterfaceForwardingSupport.None; + + // + // QI and figure out the right flags + // + if (target as IList != null) + supportFlags |= InterfaceForwardingSupport.IBindableVector; + + // NOTE: We need to use the directed type here + // If we use IVector_Raw<T1> here, it derives from a different IIterable<T> which the runtime + // doesn't recognize, and therefore IEnumerable cast won't be able to take advantage of this QI + if (target as IList<T1> != null) + supportFlags |= InterfaceForwardingSupport.IVector; + + if (target as IBindableVectorView != null) + supportFlags |= InterfaceForwardingSupport.IBindableVectorView; + + // NOTE: We need to use the redirected type here + // If we use IVector_Raw<T1> here, it derives from a different IIterable<T> which the runtime + // doesn't recognize, and therefore IEnumerable cast won't be able to take advantage of this QI + if (target as IReadOnlyList<T2> != null) + supportFlags |= InterfaceForwardingSupport.IVectorView; + + // Verify IEnumerable last because the first few QIs might succeed and we need + // IEnumerable cast to use that cache (instead of having ICustomPropertyProvider to + // forward it manually) + // For example, if we try to shoot in the dark by trying IVector<IInspectable> and it + // succeeded, IEnumerable needs to know that + if (target as IEnumerable != null) + supportFlags |= InterfaceForwardingSupport.IBindableIterableOrIIterable; + + return new ICustomPropertyProviderProxy<T1, T2>(target, supportFlags); + } + + + // + // override ToString() to make sure callers get correct IStringable.ToString() behavior in native code + // + public override string ToString() + { + return WindowsRuntime.IStringableHelper.ToString(_target); + } + + // + // IGetProxyTarget - unwraps the target object and use it for data binding + // + object IGetProxyTarget.GetTarget() + { + return _target; + } + + // + // ICustomQueryInterface methods + // + [System.Security.SecurityCritical] + public CustomQueryInterfaceResult GetInterface([In]ref Guid iid, out IntPtr ppv) + { + ppv = IntPtr.Zero; + + if (iid == typeof(IBindableIterable).GUID) + { + // Reject the QI if target doesn't implement IEnumerable + if ((_flags & (InterfaceForwardingSupport.IBindableIterableOrIIterable)) == 0) + return CustomQueryInterfaceResult.Failed; + } + + if (iid == typeof(IBindableVector).GUID) + { + // Reject the QI if target doesn't implement IBindableVector/IVector + if ((_flags & (InterfaceForwardingSupport.IBindableVector | InterfaceForwardingSupport.IVector)) == 0) + return CustomQueryInterfaceResult.Failed; + } + + if (iid == typeof(IBindableVectorView).GUID) + { + // Reject the QI if target doesn't implement IBindableVectorView/IVectorView + if ((_flags & (InterfaceForwardingSupport.IBindableVectorView | InterfaceForwardingSupport.IVectorView)) == 0) + return CustomQueryInterfaceResult.Failed; + } + + return CustomQueryInterfaceResult.NotHandled; + } + + // + // IEnumerable methods + // + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_target).GetEnumerator(); + } + + // + // IBindableVector implementation (forwards to IBindableVector / IVector<T>) + // + [Pure] + object IBindableVector.GetAt(uint index) + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + return bindableVector.GetAt(index); + } + else + { + // IBindableVector -> IVector<T> + return GetVectorOfT().GetAt(index); + } + } + + [Pure] + uint IBindableVector.Size + { + get + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + return bindableVector.Size; + } + else + { + // IBindableVector -> IVector<T> + return GetVectorOfT().Size; + } + } + } + + [Pure] + IBindableVectorView IBindableVector.GetView() + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + return bindableVector.GetView(); + } + else + { + // IBindableVector -> IVector<T> + return new IVectorViewToIBindableVectorViewAdapter<T1>(GetVectorOfT().GetView()); + } + } + + private sealed class IVectorViewToIBindableVectorViewAdapter<T> : IBindableVectorView + { + private IVectorView<T> _vectorView; + + public IVectorViewToIBindableVectorViewAdapter(IVectorView<T> vectorView) + { + this._vectorView = vectorView; + } + + [Pure] + object IBindableVectorView.GetAt(uint index) + { + return _vectorView.GetAt(index); + } + + [Pure] + uint IBindableVectorView.Size + { + get + { + return _vectorView.Size; + } + } + + [Pure] + bool IBindableVectorView.IndexOf(object value, out uint index) + { + return _vectorView.IndexOf(ConvertTo<T>(value), out index); + } + + IBindableIterator IBindableIterable.First() + { + return new IteratorOfTToIteratorAdapter<T>(_vectorView.First()); + } + + } + + [Pure] + bool IBindableVector.IndexOf(object value, out uint index) + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + return bindableVector.IndexOf(value, out index); + } + else + { + // IBindableVector -> IVector<T> + return GetVectorOfT().IndexOf(ConvertTo<T1>(value), out index); + } + } + + void IBindableVector.SetAt(uint index, object value) + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + bindableVector.SetAt(index, value); + } + else + { + // IBindableVector -> IVector<T> + GetVectorOfT().SetAt(index, ConvertTo<T1>(value)); + } + } + + void IBindableVector.InsertAt(uint index, object value) + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + bindableVector.InsertAt(index, value); + } + else + { + // IBindableVector -> IVector<T> + GetVectorOfT().InsertAt(index, ConvertTo<T1>(value)); + } + } + + void IBindableVector.RemoveAt(uint index) + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + bindableVector.RemoveAt(index); + } + else + { + // IBindableVector -> IVector<T> + GetVectorOfT().RemoveAt(index); + } + } + + void IBindableVector.Append(object value) + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + bindableVector.Append(value); + } + else + { + // IBindableVector -> IVector<T> + GetVectorOfT().Append(ConvertTo<T1>(value)); + } + } + + void IBindableVector.RemoveAtEnd() + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + bindableVector.RemoveAtEnd(); + } + else + { + // IBindableVector -> IVector<T> + GetVectorOfT().RemoveAtEnd(); + } + } + + void IBindableVector.Clear() + { + IBindableVector bindableVector = GetIBindableVectorNoThrow(); + if (bindableVector != null) + { + // IBindableVector -> IBindableVector + bindableVector.Clear(); + } + else + { + // IBindableVector -> IVector<T> + GetVectorOfT().Clear(); + } + } + + [SecuritySafeCritical] + private IBindableVector GetIBindableVectorNoThrow() + { + if ((_flags & InterfaceForwardingSupport.IBindableVector) != 0) + return JitHelpers.UnsafeCast<IBindableVector>(_target); + else + return null; + } + + [SecuritySafeCritical] + private IVector_Raw<T1> GetVectorOfT() + { + if ((_flags & InterfaceForwardingSupport.IVector) != 0) + return JitHelpers.UnsafeCast<IVector_Raw<T1>>(_target); + else + throw new InvalidOperationException(); // We should not go down this path, unless Jupiter pass this out to managed code + // and managed code use reflection to do the cast + } + + // + // IBindableVectorView implementation (forwarding to IBindableVectorView or IVectorView<T>) + // + [Pure] + object IBindableVectorView.GetAt(uint index) + { + IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow(); + if (bindableVectorView != null) + return bindableVectorView.GetAt(index); + else + return GetVectorViewOfT().GetAt(index); + } + + [Pure] + uint IBindableVectorView.Size + { + get + { + IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow(); + if (bindableVectorView != null) + return bindableVectorView.Size; + else + return GetVectorViewOfT().Size; + } + } + + [Pure] + bool IBindableVectorView.IndexOf(object value, out uint index) + { + IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow(); + if (bindableVectorView != null) + return bindableVectorView.IndexOf(value, out index); + else + return GetVectorViewOfT().IndexOf(ConvertTo<T2>(value), out index); + } + + IBindableIterator IBindableIterable.First() + { + IBindableVectorView bindableVectorView = GetIBindableVectorViewNoThrow(); + if (bindableVectorView != null) + return bindableVectorView.First(); + else + return new IteratorOfTToIteratorAdapter<T2>(GetVectorViewOfT().First()); + } + + private sealed class IteratorOfTToIteratorAdapter<T> : IBindableIterator + { + private IIterator<T> _iterator; + + public IteratorOfTToIteratorAdapter(IIterator<T> iterator) + { this._iterator = iterator; } + + public bool HasCurrent { get { return _iterator.HasCurrent; } } + public object Current { get { return (object)_iterator.Current; } } + public bool MoveNext() { return _iterator.MoveNext(); } + } + + [SecuritySafeCritical] + private IBindableVectorView GetIBindableVectorViewNoThrow() + { + if ((_flags & InterfaceForwardingSupport.IBindableVectorView) != 0) + return JitHelpers.UnsafeCast<IBindableVectorView>(_target); + else + return null; + } + + [SecuritySafeCritical] + private IVectorView<T2> GetVectorViewOfT() + { + if ((_flags & InterfaceForwardingSupport.IVectorView) != 0) + return JitHelpers.UnsafeCast<IVectorView<T2>>(_target); + else + throw new InvalidOperationException(); // We should not go down this path, unless Jupiter pass this out to managed code + // and managed code use reflection to do the cast + } + + // + // Convert to type T + // + private static T ConvertTo<T>(object value) + { + // Throw ArgumentNullException if value is null (otherwise we'll throw NullReferenceException + // when casting value to T) + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value); + + // No coersion support needed. If we need coersion later, this is the place + return (T) value; + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IIterable.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IIterable.cs new file mode 100644 index 0000000000..dbf4771cf6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IIterable.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +// Windows.Foundation.Collections.IIterable`1 cannot be referenced from managed code because it's hidden +// by the metadata adapter. We redeclare the interface manually to be able to talk to native WinRT objects. +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("faa585ea-6214-4217-afda-7f46de5869b3")] + [WindowsRuntimeImport] + internal interface IIterable<T> : IEnumerable<T> + { + [Pure] + IIterator<T> First(); + } + + [ComImport] + [Guid("036d2c08-df29-41af-8aa2-d774be62ba6f")] + [WindowsRuntimeImport] + internal interface IBindableIterable + { + [Pure] + IBindableIterator First(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IIterator.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IIterator.cs new file mode 100644 index 0000000000..83817494df --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IIterator.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Diagnostics.Contracts; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [WindowsRuntimeImport] + [Guid("6a79e863-4300-459a-9966-cbb660963ee1")] + internal interface IIterator<T> + { + [Pure] + T Current + { + get; + } + + [Pure] + bool HasCurrent + { + get; + } + + bool MoveNext(); + + [Pure] + int GetMany([Out] T[] items); + } + + [ComImport] + [WindowsRuntimeImport] + [Guid("6a1d6c07-076d-49f2-8314-f52c9c9a8331")] + internal interface IBindableIterator + { + [Pure] + object Current + { + get; + } + + [Pure] + bool HasCurrent + { + get; + } + + bool MoveNext(); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMap.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMap.cs new file mode 100644 index 0000000000..8160e6afc9 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMap.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +// Windows.Foundation.Collections.IMap`2, IMapView`2, and IKeyValuePair`2 cannot be referenced from +// managed code because they're hidden by the metadata adapter. We redeclare the interfaces manually +// to be able to talk to native WinRT objects. +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("3c2925fe-8519-45c1-aa79-197b6718c1c1")] + [WindowsRuntimeImport] + internal interface IMap<K, V> : IIterable<IKeyValuePair<K, V>> + { + [Pure] + V Lookup(K key); + [Pure] + uint Size { get; } + [Pure] + bool HasKey(K key); + [Pure] + IReadOnlyDictionary<K, V> GetView(); // Really an IMapView<K, V> + bool Insert(K key, V value); + void Remove(K key); + void Clear(); + } + + [ComImport] + [Guid("e480ce40-a338-4ada-adcf-272272e48cb9")] + [WindowsRuntimeImport] + internal interface IMapView<K, V> : IIterable<IKeyValuePair<K, V>> + { + [Pure] + V Lookup(K key); + [Pure] + uint Size { get; } + [Pure] + bool HasKey(K key); + [Pure] + void Split(out IMapView<K, V> first, out IMapView<K, V> second); + } + + [ComImport] + [Guid("02b51929-c1c4-4a7e-8940-0312b5c18500")] + [WindowsRuntimeImport] + internal interface IKeyValuePair<K, V> + { + [Pure] + K Key { get; } + [Pure] + V Value { get; } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs new file mode 100644 index 0000000000..3600a3ae70 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs @@ -0,0 +1,324 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IReadOnlyDictionary`2 interface on WinRT + // objects that support IMapView`2. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not IMapViewToIReadOnlyDictionaryAdapter objects. Rather, they are of type + // IMapView<K, V>. No actual IMapViewToIReadOnlyDictionaryAdapter object is ever instantiated. Thus, you will see + // a lot of expressions that cast "this" to "IMapView<K, V>". + [DebuggerDisplay("Count = {Count}")] + internal sealed class IMapViewToIReadOnlyDictionaryAdapter + { + private IMapViewToIReadOnlyDictionaryAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // V this[K key] { get } + [SecurityCritical] + internal V Indexer_Get<K, V>(K key) + { + if (key == null) + throw new ArgumentNullException("key"); + Contract.EndContractBlock(); + + IMapView<K, V> _this = JitHelpers.UnsafeCast<IMapView<K, V>>(this); + return Lookup(_this, key); + } + + // IEnumerable<K> Keys { get } + [SecurityCritical] + internal IEnumerable<K> Keys<K, V>() + { + IMapView<K, V> _this = JitHelpers.UnsafeCast<IMapView<K, V>>(this); + IReadOnlyDictionary<K, V> roDictionary = (IReadOnlyDictionary<K, V>)_this; + return new ReadOnlyDictionaryKeyCollection<K, V>(roDictionary); + } + + // IEnumerable<V> Values { get } + [SecurityCritical] + internal IEnumerable<V> Values<K, V>() + { + IMapView<K, V> _this = JitHelpers.UnsafeCast<IMapView<K, V>>(this); + IReadOnlyDictionary<K, V> roDictionary = (IReadOnlyDictionary<K, V>)_this; + return new ReadOnlyDictionaryValueCollection<K, V>(roDictionary); + } + + // bool ContainsKey(K key) + [Pure] + [SecurityCritical] + internal bool ContainsKey<K, V>(K key) + { + if (key == null) + throw new ArgumentNullException("key"); + + IMapView<K, V> _this = JitHelpers.UnsafeCast<IMapView<K, V>>(this); + return _this.HasKey(key); + } + + // bool TryGetValue(TKey key, out TValue value) + [SecurityCritical] + internal bool TryGetValue<K, V>(K key, out V value) + { + if (key == null) + throw new ArgumentNullException("key"); + + IMapView<K, V> _this = JitHelpers.UnsafeCast<IMapView<K, V>>(this); + + // It may be faster to call HasKey then Lookup. On failure, we would otherwise + // throw an exception from Lookup. + if (!_this.HasKey(key)) + { + value = default(V); + return false; + } + + try + { + value = _this.Lookup(key); + return true; + } + catch (Exception ex) // Still may hit this case due to a race condition + { + if (__HResults.E_BOUNDS == ex._HResult) + { + value = default(V); + return false; + } + throw; + } + } + + #region Helpers + + private static V Lookup<K, V>(IMapView<K, V> _this, K key) + { + Contract.Requires(null != key); + + try + { + return _this.Lookup(key); + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); + throw; + } + } + + #endregion Helpers + } + + // Note: One day we may make these return IReadOnlyCollection<T> + [Serializable] + [DebuggerDisplay("Count = {Count}")] + internal sealed class ReadOnlyDictionaryKeyCollection<TKey, TValue> : IEnumerable<TKey> + { + private readonly IReadOnlyDictionary<TKey, TValue> dictionary; + + public ReadOnlyDictionaryKeyCollection(IReadOnlyDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + } + + /* + public void CopyTo(TKey[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + if (array.Length <= index && this.Count > 0) + throw new ArgumentException(Environment.GetResourceString("Arg_IndexOutOfRangeException")); + if (array.Length - index < dictionary.Count) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + int i = index; + foreach (KeyValuePair<TKey, TValue> mapping in dictionary) + { + array[i++] = mapping.Key; + } + } + + public int Count { + get { return dictionary.Count; } + } + + public bool Contains(TKey item) + { + return dictionary.ContainsKey(item); + } + */ + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<TKey>)this).GetEnumerator(); + } + + public IEnumerator<TKey> GetEnumerator() + { + return new ReadOnlyDictionaryKeyEnumerator<TKey, TValue>(dictionary); + } + } // public class ReadOnlyDictionaryKeyCollection<TKey, TValue> + + + [Serializable] + internal sealed class ReadOnlyDictionaryKeyEnumerator<TKey, TValue> : IEnumerator<TKey> + { + private readonly IReadOnlyDictionary<TKey, TValue> dictionary; + private IEnumerator<KeyValuePair<TKey, TValue>> enumeration; + + public ReadOnlyDictionaryKeyEnumerator(IReadOnlyDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + this.enumeration = dictionary.GetEnumerator(); + } + + void IDisposable.Dispose() + { + enumeration.Dispose(); + } + + public bool MoveNext() + { + return enumeration.MoveNext(); + } + + Object IEnumerator.Current { + get { return ((IEnumerator<TKey>)this).Current; } + } + + public TKey Current { + get { return enumeration.Current.Key; } + } + + public void Reset() + { + enumeration = dictionary.GetEnumerator(); + } + } // class ReadOnlyDictionaryKeyEnumerator<TKey, TValue> + + + [Serializable] + [DebuggerDisplay("Count = {Count}")] + internal sealed class ReadOnlyDictionaryValueCollection<TKey, TValue> : IEnumerable<TValue> + { + private readonly IReadOnlyDictionary<TKey, TValue> dictionary; + + public ReadOnlyDictionaryValueCollection(IReadOnlyDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + } + + /* + public void CopyTo(TValue[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + if (array.Length <= index && this.Count > 0) + throw new ArgumentException(Environment.GetResourceString("Arg_IndexOutOfRangeException")); + if (array.Length - index < dictionary.Count) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + int i = index; + foreach (KeyValuePair<TKey, TValue> mapping in dictionary) + { + array[i++] = mapping.Value; + } + } + + public int Count { + get { return dictionary.Count; } + } + + public bool Contains(TValue item) + { + EqualityComparer<TValue> comparer = EqualityComparer<TValue>.Default; + foreach (TValue value in this) + if (comparer.Equals(item, value)) + return true; + return false; + } + */ + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<TValue>)this).GetEnumerator(); + } + + public IEnumerator<TValue> GetEnumerator() + { + return new ReadOnlyDictionaryValueEnumerator<TKey, TValue>(dictionary); + } + } // public class ReadOnlyDictionaryValueCollection<TKey, TValue> + + + [Serializable] + internal sealed class ReadOnlyDictionaryValueEnumerator<TKey, TValue> : IEnumerator<TValue> + { + private readonly IReadOnlyDictionary<TKey, TValue> dictionary; + private IEnumerator<KeyValuePair<TKey, TValue>> enumeration; + + public ReadOnlyDictionaryValueEnumerator(IReadOnlyDictionary<TKey, TValue> dictionary) + { + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + this.dictionary = dictionary; + this.enumeration = dictionary.GetEnumerator(); + } + + void IDisposable.Dispose() + { + enumeration.Dispose(); + } + + public bool MoveNext() + { + return enumeration.MoveNext(); + } + + Object IEnumerator.Current { + get { return ((IEnumerator<TValue>)this).Current; } + } + + public TValue Current { + get { return enumeration.Current.Value; } + } + + public void Reset() + { + enumeration = dictionary.GetEnumerator(); + } + } // class ReadOnlyDictionaryValueEnumerator<TKey, TValue> + +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IPropertyValue.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IPropertyValue.cs new file mode 100644 index 0000000000..4065406dfa --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IPropertyValue.cs @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Diagnostics.Contracts; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("4bd682dd-7554-40e9-9a9b-82654ede7e62")] + [WindowsRuntimeImport] + internal interface IPropertyValue + { + PropertyType Type + { + [Pure] + get; + } + + bool IsNumericScalar + { + [Pure] + get; + } + + [Pure] + Byte GetUInt8(); + + [Pure] + Int16 GetInt16(); + + [Pure] + UInt16 GetUInt16(); + + [Pure] + Int32 GetInt32(); + + [Pure] + UInt32 GetUInt32(); + + [Pure] + Int64 GetInt64(); + + [Pure] + UInt64 GetUInt64(); + + [Pure] + Single GetSingle(); + + [Pure] + Double GetDouble(); + + [Pure] + char GetChar16(); + + [Pure] + Boolean GetBoolean(); + + [Pure] + String GetString(); + + [Pure] + Guid GetGuid(); + + [Pure] + DateTimeOffset GetDateTime(); + + [Pure] + TimeSpan GetTimeSpan(); + + [Pure] + Point GetPoint(); + + [Pure] + Size GetSize(); + + [Pure] + Rect GetRect(); + + [Pure] + Byte[] GetUInt8Array(); + + [Pure] + Int16[] GetInt16Array(); + + [Pure] + UInt16[] GetUInt16Array(); + + [Pure] + Int32[] GetInt32Array(); + + [Pure] + UInt32[] GetUInt32Array(); + + [Pure] + Int64[] GetInt64Array(); + + [Pure] + UInt64[] GetUInt64Array(); + + [Pure] + Single[] GetSingleArray(); + + [Pure] + Double[] GetDoubleArray(); + + [Pure] + char[] GetChar16Array(); + + [Pure] + Boolean[] GetBooleanArray(); + + [Pure] + String[] GetStringArray(); + + [Pure] + object[] GetInspectableArray(); + + [Pure] + Guid[] GetGuidArray(); + + [Pure] + DateTimeOffset[] GetDateTimeArray(); + + [Pure] + TimeSpan[] GetTimeSpanArray(); + + [Pure] + Point[] GetPointArray(); + + [Pure] + Size[] GetSizeArray(); + + [Pure] + Rect[] GetRectArray(); + } + + // Specify size directly instead of fields to avoid warnings + [StructLayoutAttribute(LayoutKind.Sequential, Size=8)] + [WindowsRuntimeImport] + internal struct Point + { + + // float X; + // float Y; + } + + // Specify size directly instead of fields to avoid warnings + [StructLayoutAttribute(LayoutKind.Sequential, Size=8)] + [WindowsRuntimeImport] + internal struct Size + { + + // float Width; + // float Height; + } + + // Specify size directly instead of fields to avoid warnings + [StructLayoutAttribute(LayoutKind.Sequential, Size=16)] + [WindowsRuntimeImport] + internal struct Rect + { + // float X; + // float Y; + // float Width; + // float Height; + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs new file mode 100644 index 0000000000..d57f8f1f46 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IMapView`2 interface on managed + // objects that implement IReadOnlyDictionary`2. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not IReadOnlyDictionaryToIMapViewAdapter objects. Rather, they are of type + // IReadOnlyDictionary<K, V>. No actual IReadOnlyDictionaryToIMapViewAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IReadOnlyDictionary<K, V>". + [DebuggerDisplay("Size = {Size}")] + internal sealed class IReadOnlyDictionaryToIMapViewAdapter + { + private IReadOnlyDictionaryToIMapViewAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // V Lookup(K key) + [SecurityCritical] + internal V Lookup<K, V>(K key) + { + IReadOnlyDictionary<K, V> _this = JitHelpers.UnsafeCast<IReadOnlyDictionary<K, V>>(this); + V value; + bool keyFound = _this.TryGetValue(key, out value); + + if (!keyFound) + { + Exception e = new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + + return value; + } + + // uint Size { get } + [SecurityCritical] + internal uint Size<K, V>() + { + IReadOnlyDictionary<K, V> _this = JitHelpers.UnsafeCast<IReadOnlyDictionary<K, V>>(this); + return (uint)_this.Count; + } + + // bool HasKey(K key) + [SecurityCritical] + internal bool HasKey<K, V>(K key) + { + IReadOnlyDictionary<K, V> _this = JitHelpers.UnsafeCast<IReadOnlyDictionary<K, V>>(this); + return _this.ContainsKey(key); + } + + // void Split(out IMapView<K, V> first, out IMapView<K, V> second) + [SecurityCritical] + internal void Split<K, V>(out IMapView<K, V> first, out IMapView<K, V> second) + { + IReadOnlyDictionary<K, V> _this = JitHelpers.UnsafeCast<IReadOnlyDictionary<K, V>>(this); + + if (_this.Count < 2) { + first = null; + second = null; + return; + } + + ConstantSplittableMap<K, V> splittableMap = _this as ConstantSplittableMap<K, V>; + + if (splittableMap == null) + splittableMap = new ConstantSplittableMap<K, V>(_this); + + splittableMap.Split(out first, out second); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs new file mode 100644 index 0000000000..95780bcb13 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IVectorView`1 interface on managed + // objects that implement IReadOnlyList`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not IReadOnlyListToIVectorViewAdapter objects. Rather, they are of type + // IReadOnlyList<T>. No actual IReadOnlyListToIVectorViewAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IReadOnlyList<T>". + [DebuggerDisplay("Size = {Size}")] + internal sealed class IReadOnlyListToIVectorViewAdapter + { + private IReadOnlyListToIVectorViewAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // T GetAt(uint index) + [SecurityCritical] + internal T GetAt<T>(uint index) + { + IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + return _this[(int) index]; + } + catch (ArgumentOutOfRangeException ex) + { + ex.SetErrorCode(__HResults.E_BOUNDS); + throw; + } + } + + // uint Size { get } + [SecurityCritical] + internal uint Size<T>() + { + IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this); + return (uint)_this.Count; + } + + // bool IndexOf(T value, out uint index) + [SecurityCritical] + internal bool IndexOf<T>(T value, out uint index) + { + IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this); + + int ind = -1; + int max = _this.Count; + for (int i = 0; i < max; i++) + { + if (EqualityComparer<T>.Default.Equals(value, _this[i])) + { + ind = i; + break; + } + } + + if (-1 == ind) + { + index = 0; + return false; + } + + index = (uint)ind; + return true; + } + + // uint GetMany(uint startIndex, T[] items) + [SecurityCritical] + internal uint GetMany<T>(uint startIndex, T[] items) + { + IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this); + + // REX spec says "calling GetMany with startIndex equal to the length of the vector + // (last valid index + 1) and any specified capacity will succeed and return zero actual + // elements". + if (startIndex == _this.Count) + return 0; + + EnsureIndexInt32(startIndex, _this.Count); + + if (items == null) + { + return 0; + } + + uint itemCount = Math.Min((uint)items.Length, (uint)_this.Count - startIndex); + + for (uint i = 0; i < itemCount; ++i) + { + items[i] = _this[(int)(i + startIndex)]; + } + + if (typeof(T) == typeof(string)) + { + string[] stringItems = items as string[]; + + // Fill in the rest of the array with String.Empty to avoid marshaling failure + for (uint i = itemCount; i < items.Length; ++i) + stringItems[i] = String.Empty; + } + + return itemCount; + } + + #region Helpers + + private static void EnsureIndexInt32(uint index, int listCapacity) + { + // We use '<=' and not '<' because Int32.MaxValue == index would imply + // that Size > Int32.MaxValue: + if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity) + { + Exception e = new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + } + + #endregion Helpers + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReference.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReference.cs new file mode 100644 index 0000000000..d0101dafc9 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReference.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("61c17706-2d65-11e0-9ae8-d48564015472")] + [WindowsRuntimeImport] + // Note that ideally, T should be constrained to be a value type. However, Windows uses IReference<HSTRING> + // and the projection may not be exactly pretty. + internal interface IReference<T> : IPropertyValue + { + T Value { get; } + } + + [ComImport] + [Guid("61c17707-2d65-11e0-9ae8-d48564015472")] + [WindowsRuntimeImport] + // T can be any WinRT-compatible type, including reference types. + internal interface IReferenceArray<T> : IPropertyValue + { + T[] Value { get; } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IRestrictedErrorInfo.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IRestrictedErrorInfo.cs new file mode 100644 index 0000000000..48bcc4f618 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IRestrictedErrorInfo.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ +#if FEATURE_CORECLR + [System.Runtime.CompilerServices.FriendAccessAllowed] +#endif + [ComImport] + [Guid("82BA7092-4C88-427D-A7BC-16DD93FEB67E")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IRestrictedErrorInfo + { + void GetErrorDetails([MarshalAs(UnmanagedType.BStr)] out string description, + out int error, + [MarshalAs(UnmanagedType.BStr)] out string restrictedDescription, + [MarshalAs(UnmanagedType.BStr)] out string capabilitySid); + + void GetReference([MarshalAs(UnmanagedType.BStr)] out string reference); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVector.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVector.cs new file mode 100644 index 0000000000..a77ff005b9 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVector.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +// Windows.Foundation.Collections.IVector`1 and IVectorView`1 cannot be referenced from managed +// code because they're hidden by the metadata adapter. We redeclare the interfaces manually +// to be able to talk to native WinRT objects. +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("913337e9-11a1-4345-a3a2-4e7f956e222d")] + [WindowsRuntimeImport] + internal interface IVector<T> : IIterable<T> + { + [Pure] + T GetAt(uint index); + [Pure] + uint Size { get; } + [Pure] + IReadOnlyList<T> GetView(); // Really an IVectorView<T>. + [Pure] + bool IndexOf(T value, out uint index); + void SetAt(uint index, T value); + void InsertAt(uint index, T value); + void RemoveAt(uint index); + void Append(T value); + void RemoveAtEnd(); + void Clear(); + [Pure] + uint GetMany(uint startIndex, [Out] T[] items); + void ReplaceAll(T[] items); + } + + // Same as IVector - the only difference is that GetView returns IVectorView<T> + [ComImport] + [Guid("913337e9-11a1-4345-a3a2-4e7f956e222d")] + [WindowsRuntimeImport] + internal interface IVector_Raw<T> : IIterable<T> + { + [Pure] + T GetAt(uint index); + [Pure] + uint Size { get; } + [Pure] + IVectorView<T> GetView(); + [Pure] + bool IndexOf(T value, out uint index); + void SetAt(uint index, T value); + void InsertAt(uint index, T value); + void RemoveAt(uint index); + void Append(T value); + void RemoveAtEnd(); + void Clear(); + [Pure] + uint GetMany(uint startIndex, [Out] T[] items); + void ReplaceAll(T[] items); + } + + [ComImport] + [Guid("bbe1fa4c-b0e3-4583-baef-1f1b2e483e56")] + [WindowsRuntimeImport] + internal interface IVectorView<T> : IIterable<T> + { + [Pure] + T GetAt(uint index); + [Pure] + uint Size { get; } + [Pure] + bool IndexOf(T value, out uint index); + [Pure] + uint GetMany(uint startIndex, [Out] T[] items); + } + + [ComImport] + [Guid("393de7de-6fd0-4c0d-bb71-47244a113e93")] + [WindowsRuntimeImport] + internal interface IBindableVector : IBindableIterable + { + [Pure] + object GetAt(uint index); + [Pure] + uint Size { get; } + [Pure] + IBindableVectorView GetView(); + [Pure] + bool IndexOf(object value, out uint index); + void SetAt(uint index, object value); + void InsertAt(uint index, object value); + void RemoveAt(uint index); + void Append(object value); + void RemoveAtEnd(); + void Clear(); + } + + [ComImport] + [Guid("346dd6e7-976e-4bc3-815d-ece243bc0f33")] + [WindowsRuntimeImport] + internal interface IBindableVectorView : IBindableIterable + { + [Pure] + object GetAt(uint index); + [Pure] + uint Size { get; } + [Pure] + bool IndexOf(object value, out uint index); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs new file mode 100644 index 0000000000..72d6fa8cc3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + internal delegate T Indexer_Get_Delegate<out T>(int index); + + // This is a set of stub methods implementing the support for the IReadOnlyList`1 interface on WinRT + // objects that support IVectorView`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not IVectorViewToIReadOnlyListAdapter objects. Rather, they are of type + // IVectorView<T>. No actual IVectorViewToIReadOnlyListAdapter object is ever instantiated. Thus, you will see + // a lot of expressions that cast "this" to "IVectorView<T>". + [DebuggerDisplay("Count = {Count}")] + internal sealed class IVectorViewToIReadOnlyListAdapter + { + private IVectorViewToIReadOnlyListAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // T this[int index] { get } + [SecurityCritical] + internal T Indexer_Get<T>(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IVectorView<T> _this = JitHelpers.UnsafeCast<IVectorView<T>>(this); + + try + { + return _this.GetAt((uint) index); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + // T this[int index] { get } + [SecurityCritical] + internal T Indexer_Get_Variance<T>(int index) where T : class + { + bool fUseString; + Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall( + this, + typeof(IReadOnlyList<T>).TypeHandle.Value, + out fUseString); + + if (target != null) + { + return (JitHelpers.UnsafeCast<Indexer_Get_Delegate<T>>(target))(index); + } + + if (fUseString) + { + return JitHelpers.UnsafeCast<T>(Indexer_Get<string>(index)); + } + + return Indexer_Get<T>(index); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs new file mode 100644 index 0000000000..f1b799aa84 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + internal delegate IEnumerator<T> GetEnumerator_Delegate<out T>(); + + // This is a set of stub methods implementing the support for the IEnumerable`1 interface on WinRT + // objects that implement IIterable`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not IterableToEnumerableAdapter objects. Rather, they are of type + // IIterable<T>. No actual IterableToEnumerableAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IIterable<T>". + internal sealed class IterableToEnumerableAdapter + { + private IterableToEnumerableAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T>. + [SecurityCritical] + internal IEnumerator<T> GetEnumerator_Stub<T>() + { + IIterable<T> _this = JitHelpers.UnsafeCast<IIterable<T>>(this); + return new IteratorToEnumeratorAdapter<T>(_this.First()); + } + + // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable<T> + // and it is possible that the implementation supports IEnumerable<Type>/IEnumerable<string>/IEnumerable<Exception>/ + // IEnumerable<array>/IEnumerable<delegate> rather than IEnumerable<T> because T is assignable from Type/string/ + // Exception/array/delegate via co-variance. + [SecurityCritical] + internal IEnumerator<T> GetEnumerator_Variance_Stub<T>() where T : class + { + bool fUseString; + Delegate target = System.StubHelpers.StubHelpers.GetTargetForAmbiguousVariantCall( + this, + typeof(IEnumerable<T>).TypeHandle.Value, + out fUseString); + + if (target != null) + { + return (JitHelpers.UnsafeCast<GetEnumerator_Delegate<T>>(target))(); + } + + if (fUseString) + { + return JitHelpers.UnsafeCast<IEnumerator<T>>(GetEnumerator_Stub<string>()); + } + + return GetEnumerator_Stub<T>(); + } + } + + internal sealed class BindableIterableToEnumerableAdapter + { + private BindableIterableToEnumerableAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + private sealed class NonGenericToGenericIterator : IIterator<object> + { + private IBindableIterator iterator; + + public NonGenericToGenericIterator(IBindableIterator iterator) + { this.iterator = iterator; } + + public object Current { get { return iterator.Current; } } + public bool HasCurrent { get { return iterator.HasCurrent; } } + public bool MoveNext() { return iterator.MoveNext(); } + public int GetMany(object[] items) { throw new NotSupportedException(); } + } + + // This method is invoked when GetEnumerator is called on a WinRT-backed implementation of IEnumerable. + [SecurityCritical] + internal IEnumerator GetEnumerator_Stub() + { + IBindableIterable _this = JitHelpers.UnsafeCast<IBindableIterable>(this); + return new IteratorToEnumeratorAdapter<object>(new NonGenericToGenericIterator(_this.First())); + } + } + + // Adapter class which holds a Windows Runtime IIterator<T>, exposing it as a managed IEnumerator<T> + + + // There are a few implementation differences between the Iterator and IEnumerator which need to be + // addressed. Iterator starts at index 0 while IEnumerator starts at index -1 as a result of which + // the first call to IEnumerator.Current is correct only after calling MoveNext(). + // Also IEnumerator throws an exception when we call Current after reaching the end of collection. + internal sealed class IteratorToEnumeratorAdapter<T> : IEnumerator<T> + { + private IIterator<T> m_iterator; + private bool m_hadCurrent; + private T m_current; + private bool m_isInitialized; + + internal IteratorToEnumeratorAdapter(IIterator<T> iterator) + { + Contract.Requires(iterator != null); + m_iterator = iterator; + m_hadCurrent = true; + m_isInitialized = false; + } + + public T Current + { + get + { + // The enumerator has not been advanced to the first element yet. + if (!m_isInitialized) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); + // The enumerator has reached the end of the collection + if (!m_hadCurrent) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); + return m_current; + } + } + + object IEnumerator.Current + { + get + { + // The enumerator has not been advanced to the first element yet. + if (!m_isInitialized) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); + // The enumerator has reached the end of the collection + if (!m_hadCurrent) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); + return m_current; + } + } + + [SecuritySafeCritical] + public bool MoveNext() + { + // If we've passed the end of the iteration, IEnumerable<T> should return false, while + // IIterable will fail the interface call + if (!m_hadCurrent) + { + return false; + } + + // IIterators start at index 0, rather than -1. If this is the first call, we need to just + // check HasCurrent rather than actually moving to the next element + try + { + if (!m_isInitialized) + { + m_hadCurrent = m_iterator.HasCurrent; + m_isInitialized = true; + } + else + { + m_hadCurrent = m_iterator.MoveNext(); + } + + // We want to save away the current value for two reasons: + // 1. Accessing .Current is cheap on other iterators, so having it be a property which is a + // simple field access preserves the expected performance characteristics (as opposed to + // triggering a COM call every time the property is accessed) + // + // 2. This allows us to preserve the same semantics as generic collection iteration when iterating + // beyond the end of the collection - namely that Current continues to return the last value + // of the collection + if (m_hadCurrent) + { + m_current = m_iterator.Current; + } + } + catch (Exception e) + { + // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration + if (Marshal.GetHRForException(e) == __HResults.E_CHANGED_STATE) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + else + { + throw; + } + } + + return m_hadCurrent; + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public void Dispose() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs new file mode 100644 index 0000000000..35dc495d3f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IBindableVector interface on managed + // objects that implement IList. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not ListToBindableVectorAdapter objects. Rather, they are of type + // IList. No actual ListToVectorBindableAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IList". + internal sealed class ListToBindableVectorAdapter + { + private ListToBindableVectorAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // object GetAt(uint index) + [SecurityCritical] + internal object GetAt(uint index) + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + return _this[(Int32)index]; + } + catch (ArgumentOutOfRangeException ex) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); + } + } + + // uint Size { get } + [SecurityCritical] + internal uint Size() + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + return (uint)_this.Count; + } + + // IBindableVectorView GetView() + [SecurityCritical] + internal IBindableVectorView GetView() + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + return new ListToBindableVectorViewAdapter(_this); + } + + // bool IndexOf(object value, out uint index) + [SecurityCritical] + internal bool IndexOf(object value, out uint index) + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + int ind = _this.IndexOf(value); + + if (-1 == ind) + { + index = 0; + return false; + } + + index = (uint)ind; + return true; + } + + // void SetAt(uint index, object value) + [SecurityCritical] + internal void SetAt(uint index, object value) + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + _this[(int)index] = value; + } + catch (ArgumentOutOfRangeException ex) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); + } + } + + // void InsertAt(uint index, object value) + [SecurityCritical] + internal void InsertAt(uint index, object value) + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + + // Inserting at an index one past the end of the list is equivalent to appending + // so we need to ensure that we're within (0, count + 1). + EnsureIndexInt32(index, _this.Count + 1); + + try + { + _this.Insert((int)index, value); + } + catch (ArgumentOutOfRangeException ex) + { + // Change error code to match what WinRT expects + ex.SetErrorCode(__HResults.E_BOUNDS); + throw; + } + } + + // void RemoveAt(uint index) + [SecurityCritical] + internal void RemoveAt(uint index) + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + _this.RemoveAt((Int32)index); + } + catch (ArgumentOutOfRangeException ex) + { + // Change error code to match what WinRT expects + ex.SetErrorCode(__HResults.E_BOUNDS); + throw; + } + } + + // void Append(object value) + [SecurityCritical] + internal void Append(object value) + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + _this.Add(value); + } + + // void RemoveAtEnd() + [SecurityCritical] + internal void RemoveAtEnd() + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + if (_this.Count == 0) + { + Exception e = new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRemoveLastFromEmptyCollection")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + + uint size = (uint)_this.Count; + RemoveAt(size - 1); + } + + // void Clear() + [SecurityCritical] + internal void Clear() + { + IList _this = JitHelpers.UnsafeCast<IList>(this); + _this.Clear(); + } + + // Helpers: + + private static void EnsureIndexInt32(uint index, int listCapacity) + { + // We use '<=' and not '<' becasue Int32.MaxValue == index would imply + // that Size > Int32.MaxValue: + if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity) + { + Exception e = new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs new file mode 100644 index 0000000000..f760576aaa --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + /// A Windows Runtime IBindableVectorView implementation that wraps around a managed IList exposing + /// it to Windows runtime interop. + internal sealed class ListToBindableVectorViewAdapter : IBindableVectorView + { + private readonly IList list; + + internal ListToBindableVectorViewAdapter(IList list) + { + if (list == null) + throw new ArgumentNullException("list"); + + Contract.EndContractBlock(); + + this.list = list; + } + + private static void EnsureIndexInt32(uint index, int listCapacity) + { + // We use '<=' and not '<' becasue Int32.MaxValue == index would imply + // that Size > Int32.MaxValue: + if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity) + { + Exception e = new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + } + + // IBindableIterable implementation: + + public IBindableIterator First() + { + IEnumerator enumerator = list.GetEnumerator(); + return new EnumeratorToIteratorAdapter<object>(new EnumerableToBindableIterableAdapter.NonGenericToGenericEnumerator(enumerator)); + } + + // IBindableVectorView implementation: + + public object GetAt(uint index) + { + EnsureIndexInt32(index, list.Count); + + try + { + return list[(int)index]; + + } + catch (ArgumentOutOfRangeException ex) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); + } + } + + public uint Size + { + get + { + return (uint)list.Count; + } + } + + public bool IndexOf(object value, out uint index) + { + int ind = list.IndexOf(value); + + if (-1 == ind) + { + index = 0; + return false; + } + + index = (uint)ind; + return true; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs new file mode 100644 index 0000000000..77f3a9464f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IVector`1 interface on managed + // objects that implement IList`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not ListToVectorAdapter objects. Rather, they are of type + // IList<T>. No actual ListToVectorAdapter object is ever instantiated. Thus, you will + // see a lot of expressions that cast "this" to "IList<T>". + internal sealed class ListToVectorAdapter + { + private ListToVectorAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // T GetAt(uint index) + [SecurityCritical] + internal T GetAt<T>(uint index) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + return _this[(Int32)index]; + } + catch (ArgumentOutOfRangeException ex) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); + } + } + + // uint Size { get } + [SecurityCritical] + internal uint Size<T>() + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + return (uint)_this.Count; + } + + // IVectorView<T> GetView() + [SecurityCritical] + internal IReadOnlyList<T> GetView<T>() + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + Contract.Assert(_this != null); + + // Note: This list is not really read-only - you could QI for a modifiable + // list. We gain some perf by doing this. We believe this is acceptable. + IReadOnlyList<T> roList = _this as IReadOnlyList<T>; + if (roList == null) + { + roList = new ReadOnlyCollection<T>(_this); + } + return roList; + } + + // bool IndexOf(T value, out uint index) + [SecurityCritical] + internal bool IndexOf<T>(T value, out uint index) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + int ind = _this.IndexOf(value); + + if (-1 == ind) + { + index = 0; + return false; + } + + index = (uint)ind; + return true; + } + + // void SetAt(uint index, T value) + [SecurityCritical] + internal void SetAt<T>(uint index, T value) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + _this[(int)index] = value; + } + catch (ArgumentOutOfRangeException ex) + { + throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); + } + } + + // void InsertAt(uint index, T value) + [SecurityCritical] + internal void InsertAt<T>(uint index, T value) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + + // Inserting at an index one past the end of the list is equivalent to appending + // so we need to ensure that we're within (0, count + 1). + EnsureIndexInt32(index, _this.Count + 1); + + try + { + _this.Insert((int)index, value); + } + catch (ArgumentOutOfRangeException ex) + { + // Change error code to match what WinRT expects + ex.SetErrorCode(__HResults.E_BOUNDS); + throw; + } + } + + // void RemoveAt(uint index) + [SecurityCritical] + internal void RemoveAt<T>(uint index) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + EnsureIndexInt32(index, _this.Count); + + try + { + _this.RemoveAt((Int32)index); + } + catch (ArgumentOutOfRangeException ex) + { + // Change error code to match what WinRT expects + ex.SetErrorCode(__HResults.E_BOUNDS); + throw; + } + } + + // void Append(T value) + [SecurityCritical] + internal void Append<T>(T value) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + _this.Add(value); + } + + // void RemoveAtEnd() + [SecurityCritical] + internal void RemoveAtEnd<T>() + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + if (_this.Count == 0) + { + Exception e = new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRemoveLastFromEmptyCollection")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + + uint size = (uint)_this.Count; + RemoveAt<T>(size - 1); + } + + // void Clear() + [SecurityCritical] + internal void Clear<T>() + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + _this.Clear(); + } + + // uint GetMany(uint startIndex, T[] items) + [SecurityCritical] + internal uint GetMany<T>(uint startIndex, T[] items) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + return GetManyHelper<T>(_this, startIndex, items); + } + + // void ReplaceAll(T[] items) + [SecurityCritical] + internal void ReplaceAll<T>(T[] items) + { + IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); + _this.Clear(); + + if (items != null) + { + foreach (T item in items) + { + _this.Add(item); + } + } + } + + // Helpers: + + private static void EnsureIndexInt32(uint index, int listCapacity) + { + // We use '<=' and not '<' becasue Int32.MaxValue == index would imply + // that Size > Int32.MaxValue: + if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity) + { + Exception e = new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue")); + e.SetErrorCode(__HResults.E_BOUNDS); + throw e; + } + } + + private static uint GetManyHelper<T>(IList<T> sourceList, uint startIndex, T[] items) + { + // Calling GetMany with a start index equal to the size of the list should always + // return 0 elements, regardless of the input item size + if (startIndex == sourceList.Count) + { + return 0; + } + + EnsureIndexInt32(startIndex, sourceList.Count); + + if (items == null) + { + return 0; + } + + uint itemCount = Math.Min((uint)items.Length, (uint)sourceList.Count - startIndex); + for (uint i = 0; i < itemCount; ++i) + { + items[i] = sourceList[(int)(i + startIndex)]; + } + + if (typeof(T) == typeof(string)) + { + string[] stringItems = items as string[]; + + // Fill in rest of the array with String.Empty to avoid marshaling failure + for (uint i = itemCount; i < items.Length; ++i) + stringItems[i] = String.Empty; + } + + return itemCount; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ManagedActivationFactory.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ManagedActivationFactory.cs new file mode 100644 index 0000000000..3e93428d26 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ManagedActivationFactory.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Diagnostics.Contracts; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + [ComImport] + [Guid("60D27C8D-5F61-4CCE-B751-690FAE66AA53")] + [WindowsRuntimeImport] + internal interface IManagedActivationFactory + { + void RunClassConstructor(); + } + + // A ManangedActivationFactory provides the IActivationFactory implementation for managed types which are + // constructable via Windows Runtime. Implementation of specialized factory and static WinRT interfaces is + // provided using VM functionality (see Marshal.InitializeWinRTFactoryObject for details). + // + // In order to be activatable via the ManagedActivationFactory type, the type must be decorated with either + // ActivatableAttribute, or StaticAttribute. + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + internal sealed class ManagedActivationFactory : IActivationFactory, IManagedActivationFactory + { + private Type m_type; + + [SecurityCritical] + internal ManagedActivationFactory(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + // Check whether the type is "exported to WinRT", i.e. it is declared in a managed .winmd and is decorated + // with at least one ActivatableAttribute or StaticAttribute. + if (!(type is RuntimeType) || !type.IsExportedToWindowsRuntime) + throw new ArgumentException(Environment.GetResourceString("Argument_TypeNotActivatableViaWindowsRuntime", type), "type"); + + m_type = type; + } + + // Activate an instance of the managed type by using its default constructor. + public object ActivateInstance() + { + try + { + return Activator.CreateInstance(m_type); + } + catch (MissingMethodException) + { + // If the type doesn't expose a default constructor, then we fail with E_NOTIMPL + throw new NotImplementedException(); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + } + + // Runs the class constructor + // Currently only Jupiter use this to run class constructor in order to + // initialize DependencyProperty objects and do necessary work + void IManagedActivationFactory.RunClassConstructor() + { + RuntimeHelpers.RunClassConstructor(m_type.TypeHandle); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToCollectionAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToCollectionAdapter.cs new file mode 100644 index 0000000000..395bef93d5 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToCollectionAdapter.cs @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // These stubs will be used when a call via ICollection<KeyValuePair<K, V>> is made in managed code. + // This can mean two things - either the underlying unmanaged object implements IMap<K, V> or it + // implements IVector<IKeyValuePair<K, V>> and we cannot determine this statically in the general + // case so we have to cast at run-time. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not MapToCollectionAdapter objects. Rather, they are of type + // IVector<KeyValuePair<K, V>> or IMap<K, V>. No actual MapToCollectionAdapter object is ever + // instantiated. Thus, you will see a lot of expressions that cast "this" to "IVector<KeyValuePair<K, V>>" + // or "IMap<K, V>". + internal sealed class MapToCollectionAdapter + { + private MapToCollectionAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // int Count { get } + [Pure] + [SecurityCritical] + internal int Count<K, V>() + { + object _this = JitHelpers.UnsafeCast<object>(this); + + IMap<K, V> _this_map = _this as IMap<K, V>; + if (_this_map != null) + { + uint size = _this_map.Size; + + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingDictionaryTooLarge")); + } + + return (int)size; + } + else + { + IVector<KeyValuePair<K, V>> _this_vector = JitHelpers.UnsafeCast<IVector<KeyValuePair<K, V>>>(this); + uint size = _this_vector.Size; + + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)size; + } + } + + // bool IsReadOnly { get } + [SecurityCritical] + internal bool IsReadOnly<K, V>() + { + return false; + } + + // void Add(T item) + [SecurityCritical] + internal void Add<K, V>(KeyValuePair<K, V> item) + { + object _this = JitHelpers.UnsafeCast<object>(this); + + IDictionary<K, V> _this_dictionary = _this as IDictionary<K, V>; + if (_this_dictionary != null) + { + _this_dictionary.Add(item.Key, item.Value); + } + else + { + IVector<KeyValuePair<K, V>> _this_vector = JitHelpers.UnsafeCast<IVector<KeyValuePair<K, V>>>(this); + _this_vector.Append(item); + } + } + + // void Clear() + [SecurityCritical] + internal void Clear<K, V>() + { + object _this = JitHelpers.UnsafeCast<object>(this); + + IMap<K, V> _this_map = _this as IMap<K, V>; + if (_this_map != null) + { + _this_map.Clear(); + } + else + { + IVector<KeyValuePair<K, V>> _this_vector = JitHelpers.UnsafeCast<IVector<KeyValuePair<K, V>>>(this); + _this_vector.Clear(); + } + } + + // bool Contains(T item) + [SecurityCritical] + internal bool Contains<K, V>(KeyValuePair<K, V> item) + { + object _this = JitHelpers.UnsafeCast<object>(this); + + IDictionary<K, V> _this_dictionary = _this as IDictionary<K, V>; + if (_this_dictionary != null) + { + V value; + bool hasKey = _this_dictionary.TryGetValue(item.Key, out value); + + if (!hasKey) + return false; + + return EqualityComparer<V>.Default.Equals(value, item.Value); + } + else + { + IVector<KeyValuePair<K, V>> _this_vector = JitHelpers.UnsafeCast<IVector<KeyValuePair<K, V>>>(this); + + uint index; + return _this_vector.IndexOf(item, out index); + } + } + + // void CopyTo(T[] array, int arrayIndex) + [SecurityCritical] + internal void CopyTo<K, V>(KeyValuePair<K, V>[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex"); + + if (array.Length <= arrayIndex && Count<K, V>() > 0) + throw new ArgumentException(Environment.GetResourceString("Argument_IndexOutOfArrayBounds")); + + if (array.Length - arrayIndex < Count<K, V>()) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + Contract.EndContractBlock(); + + IIterable<KeyValuePair<K, V>> _this = JitHelpers.UnsafeCast<IIterable<KeyValuePair<K, V>>>(this); + foreach (KeyValuePair<K, V> mapping in _this) + { + array[arrayIndex++] = mapping; + } + } + + // bool Remove(T item) + [SecurityCritical] + internal bool Remove<K, V>(KeyValuePair<K, V> item) + { + object _this = JitHelpers.UnsafeCast<object>(this); + + IDictionary<K, V> _this_dictionary = _this as IDictionary<K, V>; + if (_this_dictionary != null) + { + return _this_dictionary.Remove(item.Key); + } + else + { + IVector<KeyValuePair<K, V>> _this_vector = JitHelpers.UnsafeCast<IVector<KeyValuePair<K, V>>>(this); + uint index; + bool exists = _this_vector.IndexOf(item, out index); + + if (!exists) + return false; + + if (((uint)Int32.MaxValue) < index) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + VectorToListAdapter.RemoveAtHelper<KeyValuePair<K, V>>(_this_vector, index); + return true; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs new file mode 100644 index 0000000000..d7897ced9f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IDictionary`2 interface on WinRT + // objects that support IMap`2. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not MapToDictionaryAdapter objects. Rather, they are of type + // IMap<K, V>. No actual MapToDictionaryAdapter object is ever instantiated. Thus, you will see + // a lot of expressions that cast "this" to "IMap<K, V>". + internal sealed class MapToDictionaryAdapter + { + private MapToDictionaryAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // V this[K key] { get } + [SecurityCritical] + internal V Indexer_Get<K, V>(K key) + { + if (key == null) + throw new ArgumentNullException("key"); + + Contract.EndContractBlock(); + + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + return Lookup(_this, key); + } + + // V this[K key] { set } + [SecurityCritical] + internal void Indexer_Set<K, V>(K key, V value) + { + if (key == null) + throw new ArgumentNullException("key"); + + Contract.EndContractBlock(); + + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + Insert(_this, key, value); + } + + // ICollection<K> Keys { get } + [SecurityCritical] + internal ICollection<K> Keys<K, V>() + { + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + IDictionary<K, V> dictionary = (IDictionary<K, V>)_this; + return new DictionaryKeyCollection<K, V>(dictionary); + } + + // ICollection<V> Values { get } + [SecurityCritical] + internal ICollection<V> Values<K, V>() + { + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + IDictionary<K, V> dictionary = (IDictionary<K, V>)_this; + return new DictionaryValueCollection<K, V>(dictionary); + } + + // bool ContainsKey(K key) + [Pure] + [SecurityCritical] + internal bool ContainsKey<K, V>(K key) + { + if (key == null) + throw new ArgumentNullException("key"); + + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + return _this.HasKey(key); + } + + // void Add(K key, V value) + [SecurityCritical] + internal void Add<K, V>(K key, V value) + { + if (key == null) + throw new ArgumentNullException("key"); + + if (ContainsKey<K, V>(key)) + throw new ArgumentException(Environment.GetResourceString("Argument_AddingDuplicate")); + + Contract.EndContractBlock(); + + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + Insert(_this, key, value); + } + + // bool Remove(TKey key) + [SecurityCritical] + internal bool Remove<K, V>(K key) + { + if (key == null) + throw new ArgumentNullException("key"); + + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + if (!_this.HasKey(key)) + return false; + + try + { + _this.Remove(key); + return true; + + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + return false; + + throw; + } + } + + // bool TryGetValue(TKey key, out TValue value) + [SecurityCritical] + internal bool TryGetValue<K, V>(K key, out V value) + { + if (key == null) + throw new ArgumentNullException("key"); + + IMap<K, V> _this = JitHelpers.UnsafeCast<IMap<K, V>>(this); + if (!_this.HasKey(key)) + { + value = default(V); + return false; + } + + try + { + value = Lookup(_this, key); + return true; + } + catch (KeyNotFoundException) + { + value = default(V); + return false; + } + } + + // Helpers: + + private static V Lookup<K, V>(IMap<K, V> _this, K key) + { + Contract.Requires(null != key); + + try + { + return _this.Lookup(key); + } + catch (Exception ex) + { + + if (__HResults.E_BOUNDS == ex._HResult) + throw new KeyNotFoundException(Environment.GetResourceString("Arg_KeyNotFound")); + throw; + } + } + + private static bool Insert<K, V>(IMap<K, V> _this, K key, V value) + { + Contract.Requires(null != key); + + bool replaced = _this.Insert(key, value); + return replaced; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapViewToReadOnlyCollectionAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapViewToReadOnlyCollectionAdapter.cs new file mode 100644 index 0000000000..58427fbd71 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapViewToReadOnlyCollectionAdapter.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // These stubs will be used when a call via IReadOnlyCollection<KeyValuePair<K, V>> is made in managed code. + // This can mean two things - either the underlying unmanaged object implements IMapView<K, V> or it + // implements IVectorView<IKeyValuePair<K, V>> and we cannot determine this statically in the general + // case so we have to cast at run-time. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not MapViewToReadOnlyCollectionAdapter objects. Rather, they are of type + // IVectorView<KeyValuePair<K, V>> or IMapView<K, V>. No actual MapViewToReadOnlyCollectionAdapter object is ever + // instantiated. Thus, you will see a lot of expressions that cast "this" to "IVectorView<KeyValuePair<K, V>>" + // or "IMapView<K, V>". + internal sealed class MapViewToReadOnlyCollectionAdapter + { + private MapViewToReadOnlyCollectionAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // int Count { get } + [Pure] + [SecurityCritical] + internal int Count<K, V>() + { + object _this = JitHelpers.UnsafeCast<object>(this); + + IMapView<K, V> _this_map = _this as IMapView<K, V>; + if (_this_map != null) + { + uint size = _this_map.Size; + + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingDictionaryTooLarge")); + } + + return (int)size; + } + else + { + IVectorView<KeyValuePair<K, V>> _this_vector = JitHelpers.UnsafeCast<IVectorView<KeyValuePair<K, V>>>(this); + uint size = _this_vector.Size; + + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)size; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/NativeMethods.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/NativeMethods.cs new file mode 100644 index 0000000000..b8cd65efa6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/NativeMethods.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ +#if BIT64 + [StructLayout(LayoutKind.Explicit, Size = 24)] +#else + [StructLayout(LayoutKind.Explicit, Size = 20)] +#endif + internal unsafe struct HSTRING_HEADER + { + } + + internal static class UnsafeNativeMethods + { + [DllImport("api-ms-win-core-winrt-error-l1-1-1.dll", PreserveSig = false)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static extern IRestrictedErrorInfo GetRestrictedErrorInfo(); + + [DllImport("api-ms-win-core-winrt-error-l1-1-1.dll")] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool RoOriginateLanguageException(int error, [MarshalAs(UnmanagedType.HString)]string message, IntPtr languageException); + + [DllImport("api-ms-win-core-winrt-error-l1-1-1.dll", PreserveSig = false)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static extern void RoReportUnhandledError(IRestrictedErrorInfo error); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static unsafe extern int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)] string sourceString, + int length, + [Out] IntPtr *hstring); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static unsafe extern int WindowsCreateStringReference(char *sourceString, + int length, + [Out] HSTRING_HEADER *hstringHeader, + [Out] IntPtr *hstring); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static extern int WindowsDeleteString(IntPtr hstring); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static unsafe extern char* WindowsGetStringRawBuffer(IntPtr hstring, [Out] uint *length); + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/PropertyValue.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/PropertyValue.cs new file mode 100644 index 0000000000..ad64c9917f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/PropertyValue.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // Note this is a copy of the PropertyType enumeration from Windows.Foundation.winmd + internal enum PropertyType + { + // WARNING: These values have to match enum Windows.Foundation.PropertyType !!! + Empty = 0, + UInt8 = 1, + Int16 = 2, + UInt16 = 3, + Int32 = 4, + UInt32 = 5, + Int64 = 6, + UInt64 = 7, + Single = 8, + Double = 9, + Char16 = 10, + Boolean = 11, + String = 12, + Inspectable = 13, + DateTime = 14, + TimeSpan = 15, + Guid = 16, + Point = 17, + Size = 18, + Rect = 19, + + Other = 20, + + UInt8Array = UInt8 + 1024, + Int16Array = Int16 + 1024, + UInt16Array = UInt16 + 1024, + Int32Array = Int32 + 1024, + UInt32Array = UInt32 + 1024, + Int64Array = Int64 + 1024, + UInt64Array = UInt64 + 1024, + SingleArray = Single + 1024, + DoubleArray = Double + 1024, + Char16Array = Char16 + 1024, + BooleanArray = Boolean + 1024, + StringArray = String + 1024, + InspectableArray = Inspectable + 1024, + DateTimeArray = DateTime + 1024, + TimeSpanArray = TimeSpan + 1024, + GuidArray = Guid + 1024, + PointArray = Point + 1024, + SizeArray = Size + 1024, + RectArray = Rect + 1024, + OtherArray = Other + 1024, + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/RuntimeClass.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/RuntimeClass.cs new file mode 100644 index 0000000000..5dd7d00579 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/RuntimeClass.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** RuntimeClass is the base class of all WinRT types +** +** +===========================================================*/ +namespace System.Runtime.InteropServices.WindowsRuntime { + + using System; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.WindowsRuntime; + using System.Runtime.CompilerServices; + using System.Security; + + + // Local definition of Windows.Foundation.IStringable + [ComImport] + [Guid("96369f54-8eb6-48f0-abce-c1b211e627c3")] + [WindowsRuntimeImport] + internal interface IStringable + { + string ToString(); + } + + internal class IStringableHelper + { + internal static string ToString(object obj) + { + IGetProxyTarget proxy = obj as IGetProxyTarget; + if (proxy != null) + obj = proxy.GetTarget(); + + // Check whether the type implements IStringable. + IStringable stringableType = obj as IStringable; + if (stringableType != null) + { + return stringableType.ToString(); + } + + return obj.ToString(); + } + } + + // + // Base class for every WinRT class + // We'll make it a ComImport and WindowsRuntimeImport in the type loader + // as C# compiler won't allow putting code in ComImport type + // + internal abstract class RuntimeClass : __ComObject + { + // + // Support for ToString/GetHashCode/Equals override + // + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern IntPtr GetRedirectedGetHashCodeMD(); + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern int RedirectGetHashCode(IntPtr pMD); + + [System.Security.SecuritySafeCritical] + public override int GetHashCode() + { + IntPtr pMD = GetRedirectedGetHashCodeMD(); + if (pMD == IntPtr.Zero) + return base.GetHashCode(); + return RedirectGetHashCode(pMD); + } + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern IntPtr GetRedirectedToStringMD(); + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern string RedirectToString(IntPtr pMD); + + [System.Security.SecuritySafeCritical] + public override string ToString() + { + // Check whether the type implements IStringable. + IStringable stringableType = this as IStringable; + if (stringableType != null) + { + return stringableType.ToString(); + } + else + { + IntPtr pMD = GetRedirectedToStringMD(); + + if (pMD == IntPtr.Zero) + return base.ToString(); + + return RedirectToString(pMD); + } + } + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern IntPtr GetRedirectedEqualsMD(); + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern bool RedirectEquals(object obj, IntPtr pMD); + + [System.Security.SecuritySafeCritical] + public override bool Equals(object obj) + { + IntPtr pMD = GetRedirectedEqualsMD(); + if (pMD == IntPtr.Zero) + return base.Equals(obj); + return RedirectEquals(obj, pMD); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToCollectionAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToCollectionAdapter.cs new file mode 100644 index 0000000000..5eeb0afcfc --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToCollectionAdapter.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the ICollection`1 interface on WinRT + // objects that support IVector`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not VectorToCollectionAdapter objects. Rather, they are of type + // IVector<T>. No actual VectorToCollectionAdapter object is ever instantiated. Thus, you will see + // a lot of expressions that cast "this" to "IVector<T>". + internal sealed class VectorToCollectionAdapter + { + private VectorToCollectionAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // int Count { get } + [Pure] + [SecurityCritical] + internal int Count<T>() + { + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + uint size = _this.Size; + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)size; + } + + // bool IsReadOnly { get } + [SecurityCritical] + internal bool IsReadOnly<T>() + { + return false; + } + + // void Add(T item) + [SecurityCritical] + internal void Add<T>(T item) + { + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + _this.Append(item); + } + + // void Clear() + [SecurityCritical] + internal void Clear<T>() + { + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + _this.Clear(); + } + + // bool Contains(T item) + [SecurityCritical] + internal bool Contains<T>(T item) + { + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + + uint index; + return _this.IndexOf(item, out index); + } + + // void CopyTo(T[] array, int arrayIndex) + [SecurityCritical] + internal void CopyTo<T>(T[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex"); + + if (array.Length <= arrayIndex && Count<T>() > 0) + throw new ArgumentException(Environment.GetResourceString("Argument_IndexOutOfArrayBounds")); + + if (array.Length - arrayIndex < Count<T>()) + throw new ArgumentException(Environment.GetResourceString("Argument_InsufficientSpaceToCopyCollection")); + + Contract.EndContractBlock(); + + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + int count = Count<T>(); + for (int i = 0; i < count; i++) + { + array[i + arrayIndex] = VectorToListAdapter.GetAt<T>(_this, (uint)i); + } + } + + // bool Remove(T item) + [SecurityCritical] + internal bool Remove<T>(T item) + { + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + + uint index; + bool exists = _this.IndexOf(item, out index); + + if (!exists) + return false; + + if (((uint)Int32.MaxValue) < index) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + VectorToListAdapter.RemoveAtHelper<T>(_this, index); + return true; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs new file mode 100644 index 0000000000..f27cc95176 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IList`1 interface on WinRT + // objects that support IVector`1. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not VectorToListAdapter objects. Rather, they are of type + // IVector<T>. No actual VectorToListAdapter object is ever instantiated. Thus, you will see + // a lot of expressions that cast "this" to "IVector<T>". + internal sealed class VectorToListAdapter + { + private VectorToListAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // T this[int index] { get } + [SecurityCritical] + internal T Indexer_Get<T>(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + return GetAt(_this, (uint)index); + } + + // T this[int index] { set } + [SecurityCritical] + internal void Indexer_Set<T>(int index, T value) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + SetAt(_this, (uint)index, value); + } + + // int IndexOf(T item) + [SecurityCritical] + internal int IndexOf<T>(T item) + { + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + + uint index; + bool exists = _this.IndexOf(item, out index); + + if (!exists) + return -1; + + if (((uint)Int32.MaxValue) < index) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)index; + } + + // void Insert(int index, T item) + [SecurityCritical] + internal void Insert<T>(int index, T item) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + InsertAtHelper<T>(_this, (uint)index, item); + } + + // void RemoveAt(int index) + [SecurityCritical] + internal void RemoveAt<T>(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + IVector<T> _this = JitHelpers.UnsafeCast<IVector<T>>(this); + RemoveAtHelper<T>(_this, (uint)index); + } + + // Helpers: + + internal static T GetAt<T>(IVector<T> _this, uint index) + { + try + { + return _this.GetAt(index); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + private static void SetAt<T>(IVector<T> _this, UInt32 index, T value) + { + try + { + _this.SetAt(index, value); + + // We deligate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + private static void InsertAtHelper<T>(IVector<T> _this, uint index, T item) + { + try + { + _this.InsertAt(index, item); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + + internal static void RemoveAtHelper<T>(IVector<T> _this, uint index) + { + try + { + _this.RemoveAt(index); + + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (__HResults.E_BOUNDS == ex._HResult) + throw new ArgumentOutOfRangeException("index"); + + throw; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorViewToReadOnlyCollectionAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorViewToReadOnlyCollectionAdapter.cs new file mode 100644 index 0000000000..4b4ae5d6fc --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorViewToReadOnlyCollectionAdapter.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Security; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // This is a set of stub methods implementing the support for the IReadOnlyCollection<T> interface on WinRT + // objects that support IVectorView<T>. Used by the interop mashaling infrastructure. + // + // The methods on this class must be written VERY carefully to avoid introducing security holes. + // That's because they are invoked with special "this"! The "this" object + // for all of these methods are not VectorViewToReadOnlyCollectionAdapter objects. Rather, they are of type + // IVectorView<T>. No actual VectorViewToReadOnlyCollectionAdapter object is ever instantiated. Thus, you will see + // a lot of expressions that cast "this" to "IVectorView<T>". + internal sealed class VectorViewToReadOnlyCollectionAdapter + { + private VectorViewToReadOnlyCollectionAdapter() + { + Contract.Assert(false, "This class is never instantiated"); + } + + // int Count { get } + [Pure] + [SecurityCritical] + internal int Count<T>() + { + IVectorView<T> _this = JitHelpers.UnsafeCast<IVectorView<T>>(this); + uint size = _this.Size; + if (((uint)Int32.MaxValue) < size) + { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CollectionBackingListTooLarge")); + } + + return (int)size; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsFoundationEventHandler.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsFoundationEventHandler.cs new file mode 100644 index 0000000000..1f6e373842 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsFoundationEventHandler.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // WindowsFoundationEventHandler<T> a copy of the definition for the Windows.Foundation.EventHandler<T> delegate + [Guid("9de1c535-6ae1-11e0-84e1-18a905bcc53f")] + [WindowsRuntimeImport] + internal delegate void WindowsFoundationEventHandler<T>(object sender, T args); +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferHelper.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferHelper.cs new file mode 100644 index 0000000000..0d59895bc4 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeBufferHelper.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; +using System.Security; +using System.Threading; + + +namespace System.Runtime.InteropServices.WindowsRuntime { + +/// <summary> +/// Exposes a helper method that allows <code>WindowsRuntimeBuffer : IBuffer, IBufferInternal</code> which is implemented in +/// <code>System.Runtime.WindowsRuntime.dll</code> to call into the VM. +/// </summary> +[FriendAccessAllowed] +internal static class WindowsRuntimeBufferHelper { + + + [SecurityCritical] + [DllImport(JitHelpers.QCall)] + [SuppressUnmanagedCodeSecurity] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private unsafe extern static void StoreOverlappedPtrInCCW(ObjectHandleOnStack windowsRuntimeBuffer, NativeOverlapped* overlapped); + + + [FriendAccessAllowed] + [SecurityCritical] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + internal unsafe static void StoreOverlappedInCCW(Object windowsRuntimeBuffer, NativeOverlapped* overlapped) { + + StoreOverlappedPtrInCCW(JitHelpers.GetObjectHandleOnStack(ref windowsRuntimeBuffer), overlapped); + } + +} // class WindowsRuntimeBufferHelper + +} // namespace + +// WindowsRuntimeBufferHelper.cs diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs new file mode 100644 index 0000000000..038efd5013 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs @@ -0,0 +1,1335 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Security; + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + // Helper functions to manually marshal data between .NET and WinRT + public static class WindowsRuntimeMarshal + { + // Add an event handler to a Windows Runtime style event, such that it can be removed via a delegate + // lookup at a later time. This method adds the handler to the add method using the supplied + // delegate. It then stores the corresponding token in a dictionary for easy access by RemoveEventHandler + // later. Note that the dictionary is indexed by the remove method that will be used for RemoveEventHandler + // so the removeMethod given here must match the remove method supplied there exactly. + [SecurityCritical] + public static void AddEventHandler<T>(Func<T, EventRegistrationToken> addMethod, + Action<EventRegistrationToken> removeMethod, + T handler) + { + if (addMethod == null) + throw new ArgumentNullException("addMethod"); + if (removeMethod == null) + throw new ArgumentNullException("removeMethod"); + Contract.EndContractBlock(); + + // Managed code allows adding a null event handler, the effect is a no-op. To match this behavior + // for WinRT events, we simply ignore attempts to add null. + if (handler == null) + { + return; + } + + // Delegate to managed event registration implementation or native event registration implementation + // They have completely different implementation because native side has its own unique problem to solve - + // there could be more than one RCW for the same COM object + // it would be more confusing and less-performant if we were to merge them together + object target = removeMethod.Target; + if (target == null || Marshal.IsComObject(target)) + NativeOrStaticEventRegistrationImpl.AddEventHandler<T>(addMethod, removeMethod, handler); + else + ManagedEventRegistrationImpl.AddEventHandler<T>(addMethod, removeMethod, handler); + } + + // Remove the delegate handler from the Windows Runtime style event registration by looking for + // its token, previously stored via AddEventHandler<T> + [SecurityCritical] + public static void RemoveEventHandler<T>(Action<EventRegistrationToken> removeMethod, T handler) + { + if (removeMethod == null) + throw new ArgumentNullException("removeMethod"); + Contract.EndContractBlock(); + + // Managed code allows removing a null event handler, the effect is a no-op. To match this behavior + // for WinRT events, we simply ignore attempts to remove null. + if (handler == null) + { + return; + } + + // Delegate to managed event registration implementation or native event registration implementation + // They have completely different implementation because native side has its own unique problem to solve - + // there could be more than one RCW for the same COM object + // it would be more confusing and less-performant if we were to merge them together + object target = removeMethod.Target; + if (target == null || Marshal.IsComObject(target)) + NativeOrStaticEventRegistrationImpl.RemoveEventHandler<T>(removeMethod, handler); + else + ManagedEventRegistrationImpl.RemoveEventHandler<T>(removeMethod, handler); + } + + [SecurityCritical] + public static void RemoveAllEventHandlers(Action<EventRegistrationToken> removeMethod) + { + if (removeMethod == null) + throw new ArgumentNullException("removeMethod"); + Contract.EndContractBlock(); + + // Delegate to managed event registration implementation or native event registration implementation + // They have completely different implementation because native side has its own unique problem to solve - + // there could be more than one RCW for the same COM object + // it would be more confusing and less-performant if we were to merge them together + object target = removeMethod.Target; + if (target == null || Marshal.IsComObject(target)) + NativeOrStaticEventRegistrationImpl.RemoveAllEventHandlers(removeMethod); + else + ManagedEventRegistrationImpl.RemoveAllEventHandlers(removeMethod); + } + + // Returns the total cache size + // Used by test only to verify we don't leak event cache + internal static int GetRegistrationTokenCacheSize() + { + int count = 0; + + if (ManagedEventRegistrationImpl.s_eventRegistrations != null) + { + lock (ManagedEventRegistrationImpl.s_eventRegistrations) + { + count += ManagedEventRegistrationImpl.s_eventRegistrations.Keys.Count; + } + } + + if (NativeOrStaticEventRegistrationImpl.s_eventRegistrations != null) + { + lock (NativeOrStaticEventRegistrationImpl.s_eventRegistrations) + { + count += NativeOrStaticEventRegistrationImpl.s_eventRegistrations.Count; + } + } + + return count; + } + + // + // Optimized version of List of EventRegistrationToken + // It is made a struct to reduce overhead + // + internal struct EventRegistrationTokenList + { + private EventRegistrationToken firstToken; // Optimization for common case where there is only one token + private List<EventRegistrationToken> restTokens; // Rest of the tokens + + internal EventRegistrationTokenList(EventRegistrationToken token) + { + firstToken = token; + restTokens = null; + } + + internal EventRegistrationTokenList(EventRegistrationTokenList list) + { + firstToken = list.firstToken; + restTokens = list.restTokens; + } + + // Push a new token into this list + // Returns true if you need to copy back this list into the dictionary (so that you + // don't lose change outside the dictionary). false otherwise. + public bool Push(EventRegistrationToken token) + { + bool needCopy = false; + + if (restTokens == null) + { + restTokens = new List<EventRegistrationToken>(); + needCopy = true; + } + + restTokens.Add(token); + + return needCopy; + } + + // Pops the last token + // Returns false if no more tokens left, true otherwise + public bool Pop(out EventRegistrationToken token) + { + // Only 1 token in this list and we just removed the last token + if (restTokens == null || restTokens.Count == 0) + { + token = firstToken; + return false; + } + + int last = restTokens.Count - 1; + token = restTokens[last]; + restTokens.RemoveAt(last); + + return true; + } + + public void CopyTo(List<EventRegistrationToken> tokens) + { + tokens.Add(firstToken); + if (restTokens != null) + tokens.AddRange(restTokens); + } + } + + // + // Event registration support for managed objects events & static events + // + internal static class ManagedEventRegistrationImpl + { + // Mappings of delegates registered for events -> their registration tokens. + // These mappings are stored indexed by the remove method which can be used to undo the registrations. + // + // The full structure of this table is: + // object the event is being registered on -> + // Table [RemoveMethod] -> + // Table [Handler] -> Token + // + // Note: There are a couple of optimizations I didn't do here because they don't make sense for managed events: + // 1. Flatten the event cache (see EventCacheKey in native WinRT event implementation below) + // + // This is because managed events use ConditionalWeakTable to hold Objects->(Event->(Handler->Tokens)), + // and when object goes away everything else will be nicely cleaned up. If I flatten it like native WinRT events, + // I'll have to use Dictionary (as ConditionalWeakTable won't work - nobody will hold the new key alive anymore) + // instead, and that means I'll have to add more code from native WinRT events into managed WinRT event to support + // self-cleanup in the finalization, as well as reader/writer lock to protect against race conditions in the finalization, + // which adds a lot more complexity and doesn't really worth it. + // + // 2. Use conditionalWeakTable to hold Handler->Tokens. + // + // The reason is very simple - managed object use dictionary (see EventRegistrationTokenTable) to hold delegates alive. + // If the delegates aren't alive, it means either they have been unsubscribed, or the object itself is gone, + // and in either case, they've been already taken care of. + // + internal volatile static + ConditionalWeakTable<object, Dictionary<MethodInfo, Dictionary<object, EventRegistrationTokenList>>> s_eventRegistrations = + new ConditionalWeakTable<object, Dictionary<MethodInfo, Dictionary<object, EventRegistrationTokenList>>>(); + + [SecurityCritical] + internal static void AddEventHandler<T>(Func<T, EventRegistrationToken> addMethod, + Action<EventRegistrationToken> removeMethod, + T handler) + { + Contract.Requires(addMethod != null); + Contract.Requires(removeMethod != null); + + // Add the method, and make a note of the token -> delegate mapping. + object instance = removeMethod.Target; + Dictionary<object, EventRegistrationTokenList> registrationTokens = GetEventRegistrationTokenTable(instance, removeMethod); + EventRegistrationToken token = addMethod(handler); + lock (registrationTokens) + { + EventRegistrationTokenList tokens; + if (!registrationTokens.TryGetValue(handler, out tokens)) + { + tokens = new EventRegistrationTokenList(token); + registrationTokens[handler] = tokens; + } + else + { + bool needCopy = tokens.Push(token); + + // You need to copy back this list into the dictionary (so that you don't lose change outside dictionary) + if (needCopy) + registrationTokens[handler] = tokens; + } + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event subscribed for managed instance = " + instance + ", handler = " + handler + "\n"); + } + } + + // Get the event registration token table for an event. These are indexed by the remove method of the event. + private static Dictionary<object, EventRegistrationTokenList> GetEventRegistrationTokenTable(object instance, Action<EventRegistrationToken> removeMethod) + { + Contract.Requires(instance != null); + Contract.Requires(removeMethod != null); + Contract.Requires(s_eventRegistrations != null); + + lock (s_eventRegistrations) + { + Dictionary<MethodInfo, Dictionary<object, EventRegistrationTokenList>> instanceMap = null; + if (!s_eventRegistrations.TryGetValue(instance, out instanceMap)) + { + instanceMap = new Dictionary<MethodInfo, Dictionary<object, EventRegistrationTokenList>>(); + s_eventRegistrations.Add(instance, instanceMap); + } + + Dictionary<object, EventRegistrationTokenList> tokens = null; + if (!instanceMap.TryGetValue(removeMethod.Method, out tokens)) + { + tokens = new Dictionary<object, EventRegistrationTokenList>(); + instanceMap.Add(removeMethod.Method, tokens); + } + + return tokens; + } + } + + [SecurityCritical] + internal static void RemoveEventHandler<T>(Action<EventRegistrationToken> removeMethod, T handler) + { + Contract.Requires(removeMethod != null); + + object instance = removeMethod.Target; + Dictionary<object, EventRegistrationTokenList> registrationTokens = GetEventRegistrationTokenTable(instance, removeMethod); + EventRegistrationToken token; + + lock (registrationTokens) + { + EventRegistrationTokenList tokens; + + // Failure to find a registration for a token is not an error - it's simply a no-op. + if (!registrationTokens.TryGetValue(handler, out tokens)) + { + BCLDebug.Log("INTEROP", "[WinRT_Eventing] no registrationTokens found for instance=" + instance + ", handler= " + handler + "\n"); + + return; + } + + // Select a registration token to unregister + // We don't care which one but I'm returning the last registered token to be consistent + // with native event registration implementation + bool moreItems = tokens.Pop(out token); + if (!moreItems) + { + // Remove it from cache if this list become empty + // This must be done because EventRegistrationTokenList now becomes invalid + // (mostly because there is no safe default value for EventRegistrationToken to express 'no token') + // NOTE: We should try to remove registrationTokens itself from cache if it is empty, otherwise + // we could run into a race condition where one thread removes it from cache and another thread adds + // into the empty registrationToken table + registrationTokens.Remove(handler); + } + } + + removeMethod(token); + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event unsubscribed for managed instance = " + instance + ", handler = " + handler + ", token = " + token.m_value + "\n"); + } + + [SecurityCritical] + internal static void RemoveAllEventHandlers(Action<EventRegistrationToken> removeMethod) + { + Contract.Requires(removeMethod != null); + + object instance = removeMethod.Target; + Dictionary<object, EventRegistrationTokenList> registrationTokens = GetEventRegistrationTokenTable(instance, removeMethod); + + List<EventRegistrationToken> tokensToRemove = new List<EventRegistrationToken>(); + + lock (registrationTokens) + { + // Copy all tokens to tokensToRemove array which later we'll call removeMethod on + // outside this lock + foreach (EventRegistrationTokenList tokens in registrationTokens.Values) + { + tokens.CopyTo(tokensToRemove); + } + + // Clear the dictionary - at this point all event handlers are no longer in the cache + // but they are not removed yet + registrationTokens.Clear(); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Cache cleared for managed instance = " + instance + "\n"); + } + + // + // Remove all handlers outside the lock + // + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Start removing all events for instance = " + instance + "\n"); + CallRemoveMethods(removeMethod, tokensToRemove); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Finished removing all events for instance = " + instance + "\n"); + } + } + + // + // WinRT event registration implementation code + // + internal static class NativeOrStaticEventRegistrationImpl + { + // + // Key = (target object, event) + // We use a key of object+event to save an extra dictionary + // + internal struct EventCacheKey + { + internal object target; + internal MethodInfo method; + + public override string ToString() + { + return "(" + target + ", " + method + ")"; + } + } + + internal class EventCacheKeyEqualityComparer : IEqualityComparer<EventCacheKey> + { + public bool Equals(EventCacheKey lhs, EventCacheKey rhs) + { + return (Object.Equals(lhs.target, rhs.target) && Object.Equals(lhs.method, rhs.method)); + } + + public int GetHashCode(EventCacheKey key) + { + return key.target.GetHashCode() ^ key.method.GetHashCode(); + } + } + + // + // EventRegistrationTokenListWithCount + // + // A list of EventRegistrationTokens that maintains a count + // + // The reason this needs to be a separate class is that we need a finalizer for this class + // If the delegate is collected, it will take this list away with it (due to dependent handles), + // and we need to remove the PerInstancEntry from cache + // See ~EventRegistrationTokenListWithCount for more details + // + internal class EventRegistrationTokenListWithCount + { + private TokenListCount _tokenListCount; + EventRegistrationTokenList _tokenList; + + internal EventRegistrationTokenListWithCount(TokenListCount tokenListCount, EventRegistrationToken token) + { + _tokenListCount = tokenListCount; + _tokenListCount.Inc(); + + _tokenList = new EventRegistrationTokenList(token); + } + + ~EventRegistrationTokenListWithCount() + { + // Decrement token list count + // This is need to correctly keep trace of number of tokens for EventCacheKey + // and remove it from cache when the token count drop to 0 + // we don't need to take locks for decrement the count - we only need to take a global + // lock when we decide to destroy cache for the IUnknown */type instance + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Finalizing EventRegistrationTokenList for " + _tokenListCount.Key + "\n"); + _tokenListCount.Dec(); + } + + public void Push(EventRegistrationToken token) + { + // Since EventRegistrationTokenListWithCount is a reference type, there is no need + // to copy back. Ignore the return value + _tokenList.Push(token); + } + + public bool Pop(out EventRegistrationToken token) + { + return _tokenList.Pop(out token); + } + + public void CopyTo(List<EventRegistrationToken> tokens) + { + _tokenList.CopyTo(tokens); + } + } + + // + // Maintains the number of tokens for a particular EventCacheKey + // TokenListCount is a class for two reasons: + // 1. Efficient update in the Dictionary to avoid lookup twice to update the value + // 2. Update token count without taking a global lock. Only takes a global lock when drop to 0 + // + internal class TokenListCount + { + private int _count; + private EventCacheKey _key; + + internal TokenListCount(EventCacheKey key) + { + _key = key; + } + + internal EventCacheKey Key + { + + get { return _key; } + } + + internal void Inc() + { + int newCount = Interlocked.Increment(ref _count); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Incremented TokenListCount for " + _key + ", Value = " + newCount + "\n"); + } + + internal void Dec() + { + // Avoid racing with Add/Remove event entries into the cache + // You don't want this removing the key in the middle of a Add/Remove + s_eventCacheRWLock.AcquireWriterLock(Timeout.Infinite); + try + { + int newCount = Interlocked.Decrement(ref _count); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Decremented TokenListCount for " + _key + ", Value = " + newCount + "\n"); + if (newCount == 0) + CleanupCache(); + } + finally + { + s_eventCacheRWLock.ReleaseWriterLock(); + } + } + + private void CleanupCache() + { + // Time to destroy cache for this IUnknown */type instance + // because the total token list count has dropped to 0 and we don't have any events subscribed + Contract.Requires(s_eventRegistrations != null); + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Removing " + _key + " from cache" + "\n"); + s_eventRegistrations.Remove(_key); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] s_eventRegistrations size = " + s_eventRegistrations.Count + "\n"); + } + } + + internal struct EventCacheEntry + { + // [Handler] -> Token + internal ConditionalWeakTable<object, EventRegistrationTokenListWithCount> registrationTable; + + // Maintains current total count for the EventRegistrationTokenListWithCount for this event cache key + internal TokenListCount tokenListCount; + } + + // Mappings of delegates registered for events -> their registration tokens. + // These mappings are stored indexed by the remove method which can be used to undo the registrations. + // + // The full structure of this table is: + // EventCacheKey (instanceKey, eventMethod) -> EventCacheEntry (Handler->tokens) + // + // A InstanceKey is the IUnknown * or static type instance + // + // Couple of things to note: + // 1. We need to use IUnknown* because we want to be able to unscribe to the event for another RCW + // based on the same COM object. For example: + // m_canvas.GetAt(0).Event += Func; + // m_canvas.GetAt(0).Event -= Func; // GetAt(0) might create a new RCW + // + // 2. Handler->Token is a ConditionalWeakTable because we don't want to keep the delegate alive + // and we want EventRegistrationTokenListWithCount to be finalized after the delegate is no longer alive + // 3. It is possible another COM object is created at the same address + // before the entry in cache is destroyed. More specifically, + // a. The same delegate is being unsubscribed. In this case we'll give them a + // stale token - unlikely to be a problem + // b. The same delegate is subscribed then unsubscribed. We need to make sure give + // them the latest token in this case. This is guaranteed by always giving the last token and always use equality to + // add/remove event handlers + internal volatile static Dictionary<EventCacheKey, EventCacheEntry> s_eventRegistrations = + new Dictionary<EventCacheKey, EventCacheEntry>(new EventCacheKeyEqualityComparer()); + + // Prevent add/remove handler code to run at the same with with cache cleanup code + private volatile static MyReaderWriterLock s_eventCacheRWLock = new MyReaderWriterLock(); + + // Get InstanceKey to use in the cache + [SecuritySafeCritical] + private static object GetInstanceKey(Action<EventRegistrationToken> removeMethod) + { + object target = removeMethod.Target; + Contract.Assert(target == null || Marshal.IsComObject(target), "Must be null or a RCW"); + if (target == null) + return removeMethod.Method.DeclaringType; + + // Need the "Raw" IUnknown pointer for the RCW that is not bound to the current context + return (object) Marshal.GetRawIUnknownForComObjectNoAddRef(target); + } + + [SecurityCritical] + internal static void AddEventHandler<T>(Func<T, EventRegistrationToken> addMethod, + Action<EventRegistrationToken> removeMethod, + T handler) + { + // The instanceKey will be IUnknown * of the target object + object instanceKey = GetInstanceKey(removeMethod); + + // Call addMethod outside of RW lock + // At this point we don't need to worry about race conditions and we can avoid deadlocks + // if addMethod waits on finalizer thread + // If we later throw we need to remove the method + EventRegistrationToken token = addMethod(handler); + + bool tokenAdded = false; + + try + { + EventRegistrationTokenListWithCount tokens; + + // + // The whole add/remove code has to be protected by a reader/writer lock + // Add/Remove cannot run at the same time with cache cleanup but Add/Remove can run at the same time + // + s_eventCacheRWLock.AcquireReaderLock(Timeout.Infinite); + try + { + // Add the method, and make a note of the delegate -> token mapping. + TokenListCount tokenListCount; + ConditionalWeakTable<object, EventRegistrationTokenListWithCount> registrationTokens = GetOrCreateEventRegistrationTokenTable(instanceKey, removeMethod, out tokenListCount); + lock (registrationTokens) + { + // + // We need to find the key that equals to this handler + // Suppose we have 3 handlers A, B, C that are equal (refer to the same object and method), + // the first handler (let's say A) will be used as the key and holds all the tokens. + // We don't need to hold onto B and C, because the COM object itself will keep them alive, + // and they won't die anyway unless the COM object dies or they get unsubscribed. + // It may appear that it is fine to hold A, B, C, and add them and their corresponding tokens + // into registrationTokens table. However, this is very dangerous, because this COM object + // may die, but A, B, C might not get collected yet, and another COM object comes into life + // with the same IUnknown address, and we subscribe event B. In this case, the right token + // will be added into B's token list, but once we unsubscribe B, we might end up removing + // the last token in C, and that may lead to crash. + // + object key = registrationTokens.FindEquivalentKeyUnsafe(handler, out tokens); + if (key == null) + { + tokens = new EventRegistrationTokenListWithCount(tokenListCount, token); + registrationTokens.Add(handler, tokens); + } + else + { + tokens.Push(token); + } + + tokenAdded = true; + } + } + finally + { + s_eventCacheRWLock.ReleaseReaderLock(); + } + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event subscribed for instance = " + instanceKey + ", handler = " + handler + "\n"); + } + catch(Exception) + { + // If we've already added the token and go there, we don't need to "UNDO" anything + if (!tokenAdded) + { + // Otherwise, "Undo" addMethod if any exception occurs + // There is no need to cleanup our data structure as we haven't added the token yet + removeMethod(token); + } + + + throw; + } + } + + private static ConditionalWeakTable<object, EventRegistrationTokenListWithCount> GetEventRegistrationTokenTableNoCreate(object instance, Action<EventRegistrationToken> removeMethod, out TokenListCount tokenListCount) + { + Contract.Requires(instance != null); + Contract.Requires(removeMethod != null); + + return GetEventRegistrationTokenTableInternal(instance, removeMethod, out tokenListCount, /* createIfNotFound = */ false); + } + + private static ConditionalWeakTable<object, EventRegistrationTokenListWithCount> GetOrCreateEventRegistrationTokenTable(object instance, Action<EventRegistrationToken> removeMethod, out TokenListCount tokenListCount) + { + Contract.Requires(instance != null); + Contract.Requires(removeMethod != null); + + return GetEventRegistrationTokenTableInternal(instance, removeMethod, out tokenListCount, /* createIfNotFound = */ true); + } + + // Get the event registration token table for an event. These are indexed by the remove method of the event. + private static ConditionalWeakTable<object, EventRegistrationTokenListWithCount> GetEventRegistrationTokenTableInternal(object instance, Action<EventRegistrationToken> removeMethod, out TokenListCount tokenListCount, bool createIfNotFound) + { + Contract.Requires(instance != null); + Contract.Requires(removeMethod != null); + Contract.Requires(s_eventRegistrations != null); + + EventCacheKey eventCacheKey; + eventCacheKey.target = instance; + eventCacheKey.method = removeMethod.Method; + + lock (s_eventRegistrations) + { + EventCacheEntry eventCacheEntry; + if (!s_eventRegistrations.TryGetValue(eventCacheKey, out eventCacheEntry)) + { + if (!createIfNotFound) + { + // No need to create an entry in this case + tokenListCount = null; + return null; + } + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Adding (" + instance + "," + removeMethod.Method + ") into cache" + "\n"); + + eventCacheEntry = new EventCacheEntry(); + eventCacheEntry.registrationTable = new ConditionalWeakTable<object, EventRegistrationTokenListWithCount>(); + eventCacheEntry.tokenListCount = new TokenListCount(eventCacheKey); + + s_eventRegistrations.Add(eventCacheKey, eventCacheEntry); + } + + tokenListCount = eventCacheEntry.tokenListCount; + + return eventCacheEntry.registrationTable; + } + } + + [SecurityCritical] + internal static void RemoveEventHandler<T>(Action<EventRegistrationToken> removeMethod, T handler) + { + object instanceKey = GetInstanceKey(removeMethod); + + EventRegistrationToken token; + + // + // The whole add/remove code has to be protected by a reader/writer lock + // Add/Remove cannot run at the same time with cache cleanup but Add/Remove can run at the same time + // + s_eventCacheRWLock.AcquireReaderLock(Timeout.Infinite); + try + { + TokenListCount tokenListCount; + ConditionalWeakTable<object, EventRegistrationTokenListWithCount> registrationTokens = GetEventRegistrationTokenTableNoCreate(instanceKey, removeMethod, out tokenListCount); + if (registrationTokens == null) + { + // We have no information regarding this particular instance (IUnknown*/type) - just return + // This is necessary to avoid leaking empty dictionary/conditionalWeakTables for this instance + BCLDebug.Log("INTEROP", "[WinRT_Eventing] no registrationTokens found for instance=" + instanceKey + ", handler= " + handler + "\n"); + return; + } + + lock (registrationTokens) + { + EventRegistrationTokenListWithCount tokens; + + // Note: + // When unsubscribing events, we allow subscribing the event using a different delegate + // (but with the same object/method), so we need to find the first delegate that matches + // and unsubscribe it + // It actually doesn't matter which delegate - as long as it matches + // Note that inside TryGetValueWithValueEquality we assumes that any delegate + // with the same value equality would have the same hash code + object key = registrationTokens.FindEquivalentKeyUnsafe(handler, out tokens); + Contract.Assert((key != null && tokens != null) || (key == null && tokens == null), + "key and tokens must be both null or non-null"); + if (tokens == null) + { + // Failure to find a registration for a token is not an error - it's simply a no-op. + BCLDebug.Log("INTEROP", "[WinRT_Eventing] no token list found for instance=" + instanceKey + ", handler= " + handler + "\n"); + return; + } + + // Select a registration token to unregister + // Note that we need to always get the last token just in case another COM object + // is created at the same address before the entry for the old one goes away. + // See comments above s_eventRegistrations for more details + bool moreItems = tokens.Pop(out token); + + // If the last token is removed from token list, we need to remove it from the cache + // otherwise FindEquivalentKeyUnsafe may found this empty token list even though there could be other + // equivalent keys in there with non-0 token list + if (!moreItems) + { + // Remove it from (handler)->(tokens) + // NOTE: We should not check whether registrationTokens has 0 entries and remove it from the cache + // (just like managed event implementation), because this might have raced with the finalizer of + // EventRegistrationTokenList + registrationTokens.Remove(key); + } + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event unsubscribed for managed instance = " + instanceKey + ", handler = " + handler + ", token = " + token.m_value + "\n"); + } + } + finally + { + s_eventCacheRWLock.ReleaseReaderLock(); + } + + // Call removeMethod outside of RW lock + // At this point we don't need to worry about race conditions and we can avoid deadlocks + // if removeMethod waits on finalizer thread + removeMethod(token); + } + + [SecurityCritical] + internal static void RemoveAllEventHandlers(Action<EventRegistrationToken> removeMethod) + { + object instanceKey = GetInstanceKey(removeMethod); + + List<EventRegistrationToken> tokensToRemove = new List<EventRegistrationToken>(); + + // + // The whole add/remove code has to be protected by a reader/writer lock + // Add/Remove cannot run at the same time with cache cleanup but Add/Remove can run at the same time + // + s_eventCacheRWLock.AcquireReaderLock(Timeout.Infinite); + try + { + TokenListCount tokenListCount; + ConditionalWeakTable<object, EventRegistrationTokenListWithCount> registrationTokens = GetEventRegistrationTokenTableNoCreate(instanceKey, removeMethod, out tokenListCount); + if (registrationTokens == null) + { + // We have no information regarding this particular instance (IUnknown*/type) - just return + // This is necessary to avoid leaking empty dictionary/conditionalWeakTables for this instance + return; + } + + lock (registrationTokens) + { + // Copy all tokens to tokensToRemove array which later we'll call removeMethod on + // outside this lock + foreach (EventRegistrationTokenListWithCount tokens in registrationTokens.Values) + { + tokens.CopyTo(tokensToRemove); + } + + // Clear the table - at this point all event handlers are no longer in the cache + // but they are not removed yet + registrationTokens.Clear(); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Cache cleared for managed instance = " + instanceKey + "\n"); + } + } + finally + { + s_eventCacheRWLock.ReleaseReaderLock(); + } + + // + // Remove all handlers outside the lock + // + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Start removing all events for instance = " + instanceKey + "\n"); + CallRemoveMethods(removeMethod, tokensToRemove); + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Finished removing all events for instance = " + instanceKey + "\n"); + } + + + internal class ReaderWriterLockTimedOutException : ApplicationException + { + } + + /// I borrowed Vance's reader writer lock implementation from his blog as ReaderWriterLockSlim is + /// available in System.Core.dll! + /// + /// <summary> + /// A reader-writer lock implementation that is intended to be simple, yet very + /// efficient. In particular only 1 interlocked operation is taken for any lock + /// operation (we use spin locks to achieve this). The spin lock is never held + /// for more than a few instructions (in particular, we never call event APIs + /// or in fact any non-trivial API while holding the spin lock). + /// + /// Currently this ReaderWriterLock does not support recurision, however it is + /// not hard to add + /// </summary> + internal class MyReaderWriterLock + { + // Lock specifiation for myLock: This lock protects exactly the local fields associted + // instance of MyReaderWriterLock. It does NOT protect the memory associted with the + // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). + int myLock; + + // Who owns the lock owners > 0 => readers + // owners = -1 means there is one writer. Owners must be >= -1. + int owners; + + // These variables allow use to avoid Setting events (which is expensive) if we don't have to. + uint numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent + uint numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent + + // conditions we wait on. + EventWaitHandle writeEvent; // threads waiting to aquire a write lock go here. + EventWaitHandle readEvent; // threads waiting to aquire a read lock go here (will be released in bulk) + + internal MyReaderWriterLock() + { + // All state can start out zeroed. + } + + internal void AcquireReaderLock(int millisecondsTimeout) + { + EnterMyLock(); + for (; ; ) + { + // We can enter a read lock if there are only read-locks have been given out + // and a writer is not trying to get in. + if (owners >= 0 && numWriteWaiters == 0) + { + // Good case, there is no contention, we are basically done + owners++; // Indicate we have another reader + break; + } + + // Drat, we need to wait. Mark that we have waiters and wait. + if (readEvent == null) // Create the needed event + { + LazyCreateEvent(ref readEvent, false); + continue; // since we left the lock, start over. + } + + WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout); + } + ExitMyLock(); + } + + internal void AcquireWriterLock(int millisecondsTimeout) + { + EnterMyLock(); + for (; ; ) + { + if (owners == 0) + { + // Good case, there is no contention, we are basically done + owners = -1; // indicate we have a writer. + break; + } + + // Drat, we need to wait. Mark that we have waiters and wait. + if (writeEvent == null) // create the needed event. + { + LazyCreateEvent(ref writeEvent, true); + continue; // since we left the lock, start over. + } + + WaitOnEvent(writeEvent, ref numWriteWaiters, millisecondsTimeout); + } + ExitMyLock(); + } + + internal void ReleaseReaderLock() + { + EnterMyLock(); + Contract.Assert(owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken"); + --owners; + ExitAndWakeUpAppropriateWaiters(); + } + + internal void ReleaseWriterLock() + { + EnterMyLock(); + Contract.Assert(owners == -1, "Calling ReleaseWriterLock when no write lock is held"); + owners++; + ExitAndWakeUpAppropriateWaiters(); + } + + /// <summary> + /// A routine for lazily creating a event outside the lock (so if errors + /// happen they are outside the lock and that we don't do much work + /// while holding a spin lock). If all goes well, reenter the lock and + /// set 'waitEvent' + /// </summary> + private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) { + Contract.Assert(myLock != 0, "Lock must be held"); + Contract.Assert(waitEvent == null, "Wait event must be null"); + + ExitMyLock(); + EventWaitHandle newEvent; + if (makeAutoResetEvent) + newEvent = new AutoResetEvent(false); + else + newEvent = new ManualResetEvent(false); + EnterMyLock(); + if (waitEvent == null) // maybe someone snuck in. + waitEvent = newEvent; + } + + /// <summary> + /// Waits on 'waitEvent' with a timeout of 'millisceondsTimeout. + /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. + /// </summary> + private void WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout) + { + Contract.Assert(myLock != 0, "Lock must be held"); + + waitEvent.Reset(); + numWaiters++; + + bool waitSuccessful = false; + ExitMyLock(); // Do the wait outside of any lock + try + { + if (!waitEvent.WaitOne(millisecondsTimeout, false)) + throw new ReaderWriterLockTimedOutException(); + + waitSuccessful = true; + } + finally + { + EnterMyLock(); + --numWaiters; + if (!waitSuccessful) // We are going to throw for some reason. Exit myLock. + ExitMyLock(); + } + } + + /// <summary> + /// Determines the appropriate events to set, leaves the locks, and sets the events. + /// </summary> + private void ExitAndWakeUpAppropriateWaiters() + { + Contract.Assert(myLock != 0, "Lock must be held"); + + if (owners == 0 && numWriteWaiters > 0) + { + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + writeEvent.Set(); // release one writer. + } + else if (owners >= 0 && numReadWaiters != 0) + { + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) + readEvent.Set(); // release all readers. + } + else + ExitMyLock(); + } + + private void EnterMyLock() { + if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0) + EnterMyLockSpin(); + } + + private void EnterMyLockSpin() + { + for (int i = 0; ;i++) + { + if (i < 3 && Environment.ProcessorCount > 1) + Thread.SpinWait(20); // Wait a few dozen instructions to let another processor release lock. + else + Thread.Sleep(0); // Give up my quantum. + + if (Interlocked.CompareExchange(ref myLock, 1, 0) == 0) + return; + } + } + private void ExitMyLock() + { + Contract.Assert(myLock != 0, "Exiting spin lock that is not held"); + myLock = 0; + } + }; + } + + // + // Call removeMethod on each token and aggregate all exceptions thrown from removeMethod into one in case of failure + // + internal static void CallRemoveMethods(Action<EventRegistrationToken> removeMethod, List<EventRegistrationToken> tokensToRemove) + { + + List<Exception> exceptions = new List<Exception>(); + + foreach (EventRegistrationToken token in tokensToRemove) + { + try + { + removeMethod(token); + } + catch(Exception ex) + { + exceptions.Add(ex); + } + + BCLDebug.Log("INTEROP", "[WinRT_Eventing] Event unsubscribed for token = " + token.m_value + "\n"); + } + + if (exceptions.Count > 0) + throw new AggregateException(exceptions.ToArray()); + } + + [SecurityCritical] + internal static unsafe string HStringToString(IntPtr hstring) + { + Contract.Requires(Environment.IsWinRTSupported); + + // There is no difference between a null and empty HSTRING + if (hstring == IntPtr.Zero) + { + return String.Empty; + } + + unsafe + { + uint length; + char* rawBuffer = UnsafeNativeMethods.WindowsGetStringRawBuffer(hstring, &length); + return new String(rawBuffer, 0, checked((int)length)); + } + } + + internal static Exception GetExceptionForHR(int hresult, Exception innerException, string messageResource) + { + Exception e = null; + if (innerException != null) + { + string message = innerException.Message; + if (message == null && messageResource != null) + { + message = Environment.GetResourceString(messageResource); + } + e = new Exception(message, innerException); + } + else + { + string message = (messageResource != null ? Environment.GetResourceString(messageResource) : null); + e = new Exception(message); + } + + e.SetErrorCode(hresult); + return e; + } + + internal static Exception GetExceptionForHR(int hresult, Exception innerException) + { + return GetExceptionForHR(hresult, innerException, null); + } + + private static bool s_haveBlueErrorApis = true; + + [SecurityCritical] + private static bool RoOriginateLanguageException(int error, string message, IntPtr languageException) + { + if (s_haveBlueErrorApis) + { + try + { + return UnsafeNativeMethods.RoOriginateLanguageException(error, message, languageException); + } + catch (EntryPointNotFoundException) + { + s_haveBlueErrorApis = false; + } + } + + return false; + } + + [SecurityCritical] + private static void RoReportUnhandledError(IRestrictedErrorInfo error) + { + if (s_haveBlueErrorApis) + { + try + { + UnsafeNativeMethods.RoReportUnhandledError(error); + } + catch (EntryPointNotFoundException) + { + s_haveBlueErrorApis = false; + } + } + } + + private static Guid s_iidIErrorInfo = new Guid(0x1CF2B120, 0x547D, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19); + + /// <summary> + /// Report that an exception has occurred which went user unhandled. This allows the global error handler + /// for the application to be invoked to process the error. + /// </summary> + /// <returns>true if the error was reported, false if not (ie running on Win8)</returns> + [FriendAccessAllowed] + [SecuritySafeCritical] + internal static bool ReportUnhandledError(Exception e) + { + // Only report to the WinRT global exception handler in modern apps + if (!AppDomain.IsAppXModel()) + { + return false; + } + + // If we don't have the capability to report to the global error handler, early out + if (!s_haveBlueErrorApis) + { + return false; + } + + if (e != null) + { + IntPtr exceptionIUnknown = IntPtr.Zero; + IntPtr exceptionIErrorInfo = IntPtr.Zero; + try + { + // Get an IErrorInfo for the current exception and originate it as a langauge error in order to have + // Windows generate an IRestrictedErrorInfo corresponding to the exception object. We can then + // notify the global error handler that this IRestrictedErrorInfo instance represents an exception that + // went unhandled in managed code. + // + // Note that we need to get an IUnknown for the exception object and then QI for IErrorInfo since Exception + // doesn't implement IErrorInfo in managed code - only its CCW does. + exceptionIUnknown = Marshal.GetIUnknownForObject(e); + if (exceptionIUnknown != IntPtr.Zero) + { + Marshal.QueryInterface(exceptionIUnknown, ref s_iidIErrorInfo, out exceptionIErrorInfo); + if (exceptionIErrorInfo != IntPtr.Zero) + { + if (RoOriginateLanguageException(Marshal.GetHRForException_WinRT(e), e.Message, exceptionIErrorInfo)) + { + IRestrictedErrorInfo restrictedError = UnsafeNativeMethods.GetRestrictedErrorInfo(); + if (restrictedError != null) + { + RoReportUnhandledError(restrictedError); + return true; + } + } + } + } + } + finally + { + if (exceptionIErrorInfo != IntPtr.Zero) + { + Marshal.Release(exceptionIErrorInfo); + } + + if (exceptionIUnknown != IntPtr.Zero) + { + Marshal.Release(exceptionIUnknown); + } + } + } + + // If we got here, then some step of the marshaling failed, which means the GEH was not invoked + return false; + } + +#if FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION + // Get an IActivationFactory * for a managed type + [SecurityCritical] + internal static IntPtr GetActivationFactoryForType(Type type) + { + ManagedActivationFactory activationFactory = GetManagedActivationFactory(type); + return Marshal.GetComInterfaceForObject(activationFactory, typeof(IActivationFactory)); + } + + [SecurityCritical] + internal static ManagedActivationFactory GetManagedActivationFactory(Type type) + { + ManagedActivationFactory activationFactory = new ManagedActivationFactory(type); + + // If the type has any associated factory interfaces (i.e. supports non-default activation + // or has statics), the CCW for this instance of ManagedActivationFactory must support them. + Marshal.InitializeManagedWinRTFactoryObject(activationFactory, (RuntimeType)type); + return activationFactory; + } + +#if FEATURE_COMINTEROP_WINRT_DESKTOP_HOST + // Currently we use only a single class activator since we have a requirement that all class activations come from the same + // app base and we haven't sorted through the various code sharing implications of spinning up multiple AppDomains. This + // holds the IWinRTClassActivator* that is used for the process + private static IntPtr s_pClassActivator = IntPtr.Zero; + + [SecurityCritical] + internal static IntPtr GetClassActivatorForApplication(string appBase) + { + if (s_pClassActivator == IntPtr.Zero) + { + AppDomainSetup hostDomainSetup = new AppDomainSetup() + { + ApplicationBase = appBase, + }; + + AppDomain hostDomain = AppDomain.CreateDomain(Environment.GetResourceString("WinRTHostDomainName", appBase), null, hostDomainSetup); + WinRTClassActivator activator = (WinRTClassActivator)hostDomain.CreateInstanceAndUnwrap(typeof(WinRTClassActivator).Assembly.FullName, typeof(WinRTClassActivator).FullName); + IntPtr pActivator = activator.GetIWinRTClassActivator(); + + if (Interlocked.CompareExchange(ref s_pClassActivator, pActivator, IntPtr.Zero) != IntPtr.Zero) + { + Marshal.Release(pActivator); + activator = null; + + try + { + AppDomain.Unload(hostDomain); + } + catch (CannotUnloadAppDomainException) { } + } + } + + Marshal.AddRef(s_pClassActivator); + return s_pClassActivator; + } +#endif // FEATURE_COMINTEROP_WINRT_DESKTOP_HOST + +#endif // FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION + + // + // Get activation factory object for a specified WinRT type + // If the WinRT type is a native type, we'll always create a unique RCW for it, + // This is necessary because WinRT factories are often implemented as a singleton, + // and getting back a RCW for such WinRT factory would usually get back a RCW from + // another apartment, even if the interface pointe returned from GetActivationFactory + // is a raw pointer. As a result, user would randomly get back RCWs for activation + // factories from other apartments and make transiton to those apartments and cause + // deadlocks and create objects in incorrect apartments + // + [SecurityCritical] + public static IActivationFactory GetActivationFactory(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (type.IsWindowsRuntimeObject && type.IsImport) + { + return (IActivationFactory)Marshal.GetNativeActivationFactory(type); + } + else + { +#if FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION + return GetManagedActivationFactory(type); +#else + // Managed factories are not supported so as to minimize public surface (and test effort) + throw new NotSupportedException(); +#endif + } + } + + // HSTRING marshaling methods: + + [SecurityCritical] + public static IntPtr StringToHString(String s) + { + if (!Environment.IsWinRTSupported) + throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_WinRT")); + + if (s == null) + throw new ArgumentNullException("s"); + + unsafe + { + IntPtr hstring; + int hrCreate = UnsafeNativeMethods.WindowsCreateString(s, s.Length, &hstring); + Marshal.ThrowExceptionForHR(hrCreate, new IntPtr(-1)); + return hstring; + } + } + + [SecurityCritical] + public static String PtrToStringHString(IntPtr ptr) + { + if (!Environment.IsWinRTSupported) + { + throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_WinRT")); + } + + return HStringToString(ptr); + } + + [SecurityCritical] + public static void FreeHString(IntPtr ptr) + { + if (!Environment.IsWinRTSupported) + throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_WinRT")); + + if (ptr != IntPtr.Zero) + { + UnsafeNativeMethods.WindowsDeleteString(ptr); + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMetadata.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMetadata.cs new file mode 100644 index 0000000000..e2ad203583 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMetadata.cs @@ -0,0 +1,216 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics.Contracts; + using System.Reflection; + using System.Reflection.Emit; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Security; + + public static class WindowsRuntimeMetadata + { + // Wrapper for Win8 API RoResolveNamespace with default Windows SDK path as installed .winmd files in %WINDIR%\system32\WinMetadata. + [System.Security.SecurityCritical] + public static IEnumerable<string> ResolveNamespace(string namespaceName, IEnumerable<string> packageGraphFilePaths) + { + return ResolveNamespace(namespaceName, null, packageGraphFilePaths); + } + + // Wrapper for Win8 API RoResolveNamespace. + [System.Security.SecurityCritical] + public static IEnumerable<string> ResolveNamespace(string namespaceName, string windowsSdkFilePath, IEnumerable<string> packageGraphFilePaths) + { + if (namespaceName == null) + throw new ArgumentNullException("namespaceName"); + Contract.EndContractBlock(); + + string[] packageGraphFilePathsArray = null; + if (packageGraphFilePaths != null) + { + List<string> packageGraphFilePathsList = new List<string>(packageGraphFilePaths); + packageGraphFilePathsArray = new string[packageGraphFilePathsList.Count]; + + int index = 0; + foreach (string packageGraphFilePath in packageGraphFilePathsList) + { + packageGraphFilePathsArray[index] = packageGraphFilePath; + index++; + } + } + + string[] retFileNames = null; + nResolveNamespace( + namespaceName, + windowsSdkFilePath, + packageGraphFilePathsArray, + ((packageGraphFilePathsArray == null) ? 0 : packageGraphFilePathsArray.Length), + JitHelpers.GetObjectHandleOnStack(ref retFileNames)); + + return retFileNames; + } + + [System.Security.SecurityCritical] + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private extern static void nResolveNamespace( + string namespaceName, + string windowsSdkFilePath, + string[] packageGraphFilePaths, + int cPackageGraphFilePaths, + ObjectHandleOnStack retFileNames); + +#if FEATURE_REFLECTION_ONLY_LOAD + [method: System.Security.SecurityCritical] + public static event EventHandler<NamespaceResolveEventArgs> ReflectionOnlyNamespaceResolve; + + internal static RuntimeAssembly[] OnReflectionOnlyNamespaceResolveEvent(AppDomain appDomain, RuntimeAssembly assembly, string namespaceName) + { + EventHandler<NamespaceResolveEventArgs> eventHandler = ReflectionOnlyNamespaceResolve; + if (eventHandler != null) + { + Delegate[] ds = eventHandler.GetInvocationList(); + int len = ds.Length; + for (int i = 0; i < len; i++) + { + NamespaceResolveEventArgs eventArgs = new NamespaceResolveEventArgs(namespaceName, assembly); + + ((EventHandler<NamespaceResolveEventArgs>)ds[i])(appDomain, eventArgs); + + Collection<Assembly> assembliesCollection = eventArgs.ResolvedAssemblies; + if (assembliesCollection.Count > 0) + { + RuntimeAssembly[] retAssemblies = new RuntimeAssembly[assembliesCollection.Count]; + int retIndex = 0; + foreach (Assembly asm in assembliesCollection) + { + retAssemblies[retIndex] = AppDomain.GetRuntimeAssembly(asm); + retIndex++; + } + return retAssemblies; + } + } + } + + return null; + } +#endif //FEATURE_REFLECTION_ONLY + + [method: System.Security.SecurityCritical] + public static event EventHandler<DesignerNamespaceResolveEventArgs> DesignerNamespaceResolve; + + internal static string[] OnDesignerNamespaceResolveEvent(AppDomain appDomain, string namespaceName) + { + EventHandler<DesignerNamespaceResolveEventArgs> eventHandler = DesignerNamespaceResolve; + if (eventHandler != null) + { + Delegate[] ds = eventHandler.GetInvocationList(); + int len = ds.Length; + for (int i = 0; i < len; i++) + { + DesignerNamespaceResolveEventArgs eventArgs = new DesignerNamespaceResolveEventArgs(namespaceName); + + ((EventHandler<DesignerNamespaceResolveEventArgs>)ds[i])(appDomain, eventArgs); + + Collection<string> assemblyFilesCollection = eventArgs.ResolvedAssemblyFiles; + if (assemblyFilesCollection.Count > 0) + { + string[] retAssemblyFiles = new string[assemblyFilesCollection.Count]; + int retIndex = 0; + foreach (string assemblyFile in assemblyFilesCollection) + { + if (String.IsNullOrEmpty(assemblyFile)) + { // DesignerNamespaceResolve event returned null or empty file name - that is not allowed + throw new ArgumentException(Environment.GetResourceString("Arg_EmptyOrNullString"), "DesignerNamespaceResolveEventArgs.ResolvedAssemblyFiles"); + } + retAssemblyFiles[retIndex] = assemblyFile; + retIndex++; + } + + return retAssemblyFiles; + } + } + } + + return null; + } + } + +#if FEATURE_REFLECTION_ONLY_LOAD + [ComVisible(false)] + public class NamespaceResolveEventArgs : EventArgs + { + private string _NamespaceName; + private Assembly _RequestingAssembly; + private Collection<Assembly> _ResolvedAssemblies; + + public string NamespaceName + { + get + { + return _NamespaceName; + } + } + + public Assembly RequestingAssembly + { + get + { + return _RequestingAssembly; + } + } + + public Collection<Assembly> ResolvedAssemblies + { + get + { + return _ResolvedAssemblies; + } + } + + public NamespaceResolveEventArgs(string namespaceName, Assembly requestingAssembly) + { + _NamespaceName = namespaceName; + _RequestingAssembly = requestingAssembly; + _ResolvedAssemblies = new Collection<Assembly>(); + } + } +#endif //FEATURE_REFLECTION_ONLY + + [ComVisible(false)] + public class DesignerNamespaceResolveEventArgs : EventArgs + { + private string _NamespaceName; + private Collection<string> _ResolvedAssemblyFiles; + + public string NamespaceName + { + get + { + return _NamespaceName; + } + } + + public Collection<string> ResolvedAssemblyFiles + { + get + { + return _ResolvedAssemblyFiles; + } + } + + public DesignerNamespaceResolveEventArgs(string namespaceName) + { + _NamespaceName = namespaceName; + _ResolvedAssemblyFiles = new Collection<string>(); + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs new file mode 100644 index 0000000000..75529868bd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -0,0 +1,481 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using System.Reflection; +using System.Diagnostics.Contracts; +using System.IO; +using System.Runtime.Versioning; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using System.Threading; + +#if FEATURE_HOST_ASSEMBLY_RESOLVER + +namespace System.Runtime.Loader +{ + [System.Security.SecuritySafeCritical] + public abstract class AssemblyLoadContext + { + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern bool OverrideDefaultAssemblyLoadContextForCurrentDomain(IntPtr ptrNativeAssemblyLoadContext); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern bool CanUseAppPathAssemblyLoadContextInCurrentDomain(); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern IntPtr InitializeAssemblyLoadContext(IntPtr ptrAssemblyLoadContext, bool fRepresentsTPALoadContext); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern IntPtr LoadFromAssemblyName(IntPtr ptrNativeAssemblyLoadContext, bool fRepresentsTPALoadContext); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern IntPtr LoadFromStream(IntPtr ptrNativeAssemblyLoadContext, IntPtr ptrAssemblyArray, int iAssemblyArrayLen, IntPtr ptrSymbols, int iSymbolArrayLen, ObjectHandleOnStack retAssembly); + +#if FEATURE_MULTICOREJIT + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern void InternalSetProfileRoot(string directoryPath); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + internal static extern void InternalStartProfile(string profile, IntPtr ptrNativeAssemblyLoadContext); +#endif // FEATURE_MULTICOREJIT + + protected AssemblyLoadContext() + { + // Initialize the ALC representing non-TPA LoadContext + InitializeLoadContext(false); + } + + internal AssemblyLoadContext(bool fRepresentsTPALoadContext) + { + // Initialize the ALC representing TPA LoadContext + InitializeLoadContext(fRepresentsTPALoadContext); + } + + [System.Security.SecuritySafeCritical] + void InitializeLoadContext(bool fRepresentsTPALoadContext) + { + // Initialize the VM side of AssemblyLoadContext if not already done. + GCHandle gchALC = GCHandle.Alloc(this); + IntPtr ptrALC = GCHandle.ToIntPtr(gchALC); + m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(ptrALC, fRepresentsTPALoadContext); + + // Initialize event handlers to be null by default + Resolving = null; + Unloading = null; + + // Since unloading an AssemblyLoadContext is not yet implemented, this is a temporary solution to raise the + // Unloading event on process exit. Register for the current AppDomain's ProcessExit event, and the handler will in + // turn raise the Unloading event. + AppDomain.CurrentDomain.ProcessExit += OnProcessExit; + } + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern void LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, string ilPath, string niPath, ObjectHandleOnStack retAssembly); + + // These are helpers that can be used by AssemblyLoadContext derivations. + // They are used to load assemblies in DefaultContext. + public Assembly LoadFromAssemblyPath(string assemblyPath) + { + if (assemblyPath == null) + { + throw new ArgumentNullException("assemblyPath"); + } + + if (Path.IsRelative(assemblyPath)) + { + throw new ArgumentException( Environment.GetResourceString("Argument_AbsolutePathRequired"), "assemblyPath"); + } + + RuntimeAssembly loadedAssembly = null; + LoadFromPath(m_pNativeAssemblyLoadContext, assemblyPath, null, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + return loadedAssembly; + } + + public Assembly LoadFromNativeImagePath(string nativeImagePath, string assemblyPath) + { + if (nativeImagePath == null) + { + throw new ArgumentNullException("nativeImagePath"); + } + + if (Path.IsRelative(nativeImagePath)) + { + throw new ArgumentException( Environment.GetResourceString("Argument_AbsolutePathRequired"), "nativeImagePath"); + } + + if (assemblyPath != null && Path.IsRelative(assemblyPath)) + { + throw new ArgumentException(Environment.GetResourceString("Argument_AbsolutePathRequired"), "assemblyPath"); + } + + // Basic validation has succeeded - lets try to load the NI image. + // Ask LoadFile to load the specified assembly in the DefaultContext + RuntimeAssembly loadedAssembly = null; + LoadFromPath(m_pNativeAssemblyLoadContext, assemblyPath, nativeImagePath, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + return loadedAssembly; + } + + public Assembly LoadFromStream(Stream assembly) + { + return LoadFromStream(assembly, null); + } + + public Assembly LoadFromStream(Stream assembly, Stream assemblySymbols) + { + if (assembly == null) + { + throw new ArgumentNullException("assembly"); + } + + int iAssemblyStreamLength = (int)assembly.Length; + int iSymbolLength = 0; + + // Allocate the byte[] to hold the assembly + byte[] arrAssembly = new byte[iAssemblyStreamLength]; + + // Copy the assembly to the byte array + assembly.Read(arrAssembly, 0, iAssemblyStreamLength); + + // Get the symbol stream in byte[] if provided + byte[] arrSymbols = null; + if (assemblySymbols != null) + { + iSymbolLength = (int)assemblySymbols.Length; + arrSymbols = new byte[iSymbolLength]; + + assemblySymbols.Read(arrSymbols, 0, iSymbolLength); + } + + RuntimeAssembly loadedAssembly = null; + unsafe + { + fixed(byte *ptrAssembly = arrAssembly, ptrSymbols = arrSymbols) + { + LoadFromStream(m_pNativeAssemblyLoadContext, new IntPtr(ptrAssembly), iAssemblyStreamLength, new IntPtr(ptrSymbols), iSymbolLength, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly)); + } + } + + return loadedAssembly; + } + + // Custom AssemblyLoadContext implementations can override this + // method to perform custom processing and use one of the protected + // helpers above to load the assembly. + protected abstract Assembly Load(AssemblyName assemblyName); + + // This method is invoked by the VM when using the host-provided assembly load context + // implementation. + private static Assembly Resolve(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target); + + return context.ResolveUsingLoad(assemblyName); + } + + // This method is invoked by the VM to resolve an assembly reference using the Resolving event + // after trying assembly resolution via Load override and TPA load context without success. + private static Assembly ResolveUsingResolvingEvent(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target); + + // Invoke the AssemblyResolve event callbacks if wired up + return context.ResolveUsingEvent(assemblyName); + } + + private Assembly GetFirstResolvedAssembly(AssemblyName assemblyName) + { + Assembly resolvedAssembly = null; + + Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = Resolving; + + if (assemblyResolveHandler != null) + { + // Loop through the event subscribers and return the first non-null Assembly instance + Delegate [] arrSubscribers = assemblyResolveHandler.GetInvocationList(); + for(int i = 0; i < arrSubscribers.Length; i++) + { + resolvedAssembly = ((Func<AssemblyLoadContext, AssemblyName, Assembly>)arrSubscribers[i])(this, assemblyName); + if (resolvedAssembly != null) + { + break; + } + } + } + + return resolvedAssembly; + } + + private Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string requestedSimpleName) + { + // Get the name of the loaded assembly + string loadedSimpleName = null; + + // Derived type's Load implementation is expected to use one of the LoadFrom* methods to get the assembly + // which is a RuntimeAssembly instance. However, since Assembly type can be used build any other artifact (e.g. AssemblyBuilder), + // we need to check for RuntimeAssembly. + RuntimeAssembly rtLoadedAssembly = assembly as RuntimeAssembly; + if (rtLoadedAssembly != null) + { + loadedSimpleName = rtLoadedAssembly.GetSimpleName(); + } + + // The simple names should match at the very least + if (String.IsNullOrEmpty(loadedSimpleName) || (!requestedSimpleName.Equals(loadedSimpleName, StringComparison.InvariantCultureIgnoreCase))) + throw new InvalidOperationException(Environment.GetResourceString("Argument_CustomAssemblyLoadContextRequestedNameMismatch")); + + return assembly; + + } + + private Assembly ResolveUsingLoad(AssemblyName assemblyName) + { + string simpleName = assemblyName.Name; + Assembly assembly = Load(assemblyName); + + if (assembly != null) + { + assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName); + } + + return assembly; + } + + private Assembly ResolveUsingEvent(AssemblyName assemblyName) + { + string simpleName = assemblyName.Name; + + // Invoke the AssemblyResolve event callbacks if wired up + Assembly assembly = GetFirstResolvedAssembly(assemblyName); + if (assembly != null) + { + assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName); + } + + // Since attempt to resolve the assembly via Resolving event is the last option, + // throw an exception if we do not find any assembly. + if (assembly == null) + { + throw new FileNotFoundException(Environment.GetResourceString("IO.FileLoad"), simpleName); + } + + return assembly; + } + + public Assembly LoadFromAssemblyName(AssemblyName assemblyName) + { + // Attempt to load the assembly, using the same ordering as static load, in the current load context. + Assembly loadedAssembly = Assembly.Load(assemblyName, m_pNativeAssemblyLoadContext); + + return loadedAssembly; + } + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern IntPtr InternalLoadUnmanagedDllFromPath(string unmanagedDllPath); + + // This method provides a way for overriders of LoadUnmanagedDll() to load an unmanaged DLL from a specific path in a + // platform-independent way. The DLL is loaded with default load flags. + protected IntPtr LoadUnmanagedDllFromPath(string unmanagedDllPath) + { + if (unmanagedDllPath == null) + { + throw new ArgumentNullException("unmanagedDllPath"); + } + if (unmanagedDllPath.Length == 0) + { + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"), "unmanagedDllPath"); + } + if (Path.IsRelative(unmanagedDllPath)) + { + throw new ArgumentException(Environment.GetResourceString("Argument_AbsolutePathRequired"), "unmanagedDllPath"); + } + + return InternalLoadUnmanagedDllFromPath(unmanagedDllPath); + } + + // Custom AssemblyLoadContext implementations can override this + // method to perform the load of unmanaged native dll + // This function needs to return the HMODULE of the dll it loads + protected virtual IntPtr LoadUnmanagedDll(String unmanagedDllName) + { + //defer to default coreclr policy of loading unmanaged dll + return IntPtr.Zero; + } + + // This method is invoked by the VM when using the host-provided assembly load context + // implementation. + private static IntPtr ResolveUnmanagedDll(String unmanagedDllName, IntPtr gchManagedAssemblyLoadContext) + { + AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target); + return context.LoadUnmanagedDll(unmanagedDllName); + } + + public static AssemblyLoadContext Default + { + get + { + if (s_DefaultAssemblyLoadContext == null) + { + // Try to initialize the default assembly load context with apppath one if we are allowed to + if (AssemblyLoadContext.CanUseAppPathAssemblyLoadContextInCurrentDomain()) + { + // Synchronize access to initializing Default ALC + lock(s_initLock) + { + if (s_DefaultAssemblyLoadContext == null) + { + s_DefaultAssemblyLoadContext = new AppPathAssemblyLoadContext(); + } + } + } + } + + return s_DefaultAssemblyLoadContext; + } + } + + // This will be used to set the AssemblyLoadContext for DefaultContext, for the AppDomain, + // by a host. Once set, the runtime will invoke the LoadFromAssemblyName method against it to perform + // assembly loads for the DefaultContext. + // + // This method will throw if the Default AssemblyLoadContext is already set or the Binding model is already locked. + public static void InitializeDefaultContext(AssemblyLoadContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + // Try to override the default assembly load context + if (!AssemblyLoadContext.OverrideDefaultAssemblyLoadContextForCurrentDomain(context.m_pNativeAssemblyLoadContext)) + { + throw new InvalidOperationException(Environment.GetResourceString("AppDomain_BindingModelIsLocked")); + } + + // Update the managed side as well. + s_DefaultAssemblyLoadContext = context; + } + + // This call opens and closes the file, but does not add the + // assembly to the domain. + [MethodImplAttribute(MethodImplOptions.InternalCall)] + static internal extern AssemblyName nGetFileInformation(String s); + + // Helper to return AssemblyName corresponding to the path of an IL assembly + public static AssemblyName GetAssemblyName(string assemblyPath) + { + if (assemblyPath == null) + { + throw new ArgumentNullException("assemblyPath"); + } + + String fullPath = Path.GetFullPathInternal(assemblyPath); + return nGetFileInformation(fullPath); + } + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern IntPtr GetLoadContextForAssembly(RuntimeAssembly assembly); + + // Returns the load context in which the specified assembly has been loaded + public static AssemblyLoadContext GetLoadContext(Assembly assembly) + { + if (assembly == null) + { + throw new ArgumentNullException("assembly"); + } + + AssemblyLoadContext loadContextForAssembly = null; + + RuntimeAssembly rtAsm = assembly as RuntimeAssembly; + + // We only support looking up load context for runtime assemblies. + if (rtAsm != null) + { + IntPtr ptrAssemblyLoadContext = GetLoadContextForAssembly(rtAsm); + if (ptrAssemblyLoadContext == IntPtr.Zero) + { + // If the load context is returned null, then the assembly was bound using the TPA binder + // and we shall return reference to the active "Default" binder - which could be the TPA binder + // or an overridden CLRPrivBinderAssemblyLoadContext instance. + loadContextForAssembly = AssemblyLoadContext.Default; + } + else + { + loadContextForAssembly = (AssemblyLoadContext)(GCHandle.FromIntPtr(ptrAssemblyLoadContext).Target); + } + } + + return loadContextForAssembly; + } + + // Set the root directory path for profile optimization. + public void SetProfileOptimizationRoot(string directoryPath) + { +#if FEATURE_MULTICOREJIT + InternalSetProfileRoot(directoryPath); +#endif // FEATURE_MULTICOREJIT + } + + // Start profile optimization for the specified profile name. + public void StartProfileOptimization(string profile) + { +#if FEATURE_MULTICOREJIT + InternalStartProfile(profile, m_pNativeAssemblyLoadContext); +#endif // FEATURE_MULTICOREJI + } + + private void OnProcessExit(object sender, EventArgs e) + { + var unloading = Unloading; + if (unloading != null) + { + unloading(this); + } + } + + public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving; + public event Action<AssemblyLoadContext> Unloading; + + // Contains the reference to VM's representation of the AssemblyLoadContext + private IntPtr m_pNativeAssemblyLoadContext; + + // Each AppDomain contains the reference to its AssemblyLoadContext instance, if one is + // specified by the host. By having the field as a static, we are + // making it an AppDomain-wide field. + private static volatile AssemblyLoadContext s_DefaultAssemblyLoadContext; + + // Synchronization primitive for controlling initialization of Default load context + private static readonly object s_initLock = new Object(); + } + + [System.Security.SecuritySafeCritical] + class AppPathAssemblyLoadContext : AssemblyLoadContext + { + internal AppPathAssemblyLoadContext() : base(true) + { + } + + [System.Security.SecuritySafeCritical] + protected override Assembly Load(AssemblyName assemblyName) + { + // We were loading an assembly into TPA ALC that was not found on TPA list. As a result we are here. + // Returning null will result in the AssemblyResolve event subscribers to be invoked to help resolve the assembly. + return null; + } + } +} + +#endif // FEATURE_HOST_ASSEMBLY_RESOLVER diff --git a/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs b/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs new file mode 100644 index 0000000000..6e35568310 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs @@ -0,0 +1,488 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Provides a way for an app to not start an operation unless +** there's a reasonable chance there's enough memory +** available for the operation to succeed. +** +** +===========================================================*/ + +using System; +using System.IO; +using Microsoft.Win32; +using System.Runtime.InteropServices; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Security.Permissions; +using System.Runtime.Versioning; +using System.Diagnostics.Contracts; + +/* + This class allows an application to fail before starting certain + activities. The idea is to fail early instead of failing in the middle + of some long-running operation to increase the survivability of the + application and ensure you don't have to write tricky code to handle an + OOM anywhere in your app's code (which implies state corruption, meaning you + should unload the appdomain, if you have a transacted environment to ensure + rollback of individual transactions). This is an incomplete tool to attempt + hoisting all your OOM failures from anywhere in your worker methods to one + particular point where it is easier to handle an OOM failure, and you can + optionally choose to not start a workitem if it will likely fail. This does + not help the performance of your code directly (other than helping to avoid + AD unloads). The point is to avoid starting work if it is likely to fail. + The Enterprise Services team has used these memory gates effectively in the + unmanaged world for a decade. + + In Whidbey, we will simply check to see if there is enough memory available + in the OS's page file & attempt to ensure there might be enough space free + within the process's address space (checking for address space fragmentation + as well). We will not commit or reserve any memory. To avoid race conditions with + other threads using MemoryFailPoints, we'll also keep track of a + process-wide amount of memory "reserved" via all currently-active + MemoryFailPoints. This has two problems: + 1) This can account for memory twice. If a thread creates a + MemoryFailPoint for 100 MB then allocates 99 MB, we'll see 99 MB + less free memory and 100 MB less reserved memory. Yet, subtracting + off the 100 MB is necessary because the thread may not have started + allocating memory yet. Disposing of this class immediately after + front-loaded allocations have completed is a great idea. + 2) This is still vulnerable to race conditions with other threads that don't use + MemoryFailPoints. + So this class is far from perfect. But it may be good enough to + meaningfully reduce the frequency of OutOfMemoryExceptions in managed apps. + + In Orcas or later, we might allocate some memory from the OS and add it + to a allocation context for this thread. Obviously, at that point we need + some way of conveying when we release this block of memory. So, we + implemented IDisposable on this type in Whidbey and expect all users to call + this from within a using block to provide lexical scope for their memory + usage. The call to Dispose (implicit with the using block) will give us an + opportunity to release this memory, perhaps. We anticipate this will give + us the possibility of a more effective design in a future version. + + In Orcas, we may also need to differentiate between allocations that would + go into the normal managed heap vs. the large object heap, or we should + consider checking for enough free space in both locations (with any + appropriate adjustments to ensure the memory is contiguous). +*/ + +namespace System.Runtime +{ + public sealed class MemoryFailPoint : CriticalFinalizerObject, IDisposable + { + // Find the top section of user mode memory. Avoid the last 64K. + // Windows reserves that block for the kernel, apparently, and doesn't + // let us ask about that memory. But since we ask for memory in 1 MB + // chunks, we don't have to special case this. Also, we need to + // deal with 32 bit machines in 3 GB mode. + // Using Win32's GetSystemInfo should handle all this for us. + private static readonly ulong TopOfMemory; + + // Walking the address space is somewhat expensive, taking around half + // a millisecond. Doing that per transaction limits us to a max of + // ~2000 transactions/second. Instead, let's do this address space + // walk once every 10 seconds, or when we will likely fail. This + // amortization scheme can reduce the cost of a memory gate by about + // a factor of 100. + private static long hiddenLastKnownFreeAddressSpace = 0; + private static long hiddenLastTimeCheckingAddressSpace = 0; + private const int CheckThreshold = 10 * 1000; // 10 seconds + + private static long LastKnownFreeAddressSpace + { + get { return Volatile.Read(ref hiddenLastKnownFreeAddressSpace); } + set { Volatile.Write(ref hiddenLastKnownFreeAddressSpace, value); } + } + + private static long AddToLastKnownFreeAddressSpace(long addend) + { + return Interlocked.Add(ref hiddenLastKnownFreeAddressSpace, addend); + } + + private static long LastTimeCheckingAddressSpace + { + get { return Volatile.Read(ref hiddenLastTimeCheckingAddressSpace); } + set { Volatile.Write(ref hiddenLastTimeCheckingAddressSpace, value); } + } + + // When allocating memory segment by segment, we've hit some cases + // where there are only 22 MB of memory available on the machine, + // we need 1 16 MB segment, and the OS does not succeed in giving us + // that memory. Reasons for this could include: + // 1) The GC does allocate memory when doing a collection. + // 2) Another process on the machine could grab that memory. + // 3) Some other part of the runtime might grab this memory. + // If we build in a little padding, we can help protect + // ourselves against some of these cases, and we want to err on the + // conservative side with this class. + private const int LowMemoryFudgeFactor = 16 << 20; + + // Round requested size to a 16MB multiple to have a better granularity + // when checking for available memory. + private const int MemoryCheckGranularity = 16; + + // Note: This may become dynamically tunable in the future. + // Also note that we can have different segment sizes for the normal vs. + // large object heap. We currently use the max of the two. + private static readonly ulong GCSegmentSize; + + // For multi-threaded workers, we want to ensure that if two workers + // use a MemoryFailPoint at the same time, and they both succeed, that + // they don't trample over each other's memory. Keep a process-wide + // count of "reserved" memory, and decrement this in Dispose and + // in the critical finalizer. See + // SharedStatics.MemoryFailPointReservedMemory + + private ulong _reservedMemory; // The size of this request (from user) + private bool _mustSubtractReservation; // Did we add data to SharedStatics? + + [System.Security.SecuritySafeCritical] // auto-generated + static MemoryFailPoint() + { + GetMemorySettings(out GCSegmentSize, out TopOfMemory); + } + + // We can remove this link demand in a future version - we will + // have scenarios for this in partial trust in the future, but + // we're doing this just to restrict this in case the code below + // is somehow incorrect. + [System.Security.SecurityCritical] // auto-generated_required + public MemoryFailPoint(int sizeInMegabytes) + { + if (sizeInMegabytes <= 0) + throw new ArgumentOutOfRangeException("sizeInMegabytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + Contract.EndContractBlock(); + + ulong size = ((ulong)sizeInMegabytes) << 20; + _reservedMemory = size; + + // Check to see that we both have enough memory on the system + // and that we have enough room within the user section of the + // process's address space. Also, we need to use the GC segment + // size, not the amount of memory the user wants to allocate. + // Consider correcting this to reflect free memory within the GC + // heap, and to check both the normal & large object heaps. + ulong segmentSize = (ulong) (Math.Ceiling((double)size / GCSegmentSize) * GCSegmentSize); + if (segmentSize >= TopOfMemory) + throw new InsufficientMemoryException(Environment.GetResourceString("InsufficientMemory_MemFailPoint_TooBig")); + + ulong requestedSizeRounded = (ulong)(Math.Ceiling((double)sizeInMegabytes / MemoryCheckGranularity) * MemoryCheckGranularity); + //re-convert into bytes + requestedSizeRounded <<= 20; + + ulong availPageFile = 0; // available VM (physical + page file) + ulong totalAddressSpaceFree = 0; // non-contiguous free address space + + // Check for available memory, with 2 attempts at getting more + // memory. + // Stage 0: If we don't have enough, trigger a GC. + // Stage 1: If we don't have enough, try growing the swap file. + // Stage 2: Update memory state, then fail or leave loop. + // + // (In the future, we could consider adding another stage after + // Stage 0 to run finalizers. However, before doing that make sure + // that we could abort this constructor when we call + // GC.WaitForPendingFinalizers, noting that this method uses a CER + // so it can't be aborted, and we have a critical finalizer. It + // would probably work, but do some thinking first.) + for(int stage = 0; stage < 3; stage++) { + CheckForAvailableMemory(out availPageFile, out totalAddressSpaceFree); + + // If we have enough room, then skip some stages. + // Note that multiple threads can still lead to a race condition for our free chunk + // of address space, which can't be easily solved. + ulong reserved = SharedStatics.MemoryFailPointReservedMemory; + ulong segPlusReserved = segmentSize + reserved; + bool overflow = segPlusReserved < segmentSize || segPlusReserved < reserved; + bool needPageFile = availPageFile < (requestedSizeRounded + reserved + LowMemoryFudgeFactor) || overflow; + bool needAddressSpace = totalAddressSpaceFree < segPlusReserved || overflow; + + // Ensure our cached amount of free address space is not stale. + long now = Environment.TickCount; // Handle wraparound. + if ((now > LastTimeCheckingAddressSpace + CheckThreshold || now < LastTimeCheckingAddressSpace) || + LastKnownFreeAddressSpace < (long) segmentSize) { + CheckForFreeAddressSpace(segmentSize, false); + } + bool needContiguousVASpace = (ulong) LastKnownFreeAddressSpace < segmentSize; + + BCLDebug.Trace("MEMORYFAILPOINT", "MemoryFailPoint: Checking for {0} MB, for allocation size of {1} MB, stage {9}. Need page file? {2} Need Address Space? {3} Need Contiguous address space? {4} Avail page file: {5} MB Total free VA space: {6} MB Contiguous free address space (found): {7} MB Space reserved via process's MemoryFailPoints: {8} MB", + segmentSize >> 20, sizeInMegabytes, needPageFile, + needAddressSpace, needContiguousVASpace, + availPageFile >> 20, totalAddressSpaceFree >> 20, + LastKnownFreeAddressSpace >> 20, reserved, stage); + + if (!needPageFile && !needAddressSpace && !needContiguousVASpace) + break; + + switch(stage) { + case 0: + // The GC will release empty segments to the OS. This will + // relieve us from having to guess whether there's + // enough memory in either GC heap, and whether + // internal fragmentation will prevent those + // allocations from succeeding. + GC.Collect(); + continue; + + case 1: + // Do this step if and only if the page file is too small. + if (!needPageFile) + continue; + + // Attempt to grow the OS's page file. Note that we ignore + // any allocation routines from the host intentionally. + RuntimeHelpers.PrepareConstrainedRegions(); + try { + } + finally { + // This shouldn't overflow due to the if clauses above. + UIntPtr numBytes = new UIntPtr(segmentSize); + unsafe { + void * pMemory = Win32Native.VirtualAlloc(null, numBytes, Win32Native.MEM_COMMIT, Win32Native.PAGE_READWRITE); + if (pMemory != null) { + bool r = Win32Native.VirtualFree(pMemory, UIntPtr.Zero, Win32Native.MEM_RELEASE); + if (!r) + __Error.WinIOError(); + } + } + } + continue; + + case 2: + // The call to CheckForAvailableMemory above updated our + // state. + if (needPageFile || needAddressSpace) { + InsufficientMemoryException e = new InsufficientMemoryException(Environment.GetResourceString("InsufficientMemory_MemFailPoint")); +#if _DEBUG + e.Data["MemFailPointState"] = new MemoryFailPointState(sizeInMegabytes, segmentSize, + needPageFile, needAddressSpace, needContiguousVASpace, + availPageFile >> 20, totalAddressSpaceFree >> 20, + LastKnownFreeAddressSpace >> 20, reserved); +#endif + throw e; + } + + if (needContiguousVASpace) { + InsufficientMemoryException e = new InsufficientMemoryException(Environment.GetResourceString("InsufficientMemory_MemFailPoint_VAFrag")); +#if _DEBUG + e.Data["MemFailPointState"] = new MemoryFailPointState(sizeInMegabytes, segmentSize, + needPageFile, needAddressSpace, needContiguousVASpace, + availPageFile >> 20, totalAddressSpaceFree >> 20, + LastKnownFreeAddressSpace >> 20, reserved); +#endif + throw e; + } + + break; + + default: + Contract.Assert(false, "Fell through switch statement!"); + break; + } + } + + // Success - we have enough room the last time we checked. + // Now update our shared state in a somewhat atomic fashion + // and handle a simple race condition with other MemoryFailPoint instances. + AddToLastKnownFreeAddressSpace(-((long) size)); + if (LastKnownFreeAddressSpace < 0) + CheckForFreeAddressSpace(segmentSize, true); + + RuntimeHelpers.PrepareConstrainedRegions(); + try { + } + finally { + SharedStatics.AddMemoryFailPointReservation((long) size); + _mustSubtractReservation = true; + } + } + + [System.Security.SecurityCritical] // auto-generated + private static void CheckForAvailableMemory(out ulong availPageFile, out ulong totalAddressSpaceFree) + { + bool r; + Win32Native.MEMORYSTATUSEX memory = new Win32Native.MEMORYSTATUSEX(); + r = Win32Native.GlobalMemoryStatusEx(ref memory); + if (!r) + __Error.WinIOError(); + availPageFile = memory.availPageFile; + totalAddressSpaceFree = memory.availVirtual; + //Console.WriteLine("Memory gate: Mem load: {0}% Available memory (physical + page file): {1} MB Total free address space: {2} MB GC Heap: {3} MB", memory.memoryLoad, memory.availPageFile >> 20, memory.availVirtual >> 20, GC.GetTotalMemory(true) >> 20); + } + + // Based on the shouldThrow parameter, this will throw an exception, or + // returns whether there is enough space. In all cases, we update + // our last known free address space, hopefully avoiding needing to + // probe again. + [System.Security.SecurityCritical] // auto-generated + private static unsafe bool CheckForFreeAddressSpace(ulong size, bool shouldThrow) + { + // Start walking the address space at 0. VirtualAlloc may wrap + // around the address space. We don't need to find the exact + // pages that VirtualAlloc would return - we just need to + // know whether VirtualAlloc could succeed. + ulong freeSpaceAfterGCHeap = MemFreeAfterAddress(null, size); + + BCLDebug.Trace("MEMORYFAILPOINT", "MemoryFailPoint: Checked for free VA space. Found enough? {0} Asked for: {1} Found: {2}", (freeSpaceAfterGCHeap >= size), size, freeSpaceAfterGCHeap); + + // We may set these without taking a lock - I don't believe + // this will hurt, as long as we never increment this number in + // the Dispose method. If we do an extra bit of checking every + // once in a while, but we avoid taking a lock, we may win. + LastKnownFreeAddressSpace = (long) freeSpaceAfterGCHeap; + LastTimeCheckingAddressSpace = Environment.TickCount; + + if (freeSpaceAfterGCHeap < size && shouldThrow) + throw new InsufficientMemoryException(Environment.GetResourceString("InsufficientMemory_MemFailPoint_VAFrag")); + return freeSpaceAfterGCHeap >= size; + } + + // Returns the amount of consecutive free memory available in a block + // of pages. If we didn't have enough address space, we still return + // a positive value < size, to help potentially avoid the overhead of + // this check if we use a MemoryFailPoint with a smaller size next. + [System.Security.SecurityCritical] // auto-generated + private static unsafe ulong MemFreeAfterAddress(void * address, ulong size) + { + if (size >= TopOfMemory) + return 0; + + ulong largestFreeRegion = 0; + Win32Native.MEMORY_BASIC_INFORMATION memInfo = new Win32Native.MEMORY_BASIC_INFORMATION(); + UIntPtr sizeOfMemInfo = (UIntPtr) Marshal.SizeOf(memInfo); + + while (((ulong)address) + size < TopOfMemory) { + UIntPtr r = Win32Native.VirtualQuery(address, ref memInfo, sizeOfMemInfo); + if (r == UIntPtr.Zero) + __Error.WinIOError(); + + ulong regionSize = memInfo.RegionSize.ToUInt64(); + if (memInfo.State == Win32Native.MEM_FREE) { + if (regionSize >= size) + return regionSize; + else + largestFreeRegion = Math.Max(largestFreeRegion, regionSize); + } + address = (void *) ((ulong) address + regionSize); + } + return largestFreeRegion; + } + + [System.Security.SecurityCritical] // auto-generated + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void GetMemorySettings(out ulong maxGCSegmentSize, out ulong topOfMemory); + + [System.Security.SecuritySafeCritical] // destructors should be safe to call + ~MemoryFailPoint() + { + Dispose(false); + } + + // Applications must call Dispose, which conceptually "releases" the + // memory that was "reserved" by the MemoryFailPoint. This affects a + // global count of reserved memory in this version (helping to throttle + // future MemoryFailPoints) in this version. We may in the + // future create an allocation context and release it in the Dispose + // method. While the finalizer will eventually free this block of + // memory, apps will help their performance greatly by calling Dispose. + [System.Security.SecuritySafeCritical] // auto-generated + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + [System.Security.SecurityCritical] // auto-generated + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private void Dispose(bool disposing) + { + // This is just bookkeeping to ensure multiple threads can really + // get enough memory, and this does not actually reserve memory + // within the GC heap. + if (_mustSubtractReservation) { + RuntimeHelpers.PrepareConstrainedRegions(); + try { + } + finally { + SharedStatics.AddMemoryFailPointReservation(-((long)_reservedMemory)); + _mustSubtractReservation = false; + } + } + + /* + // Prototype performance + // Let's pretend that we returned at least some free memory to + // the GC heap. We don't know this is true - the objects could + // have a longer lifetime, and the memory could be elsewhere in the + // GC heap. Additionally, we subtracted off the segment size, not + // this size. That's ok - we don't mind if this slowly degrades + // and requires us to refresh the value a little bit sooner. + // But releasing the memory here should help us avoid probing for + // free address space excessively with large workItem sizes. + Interlocked.Add(ref LastKnownFreeAddressSpace, _reservedMemory); + */ + } + +#if _DEBUG + [Serializable] + internal sealed class MemoryFailPointState + { + private ulong _segmentSize; + private int _allocationSizeInMB; + private bool _needPageFile; + private bool _needAddressSpace; + private bool _needContiguousVASpace; + private ulong _availPageFile; + private ulong _totalFreeAddressSpace; + private long _lastKnownFreeAddressSpace; + private ulong _reservedMem; + private String _stackTrace; // Where did we fail, for additional debugging. + + internal MemoryFailPointState(int allocationSizeInMB, ulong segmentSize, bool needPageFile, bool needAddressSpace, bool needContiguousVASpace, ulong availPageFile, ulong totalFreeAddressSpace, long lastKnownFreeAddressSpace, ulong reservedMem) + { + _allocationSizeInMB = allocationSizeInMB; + _segmentSize = segmentSize; + _needPageFile = needPageFile; + _needAddressSpace = needAddressSpace; + _needContiguousVASpace = needContiguousVASpace; + _availPageFile = availPageFile; + _totalFreeAddressSpace = totalFreeAddressSpace; + _lastKnownFreeAddressSpace = lastKnownFreeAddressSpace; + _reservedMem = reservedMem; + try + { + _stackTrace = Environment.StackTrace; + } + catch (System.Security.SecurityException) + { + _stackTrace = "no permission"; + } + catch (OutOfMemoryException) + { + _stackTrace = "out of memory"; + } + } + + public override String ToString() + { + return String.Format(System.Globalization.CultureInfo.InvariantCulture, "MemoryFailPoint detected insufficient memory to guarantee an operation could complete. Checked for {0} MB, for allocation size of {1} MB. Need page file? {2} Need Address Space? {3} Need Contiguous address space? {4} Avail page file: {5} MB Total free VA space: {6} MB Contiguous free address space (found): {7} MB Space reserved by process's MemoryFailPoints: {8} MB", + _segmentSize >> 20, _allocationSizeInMB, _needPageFile, + _needAddressSpace, _needContiguousVASpace, + _availPageFile >> 20, _totalFreeAddressSpace >> 20, + _lastKnownFreeAddressSpace >> 20, _reservedMem); + } + + public String StackTrace { + get { return _stackTrace; } + } + } +#endif + } +} diff --git a/src/mscorlib/src/System/Runtime/ProfileOptimization.cs b/src/mscorlib/src/System/Runtime/ProfileOptimization.cs new file mode 100644 index 0000000000..c877d2106d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/ProfileOptimization.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// This class defines entry point for multi-core JIT API +// +// +namespace System.Runtime { + + using System; + + using System.Reflection; + + using System.Security; + using System.Security.Permissions; + + using System.Runtime; + using System.Runtime.InteropServices; + using System.Runtime.Versioning; + using System.Runtime.CompilerServices; + +#if FEATURE_MULTICOREJIT + + public static class ProfileOptimization + { + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static extern void InternalSetProfileRoot(string directoryPath); + + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SecurityCritical] + [SuppressUnmanagedCodeSecurity] + internal static extern void InternalStartProfile(string profile, IntPtr ptrNativeAssemblyLoadContext); + + [SecurityCritical] + public static void SetProfileRoot(string directoryPath) + { + InternalSetProfileRoot(directoryPath); + } + + [SecurityCritical] + public static void StartProfile(string profile) + { + InternalStartProfile(profile, IntPtr.Zero); + } + } + +#endif +} + diff --git a/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs b/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs new file mode 100644 index 0000000000..2524aaaecb --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +/*============================================================ +** +** +** +** Deriving from this class will cause any finalizer you define to be critical +** (i.e. the finalizer is guaranteed to run, won't be aborted by the host and is +** run after the finalizers of other objects collected at the same time). +** +** You must possess UnmanagedCode permission in order to derive from this class. +** +** +===========================================================*/ + +using System; +using System.Security.Permissions; +using System.Runtime.InteropServices; + +namespace System.Runtime.ConstrainedExecution +{ +#if !FEATURE_CORECLR + [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode=true)] +#endif + [System.Runtime.InteropServices.ComVisible(true)] + public abstract class CriticalFinalizerObject + { + #if FEATURE_CORECLR + [System.Security.SecuritySafeCritical] // auto-generated + #endif + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected CriticalFinalizerObject() + { + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + ~CriticalFinalizerObject() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs b/src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs new file mode 100644 index 0000000000..d9774d636b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +/*============================================================ +** +** +** Purpose: Serves as a hint to ngen that the decorated method +** (and its statically determinable call graph) should be +** prepared (as for Constrained Execution Region use). This +** is primarily useful in the scenario where the method in +** question will be prepared explicitly at runtime and the +** author of the method knows this and wishes to avoid the +** overhead of runtime preparation. +** +** +===========================================================*/ +namespace System.Runtime.ConstrainedExecution +{ + using System; + using System.Runtime.InteropServices; + + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] + + public sealed class PrePrepareMethodAttribute : Attribute + { + public PrePrepareMethodAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Reliability/ReliabilityContractAttribute.cs b/src/mscorlib/src/System/Runtime/Reliability/ReliabilityContractAttribute.cs new file mode 100644 index 0000000000..4a14e5733c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Reliability/ReliabilityContractAttribute.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +/*============================================================ +** +** +** +** Purpose: Defines a publically documentable contract for +** reliability between a method and its callers, expressing +** what state will remain consistent in the presence of +** failures (ie async exceptions like thread abort) and whether +** the method needs to be called from within a CER. +** +** +===========================================================*/ + +namespace System.Runtime.ConstrainedExecution { + using System.Runtime.InteropServices; + using System; + + // ************************************************************************************************************************** + // + // Note that if you change either of the enums below or the constructors, fields or properties of the custom attribute itself + // you must also change the logic and definitions in vm\ConstrainedExecutionRegion.cpp to match. + // + // ************************************************************************************************************************** + + [Serializable] + public enum Consistency : int + { + MayCorruptProcess = 0, + MayCorruptAppDomain = 1, + MayCorruptInstance = 2, + WillNotCorruptState = 3, + } + + [Serializable] + public enum Cer : int + { + None = 0, + MayFail = 1, // Might fail, but the method will say it failed + Success = 2, + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Interface /* | AttributeTargets.Delegate*/, Inherited = false)] + public sealed class ReliabilityContractAttribute : Attribute + { + private Consistency _consistency; + private Cer _cer; + + public ReliabilityContractAttribute(Consistency consistencyGuarantee, Cer cer) + { + _consistency = consistencyGuarantee; + _cer = cer; + } + + public Consistency ConsistencyGuarantee { + get { return _consistency; } + } + + public Cer Cer { + get { return _cer; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Remoting/ObjectHandle.cs b/src/mscorlib/src/System/Runtime/Remoting/ObjectHandle.cs new file mode 100644 index 0000000000..d10df9d57b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Remoting/ObjectHandle.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** ObjectHandle wraps object references. A Handle allows a +** marshal by value object to be returned through an +** indirection allowing the caller to control when the +** object is loaded into their domain. +** +** +===========================================================*/ + +namespace System.Runtime.Remoting{ + + using System; + using System.Security.Permissions; + using System.Runtime.InteropServices; + using System.Runtime.Remoting; +#if FEATURE_REMOTING + using System.Runtime.Remoting.Activation; + using System.Runtime.Remoting.Lifetime; +#endif + + [ClassInterface(ClassInterfaceType.AutoDual)] + [System.Runtime.InteropServices.ComVisible(true)] + public class ObjectHandle: +#if FEATURE_REMOTING + MarshalByRefObject, +#endif + IObjectHandle + { + private Object WrappedObject; + + private ObjectHandle() + { + } + + public ObjectHandle(Object o) + { + WrappedObject = o; + } + + public Object Unwrap() + { + return WrappedObject; + } + + // ObjectHandle has a finite lifetime. For now the default + // lifetime is being used, this can be changed in this method to + // specify a custom lifetime. +#if FEATURE_REMOTING + [System.Security.SecurityCritical] // auto-generated_required + public override Object InitializeLifetimeService() + { + BCLDebug.Trace("REMOTE", "ObjectHandle.InitializeLifetimeService"); + + // + // If the wrapped object has implemented InitializeLifetimeService to return null, + // we don't want to go to the base class (which will result in a lease being + // requested from the MarshalByRefObject, which starts up the LeaseManager, + // which starts up the ThreadPool, adding three threads to the process. + // We check if the wrapped object is a MarshalByRef object, and call InitializeLifetimeServices on it + // and if it returns null, we return null. Otherwise we fall back to the old behavior. + // + + MarshalByRefObject mbr = WrappedObject as MarshalByRefObject; + if (mbr != null) { + Object o = mbr.InitializeLifetimeService(); + if (o == null) + return null; + } + ILease lease = (ILease)base.InitializeLifetimeService(); + return lease; + } +#endif // FEATURE_REMOTING + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/FormatterConverter.cs b/src/mscorlib/src/System/Runtime/Serialization/FormatterConverter.cs new file mode 100644 index 0000000000..7df221c9cd --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/FormatterConverter.cs @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: A base implementation of the IFormatterConverter +** interface that uses the Convert class and the +** IConvertible interface. +** +** +============================================================*/ +namespace System.Runtime.Serialization { + using System; + using System.Globalization; + using System.Diagnostics.Contracts; + +[System.Runtime.InteropServices.ComVisible(true)] + public class FormatterConverter : IFormatterConverter { + + public FormatterConverter() { + } + + public Object Convert(Object value, Type type) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + } + + public Object Convert(Object value, TypeCode typeCode) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ChangeType(value, typeCode, CultureInfo.InvariantCulture); + } + + public bool ToBoolean(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToBoolean(value, CultureInfo.InvariantCulture); + } + + public char ToChar(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToChar(value, CultureInfo.InvariantCulture); + } + + [CLSCompliant(false)] + public sbyte ToSByte(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToSByte(value, CultureInfo.InvariantCulture); + } + + public byte ToByte(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToByte(value, CultureInfo.InvariantCulture); + } + + public short ToInt16(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToInt16(value, CultureInfo.InvariantCulture); + } + + [CLSCompliant(false)] + public ushort ToUInt16(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToUInt16(value, CultureInfo.InvariantCulture); + } + + public int ToInt32(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToInt32(value, CultureInfo.InvariantCulture); + } + + [CLSCompliant(false)] + public uint ToUInt32(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToUInt32(value, CultureInfo.InvariantCulture); + } + + public long ToInt64(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToInt64(value, CultureInfo.InvariantCulture); + } + + [CLSCompliant(false)] + public ulong ToUInt64(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToUInt64(value, CultureInfo.InvariantCulture); + } + + public float ToSingle(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToSingle(value, CultureInfo.InvariantCulture); + } + + public double ToDouble(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToDouble(value, CultureInfo.InvariantCulture); + } + + public Decimal ToDecimal(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToDecimal(value, CultureInfo.InvariantCulture); + } + + public DateTime ToDateTime(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToDateTime(value, CultureInfo.InvariantCulture); + } + + public String ToString(Object value) { + if (value==null) { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + return System.Convert.ToString(value, CultureInfo.InvariantCulture); + } + } +} + diff --git a/src/mscorlib/src/System/Runtime/Serialization/FormatterServices.cs b/src/mscorlib/src/System/Runtime/Serialization/FormatterServices.cs new file mode 100644 index 0000000000..c6f27b5b2a --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/FormatterServices.cs @@ -0,0 +1,590 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Provides some static methods to aid with the implementation +** of a Formatter for Serialization. +** +** +============================================================*/ +namespace System.Runtime.Serialization { + + using System; + using System.Reflection; + using System.Collections; + using System.Collections.Generic; + using System.Security; + using System.Security.Permissions; +#if FEATURE_SERIALIZATION + using System.Runtime.Serialization.Formatters; +#endif + using System.Runtime.Remoting; + using System.Runtime.CompilerServices; + using System.Runtime.Versioning; + using System.Threading; + using System.IO; + using System.Text; + using System.Globalization; + using System.Diagnostics.Contracts; + + [System.Runtime.InteropServices.ComVisible(true)] + public static class FormatterServices { +#if FEATURE_SERIALIZATION + internal static Dictionary<MemberHolder, MemberInfo[]> m_MemberInfoTable = new Dictionary<MemberHolder, MemberInfo[]>(32); + [System.Security.SecurityCritical] + private static bool unsafeTypeForwardersIsEnabled = false; + + [System.Security.SecurityCritical] + private static volatile bool unsafeTypeForwardersIsEnabledInitialized = false; + + private static Object s_FormatterServicesSyncObject = null; + + private static Object formatterServicesSyncObject + { + get + { + if (s_FormatterServicesSyncObject == null) + { + Object o = new Object(); + Interlocked.CompareExchange<Object>(ref s_FormatterServicesSyncObject, o, null); + } + return s_FormatterServicesSyncObject; + } + } + + [SecuritySafeCritical] + static FormatterServices() + { + // Static initialization touches security critical types, so we need an + // explicit static constructor to allow us to mark it safe critical. + } + + private static MemberInfo[] GetSerializableMembers(RuntimeType type) { + // get the list of all fields + FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + int countProper = 0; + for (int i = 0; i < fields.Length; i++) { + if ((fields[i].Attributes & FieldAttributes.NotSerialized) == FieldAttributes.NotSerialized) + continue; + countProper++; + } + if (countProper != fields.Length) { + FieldInfo[] properFields = new FieldInfo[countProper]; + countProper = 0; + for (int i = 0; i < fields.Length; i++) { + if ((fields[i].Attributes & FieldAttributes.NotSerialized) == FieldAttributes.NotSerialized) + continue; + properFields[countProper] = fields[i]; + countProper++; + } + return properFields; + } + else + return fields; + } + + private static bool CheckSerializable(RuntimeType type) { + if (type.IsSerializable) { + return true; + } + return false; + } + + private static MemberInfo[] InternalGetSerializableMembers(RuntimeType type) { + List<SerializationFieldInfo> allMembers = null; + MemberInfo[] typeMembers; + FieldInfo [] typeFields; + RuntimeType parentType; + + Contract.Assert((object)type != null, "[GetAllSerializableMembers]type!=null"); + + if (type.IsInterface) { + return new MemberInfo[0]; + } + + if (!(CheckSerializable(type))) { + throw new SerializationException(Environment.GetResourceString("Serialization_NonSerType", type.FullName, type.Module.Assembly.FullName)); + } + + //Get all of the serializable members in the class to be serialized. + typeMembers = GetSerializableMembers(type); + + //If this class doesn't extend directly from object, walk its hierarchy and + //get all of the private and assembly-access fields (e.g. all fields that aren't + //virtual) and include them in the list of things to be serialized. + parentType = (RuntimeType)(type.BaseType); + if (parentType != null && parentType != (RuntimeType)typeof(Object)) { + RuntimeType[] parentTypes = null; + int parentTypeCount = 0; + bool classNamesUnique = GetParentTypes(parentType, out parentTypes, out parentTypeCount); + if (parentTypeCount > 0){ + allMembers = new List<SerializationFieldInfo>(); + for (int i = 0; i < parentTypeCount;i++){ + parentType = parentTypes[i]; + if (!CheckSerializable(parentType)) { + throw new SerializationException(Environment.GetResourceString("Serialization_NonSerType", parentType.FullName, parentType.Module.Assembly.FullName)); + } + + typeFields = parentType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + String typeName = classNamesUnique ? parentType.Name : parentType.FullName; + foreach (FieldInfo field in typeFields) { + // Family and Assembly fields will be gathered by the type itself. + if (!field.IsNotSerialized) { + allMembers.Add(new SerializationFieldInfo((RuntimeFieldInfo)field, typeName)); + } + } + } + //If we actually found any new MemberInfo's, we need to create a new MemberInfo array and + //copy all of the members which we've found so far into that. + if (allMembers!=null && allMembers.Count>0) { + MemberInfo[] membersTemp = new MemberInfo[allMembers.Count + typeMembers.Length]; + Array.Copy(typeMembers, membersTemp, typeMembers.Length); + ((ICollection)allMembers).CopyTo(membersTemp, typeMembers.Length); + typeMembers = membersTemp; + } + } + } + return typeMembers; + } + + private static bool GetParentTypes(RuntimeType parentType, out RuntimeType[] parentTypes, out int parentTypeCount){ + //Check if there are any dup class names. Then we need to include as part of + //typeName to prefix the Field names in SerializationFieldInfo + /*out*/ parentTypes = null; + /*out*/ parentTypeCount = 0; + bool unique = true; + RuntimeType objectType = (RuntimeType)typeof(object); + for (RuntimeType t1 = parentType; t1 != objectType; t1 = (RuntimeType)t1.BaseType) + { + if (t1.IsInterface) continue; + string t1Name = t1.Name; + for(int i=0;unique && i<parentTypeCount;i++){ + string t2Name = parentTypes[i].Name; + if (t2Name.Length == t1Name.Length && t2Name[0] == t1Name[0] && t1Name == t2Name){ + unique = false; + break; + } + } + //expand array if needed + if (parentTypes == null || parentTypeCount == parentTypes.Length){ + RuntimeType[] tempParentTypes = new RuntimeType[Math.Max(parentTypeCount*2, 12)]; + if (parentTypes != null) + Array.Copy(parentTypes, 0, tempParentTypes, 0, parentTypeCount); + parentTypes = tempParentTypes; + } + parentTypes[parentTypeCount++] = t1; + } + return unique; + } + + // Get all of the Serializable members for a particular class. For all practical intents and + // purposes, this is the non-transient, non-static members (fields and properties). In order to + // be included, properties must have both a getter and a setter. N.B.: A class + // which implements ISerializable or has a serialization surrogate may not use all of these members + // (or may have additional members). + [System.Security.SecurityCritical] // auto-generated_required + public static MemberInfo[] GetSerializableMembers(Type type) { + return GetSerializableMembers(type, new StreamingContext(StreamingContextStates.All)); + } + + // Get all of the Serializable Members for a particular class. If we're not cloning, this is all + // non-transient, non-static fields. If we are cloning, include the transient fields as well since + // we know that we're going to live inside of the same context. + [System.Security.SecurityCritical] // auto-generated_required + public static MemberInfo[] GetSerializableMembers(Type type, StreamingContext context) { + MemberInfo[] members; + + if ((object)type==null) { + throw new ArgumentNullException("type"); + } + Contract.EndContractBlock(); + + if (!(type is RuntimeType)) { + throw new SerializationException(Environment.GetResourceString("Serialization_InvalidType", type.ToString())); + } + + MemberHolder mh = new MemberHolder(type, context); + + //If we've already gathered the members for this type, just return them. + if (m_MemberInfoTable.ContainsKey(mh)) { + return m_MemberInfoTable[mh]; + } + + lock (formatterServicesSyncObject) { + //If we've already gathered the members for this type, just return them. + if (m_MemberInfoTable.ContainsKey(mh)) { + return m_MemberInfoTable[mh]; + } + + members = InternalGetSerializableMembers((RuntimeType)type); + + m_MemberInfoTable[mh] = members; + } + + return members; + } + + static readonly Type[] advancedTypes = new Type[]{ + typeof(System.DelegateSerializationHolder), +#if FEATURE_REMOTING + typeof(System.Runtime.Remoting.ObjRef), + typeof(System.Runtime.Remoting.IEnvoyInfo), + typeof(System.Runtime.Remoting.Lifetime.ISponsor), +#endif + }; + + public static void CheckTypeSecurity(Type t, TypeFilterLevel securityLevel) { + if (securityLevel == TypeFilterLevel.Low){ + for(int i=0;i<advancedTypes.Length;i++){ + if (advancedTypes[i].IsAssignableFrom(t)) + throw new SecurityException(Environment.GetResourceString("Serialization_TypeSecurity", advancedTypes[i].FullName, t.FullName)); + } + } + } +#endif // FEATURE_SERIALIZATION + + // Gets a new instance of the object. The entire object is initalized to 0 and no + // constructors have been run. **THIS MEANS THAT THE OBJECT MAY NOT BE IN A STATE + // CONSISTENT WITH ITS INTERNAL REQUIREMENTS** This method should only be used for + // deserialization when the user intends to immediately populate all fields. This method + // will not create an unitialized string because it is non-sensical to create an empty + // instance of an immutable type. + // + [System.Security.SecurityCritical] // auto-generated_required + public static Object GetUninitializedObject(Type type) { + if ((object)type == null) { + throw new ArgumentNullException("type"); + } + Contract.EndContractBlock(); + + if (!(type is RuntimeType)) { + throw new SerializationException(Environment.GetResourceString("Serialization_InvalidType", type.ToString())); + } + + return nativeGetUninitializedObject((RuntimeType)type); + } + + [System.Security.SecurityCritical] // auto-generated_required + public static Object GetSafeUninitializedObject(Type type) { + if ((object)type == null) { + throw new ArgumentNullException("type"); + } + Contract.EndContractBlock(); + + if (!(type is RuntimeType)) { + throw new SerializationException(Environment.GetResourceString("Serialization_InvalidType", type.ToString())); + } +#if FEATURE_REMOTING + if (Object.ReferenceEquals(type, typeof(System.Runtime.Remoting.Messaging.ConstructionCall)) || + Object.ReferenceEquals(type, typeof(System.Runtime.Remoting.Messaging.LogicalCallContext)) || + Object.ReferenceEquals(type, typeof(System.Runtime.Remoting.Contexts.SynchronizationAttribute))) + return nativeGetUninitializedObject((RuntimeType)type); +#endif + + try { + return nativeGetSafeUninitializedObject((RuntimeType)type); + } + catch(SecurityException e) { + throw new SerializationException(Environment.GetResourceString("Serialization_Security", type.FullName), e); + } + } + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Object nativeGetSafeUninitializedObject(RuntimeType type); + + [System.Security.SecurityCritical] // auto-generated + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern Object nativeGetUninitializedObject(RuntimeType type); +#if FEATURE_SERIALIZATION + [System.Security.SecurityCritical] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static extern bool GetEnableUnsafeTypeForwarders(); + + [SecuritySafeCritical] + internal static bool UnsafeTypeForwardersIsEnabled() + { + if (!unsafeTypeForwardersIsEnabledInitialized) + { + unsafeTypeForwardersIsEnabled = GetEnableUnsafeTypeForwarders(); + unsafeTypeForwardersIsEnabledInitialized = true; + } + + return unsafeTypeForwardersIsEnabled; + } +#endif + private static Binder s_binder = Type.DefaultBinder; + [System.Security.SecurityCritical] + internal static void SerializationSetValue(MemberInfo fi, Object target, Object value) + { + Contract.Requires(fi != null); + + RtFieldInfo rtField = fi as RtFieldInfo; + + if (rtField != null) + { + rtField.CheckConsistency(target); + rtField.UnsafeSetValue(target, value, BindingFlags.Default, s_binder, null); + return; + } + + SerializationFieldInfo serField = fi as SerializationFieldInfo; + if (serField != null) + { + serField.InternalSetValue(target, value, BindingFlags.Default, s_binder, null); + return; + } + + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFieldInfo")); + } + + // Fill in the members of obj with the data contained in data. + // Returns the number of members populated. + // + [System.Security.SecurityCritical] // auto-generated_required + public static Object PopulateObjectMembers(Object obj, MemberInfo[] members, Object[] data) { + if (obj==null) { + throw new ArgumentNullException("obj"); + } + + if (members==null) { + throw new ArgumentNullException("members"); + } + + if (data==null) { + throw new ArgumentNullException("data"); + } + + if (members.Length!=data.Length) { + throw new ArgumentException(Environment.GetResourceString("Argument_DataLengthDifferent")); + } + Contract.EndContractBlock(); + + MemberInfo mi; + + BCLDebug.Trace("SER", "[PopulateObjectMembers]Enter."); + + for (int i=0; i<members.Length; i++) { + mi = members[i]; + + if (mi==null) { + throw new ArgumentNullException("members", Environment.GetResourceString("ArgumentNull_NullMember", i)); + } + + //If we find an empty, it means that the value was never set during deserialization. + //This is either a forward reference or a null. In either case, this may break some of the + //invariants mantained by the setter, so we'll do nothing with it for right now. + if (data[i]!=null) { + if (mi.MemberType==MemberTypes.Field) { + SerializationSetValue(mi, obj, data[i]); + } else { + throw new SerializationException(Environment.GetResourceString("Serialization_UnknownMemberInfo")); + } + + BCLDebug.Trace("SER", "[PopulateObjectMembers]\tType:", obj.GetType(), "\tMember:", + members[i].Name, " with member type: ", ((FieldInfo)members[i]).FieldType); + } + //Console.WriteLine("X"); + } + + BCLDebug.Trace("SER", "[PopulateObjectMembers]Leave."); + + return obj; + } + + // Extracts the data from obj. members is the array of members which we wish to + // extract (must be FieldInfos or PropertyInfos). For each supplied member, extract the matching value and + // return it in a Object[] of the same size. + // + [System.Security.SecurityCritical] // auto-generated_required + public static Object[] GetObjectData(Object obj, MemberInfo[] members) { + + if (obj==null) { + throw new ArgumentNullException("obj"); + } + + if (members==null) { + throw new ArgumentNullException("members"); + } + Contract.EndContractBlock(); + + int numberOfMembers = members.Length; + + Object[] data = new Object[numberOfMembers]; + MemberInfo mi; + + for (int i=0; i<numberOfMembers; i++) { + mi=members[i]; + + if (mi==null) { + throw new ArgumentNullException("members", Environment.GetResourceString("ArgumentNull_NullMember", i)); + } + + if (mi.MemberType==MemberTypes.Field) { + Contract.Assert(mi is RuntimeFieldInfo || mi is SerializationFieldInfo, + "[FormatterServices.GetObjectData]mi is RuntimeFieldInfo || mi is SerializationFieldInfo."); + + RtFieldInfo rfi = mi as RtFieldInfo; + if (rfi != null) { + rfi.CheckConsistency(obj); + data[i] = rfi.UnsafeGetValue(obj); + } else { + data[i] = ((SerializationFieldInfo)mi).InternalGetValue(obj); + } + } else { + throw new SerializationException(Environment.GetResourceString("Serialization_UnknownMemberInfo")); + } + } + + return data; + } + + [System.Security.SecurityCritical] // auto-generated_required + [System.Runtime.InteropServices.ComVisible(false)] + public static ISerializationSurrogate GetSurrogateForCyclicalReference(ISerializationSurrogate innerSurrogate) + { + if (innerSurrogate == null) + throw new ArgumentNullException("innerSurrogate"); + Contract.EndContractBlock(); + return new SurrogateForCyclicalReference(innerSurrogate); + } + + /*=============================GetTypeFromAssembly============================== + **Action: + **Returns: + **Arguments: + **Exceptions: + ==============================================================================*/ + [System.Security.SecurityCritical] // auto-generated_required + public static Type GetTypeFromAssembly(Assembly assem, String name) { + if (assem==null) + throw new ArgumentNullException("assem"); + Contract.EndContractBlock(); + return assem.GetType(name, false, false); + } + + /*============================LoadAssemblyFromString============================ + **Action: Loads an assembly from a given string. The current assembly loading story + ** is quite confusing. If the assembly is in the fusion cache, we can load it + ** using the stringized-name which we transmitted over the wire. If that fails, + ** we try for a lookup of the assembly using the simple name which is the first + ** part of the assembly name. If we can't find it that way, we'll return null + ** as our failure result. + **Returns: The loaded assembly or null if it can't be found. + **Arguments: assemblyName -- The stringized assembly name. + **Exceptions: None + ==============================================================================*/ + internal static Assembly LoadAssemblyFromString(String assemblyName) { + // + // Try using the stringized assembly name to load from the fusion cache. + // + BCLDebug.Trace("SER", "[LoadAssemblyFromString]Looking for assembly: ", assemblyName); + Assembly found = Assembly.Load(assemblyName); + return found; + } + + internal static Assembly LoadAssemblyFromStringNoThrow(String assemblyName) { + try { + return LoadAssemblyFromString(assemblyName); + } + catch (Exception e){ + BCLDebug.Trace("SER", "[LoadAssemblyFromString]", e.ToString()); + } + return null; + } + + internal static string GetClrAssemblyName(Type type, out bool hasTypeForwardedFrom) { + if ((object)type == null) { + throw new ArgumentNullException("type"); + } + + object[] typeAttributes = type.GetCustomAttributes(typeof(TypeForwardedFromAttribute), false); + if (typeAttributes != null && typeAttributes.Length > 0) { + hasTypeForwardedFrom = true; + TypeForwardedFromAttribute typeForwardedFromAttribute = (TypeForwardedFromAttribute)typeAttributes[0]; + return typeForwardedFromAttribute.AssemblyFullName; + } + else { + hasTypeForwardedFrom = false; + return type.Assembly.FullName; + } + } + + internal static string GetClrTypeFullName(Type type) { + if (type.IsArray) { + return GetClrTypeFullNameForArray(type); + } + else { + return GetClrTypeFullNameForNonArrayTypes(type); + } + } + + static string GetClrTypeFullNameForArray(Type type) { + int rank = type.GetArrayRank(); + if (rank == 1) + { + return String.Format(CultureInfo.InvariantCulture, "{0}{1}", GetClrTypeFullName(type.GetElementType()), "[]"); + } + else + { + StringBuilder builder = new StringBuilder(GetClrTypeFullName(type.GetElementType())).Append("["); + for (int commaIndex = 1; commaIndex < rank; commaIndex++) + { + builder.Append(","); + } + builder.Append("]"); + return builder.ToString(); + } + } + + static string GetClrTypeFullNameForNonArrayTypes(Type type) { + if (!type.IsGenericType) { + return type.FullName; + } + + Type[] genericArguments = type.GetGenericArguments(); + StringBuilder builder = new StringBuilder(type.GetGenericTypeDefinition().FullName).Append("["); + bool hasTypeForwardedFrom; + + foreach (Type genericArgument in genericArguments) { + builder.Append("[").Append(GetClrTypeFullName(genericArgument)).Append(", "); + builder.Append(GetClrAssemblyName(genericArgument, out hasTypeForwardedFrom)).Append("],"); + } + + //remove the last comma and close typename for generic with a close bracket + return builder.Remove(builder.Length - 1, 1).Append("]").ToString(); + } + } + + internal sealed class SurrogateForCyclicalReference : ISerializationSurrogate + { + ISerializationSurrogate innerSurrogate; + internal SurrogateForCyclicalReference(ISerializationSurrogate innerSurrogate) + { + if (innerSurrogate == null) + throw new ArgumentNullException("innerSurrogate"); + this.innerSurrogate = innerSurrogate; + } + + [System.Security.SecurityCritical] // auto-generated + public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) + { + innerSurrogate.GetObjectData(obj, info, context); + } + + [System.Security.SecurityCritical] // auto-generated + public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) + { + return innerSurrogate.SetObjectData(obj, info, context, selector); + } + } +} + + + + + diff --git a/src/mscorlib/src/System/Runtime/Serialization/IDeserializationCallback.cs b/src/mscorlib/src/System/Runtime/Serialization/IDeserializationCallback.cs new file mode 100644 index 0000000000..e2497e5d34 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/IDeserializationCallback.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: IDeserializationEventListener +** +** +** Purpose: Implemented by any class that wants to indicate that +** it wishes to receive deserialization events. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + using System; + + // Interface does not need to be marked with the serializable attribute + [System.Runtime.InteropServices.ComVisible(true)] + public interface IDeserializationCallback { + void OnDeserialization(Object sender); + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/IFormatter.cs b/src/mscorlib/src/System/Runtime/Serialization/IFormatter.cs new file mode 100644 index 0000000000..8d91d95acf --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/IFormatter.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: IFormatter; +** +** +** Purpose: The interface for all formatters. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + using System.Runtime.Remoting; + using System; + using System.IO; + +[System.Runtime.InteropServices.ComVisible(true)] + public interface IFormatter { + Object Deserialize(Stream serializationStream); + + void Serialize(Stream serializationStream, Object graph); + + + ISurrogateSelector SurrogateSelector { + get; + set; + } + + SerializationBinder Binder { + get; + set; + } + + StreamingContext Context { + get; + set; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/IFormatterConverter.cs b/src/mscorlib/src/System/Runtime/Serialization/IFormatterConverter.cs new file mode 100644 index 0000000000..182cc93412 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/IFormatterConverter.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: The interface provides the connection between an +** instance of SerializationInfo and the formatter-provided +** class which knows how to parse the data inside the +** SerializationInfo. +** +** +============================================================*/ +namespace System.Runtime.Serialization { + using System; + + [CLSCompliant(false)] +[System.Runtime.InteropServices.ComVisible(true)] + public interface IFormatterConverter { + Object Convert(Object value, Type type); + Object Convert(Object value, TypeCode typeCode); + bool ToBoolean(Object value); + char ToChar(Object value); + sbyte ToSByte(Object value); + byte ToByte(Object value); + short ToInt16(Object value); + ushort ToUInt16(Object value); + int ToInt32(Object value); + uint ToUInt32(Object value); + long ToInt64(Object value); + ulong ToUInt64(Object value); + float ToSingle(Object value); + double ToDouble(Object value); + Decimal ToDecimal(Object value); + DateTime ToDateTime(Object value); + String ToString(Object value); + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/IObjectReference.cs b/src/mscorlib/src/System/Runtime/Serialization/IObjectReference.cs new file mode 100644 index 0000000000..f1a1bc0590 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/IObjectReference.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: IObjectReference +** +** +** Purpose: Implemented by objects that are actually references +** to a different object which can't be discovered until +** this one is completely restored. During the fixup stage, +** any object implementing IObjectReference is asked for it's +** "real" object and that object is inserted into the graph. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + + using System; + using System.Security.Permissions; + // Interface does not need to be marked with the serializable attribute +[System.Runtime.InteropServices.ComVisible(true)] + public interface IObjectReference { + [System.Security.SecurityCritical] // auto-generated_required + Object GetRealObject(StreamingContext context); + } +} + + diff --git a/src/mscorlib/src/System/Runtime/Serialization/ISerializable.cs b/src/mscorlib/src/System/Runtime/Serialization/ISerializable.cs new file mode 100644 index 0000000000..e59fa65043 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/ISerializable.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: ISerializable +** +** +** Purpose: Implemented by any object that needs to control its +** own serialization. +** +** +===========================================================*/ + +namespace System.Runtime.Serialization { + using System.Runtime.Remoting; + using System.Runtime.Serialization; + using System.Security.Permissions; + using System; + using System.Reflection; + + [System.Runtime.InteropServices.ComVisible(true)] + public interface ISerializable { + [System.Security.SecurityCritical] // auto-generated_required + void GetObjectData(SerializationInfo info, StreamingContext context); + } + +} + + + + diff --git a/src/mscorlib/src/System/Runtime/Serialization/ISerializationSurrogate.cs b/src/mscorlib/src/System/Runtime/Serialization/ISerializationSurrogate.cs new file mode 100644 index 0000000000..9bb30d99e0 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/ISerializationSurrogate.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: ISurrogate +** +** +** Purpose: The interface implemented by an object which +** supports surrogates. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + using System.Runtime.Remoting; + using System.Runtime.Serialization; + using System.Security.Permissions; + using System; + using System.Reflection; +[System.Runtime.InteropServices.ComVisible(true)] + public interface ISerializationSurrogate { + // Interface does not need to be marked with the serializable attribute + // Returns a SerializationInfo completely populated with all of the data needed to reinstantiate the + // the object at the other end of serialization. + // + [System.Security.SecurityCritical] // auto-generated_required + void GetObjectData(Object obj, SerializationInfo info, StreamingContext context); + + // Reinflate the object using all of the information in data. The information in + // members is used to find the particular field or property which needs to be set. + // + [System.Security.SecurityCritical] // auto-generated_required + Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector); + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/ISurrogateSelector.cs b/src/mscorlib/src/System/Runtime/Serialization/ISurrogateSelector.cs new file mode 100644 index 0000000000..01b960f86b --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/ISurrogateSelector.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: ISurrogateSelector +** +** +** Purpose: A user-supplied class for doing the type to surrogate +** mapping. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + + using System.Runtime.Remoting; + using System.Security.Permissions; + using System; +[System.Runtime.InteropServices.ComVisible(true)] + public interface ISurrogateSelector { + // Interface does not need to be marked with the serializable attribute + // Specifies the next ISurrogateSelector to be examined for surrogates if the current + // instance doesn't have a surrogate for the given type and assembly in the given context. + [System.Security.SecurityCritical] // auto-generated_required + void ChainSelector(ISurrogateSelector selector); + + // Returns the appropriate surrogate for the given type in the given context. + [System.Security.SecurityCritical] // auto-generated_required + ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector); + + + // Return the next surrogate in the chain. Returns null if no more exist. + [System.Security.SecurityCritical] // auto-generated_required + ISurrogateSelector GetNextSelector(); + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/MemberHolder.cs b/src/mscorlib/src/System/Runtime/Serialization/MemberHolder.cs new file mode 100644 index 0000000000..1303e40c27 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/MemberHolder.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: This is a lightweight class designed to hold the members +** and StreamingContext for a particular class. +** +** +============================================================*/ +namespace System.Runtime.Serialization { + + using System.Runtime.Remoting; + using System; + using System.Reflection; + [Serializable] + internal class MemberHolder { +// disable csharp compiler warning #0414: field assigned unused value +#pragma warning disable 0414 + internal MemberInfo[] members = null; +#pragma warning restore 0414 + internal Type memberType; + internal StreamingContext context; + + internal MemberHolder(Type type, StreamingContext ctx) { + memberType = type; + context = ctx; + } + + public override int GetHashCode() { + return memberType.GetHashCode(); + } + + public override bool Equals(Object obj) { + if (!(obj is MemberHolder)) { + return false; + } + + MemberHolder temp = (MemberHolder)obj; + + if (Object.ReferenceEquals(temp.memberType, memberType) && temp.context.State == context.State) { + return true; + } + + return false; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/SerializationAttributes.cs b/src/mscorlib/src/System/Runtime/Serialization/SerializationAttributes.cs new file mode 100644 index 0000000000..27c5751cc6 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/SerializationAttributes.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Various Attributes for Serialization +** +** +============================================================*/ +namespace System.Runtime.Serialization +{ + using System; + using System.Diagnostics.Contracts; + using System.Reflection; + + [AttributeUsage(AttributeTargets.Field, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class OptionalFieldAttribute : Attribute + { + int versionAdded = 1; + public OptionalFieldAttribute() { } + + public int VersionAdded + { + get { + return this.versionAdded; + } + set { + if (value < 1) + throw new ArgumentException(Environment.GetResourceString("Serialization_OptionalFieldVersionValue")); + Contract.EndContractBlock(); + this.versionAdded = value; + } + } + } + + [AttributeUsage(AttributeTargets.Method, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class OnSerializingAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class OnSerializedAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class OnDeserializingAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method, Inherited=false)] +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class OnDeserializedAttribute : Attribute + { + } + +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/SerializationBinder.cs b/src/mscorlib/src/System/Runtime/Serialization/SerializationBinder.cs new file mode 100644 index 0000000000..7457991008 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/SerializationBinder.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Interface: SerializationBinder +** +** +** Purpose: The base class of serialization binders. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + using System; + + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public abstract class SerializationBinder { + + public virtual void BindToName(Type serializedType, out String assemblyName, out String typeName) + { + assemblyName = null; + typeName = null; + } + + public abstract Type BindToType(String assemblyName, String typeName); + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/SerializationException.cs b/src/mscorlib/src/System/Runtime/Serialization/SerializationException.cs new file mode 100644 index 0000000000..b88ccfd706 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/SerializationException.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================================= +** +** +** +** Purpose: Thrown when something goes wrong during serialization or +** deserialization. +** +** +=============================================================================*/ + +namespace System.Runtime.Serialization { + + using System; + using System.Runtime.Serialization; + + [System.Runtime.InteropServices.ComVisible(true)] + [Serializable] + public class SerializationException : SystemException { + + private static String _nullMessage = Environment.GetResourceString("Arg_SerializationException"); + + // Creates a new SerializationException with its message + // string set to a default message. + public SerializationException() + : base(_nullMessage) { + SetErrorCode(__HResults.COR_E_SERIALIZATION); + } + + public SerializationException(String message) + : base(message) { + SetErrorCode(__HResults.COR_E_SERIALIZATION); + } + + public SerializationException(String message, Exception innerException) : base (message, innerException) { + SetErrorCode(__HResults.COR_E_SERIALIZATION); + } + + protected SerializationException(SerializationInfo info, StreamingContext context) : base (info, context) { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/SerializationFieldInfo.cs b/src/mscorlib/src/System/Runtime/Serialization/SerializationFieldInfo.cs new file mode 100644 index 0000000000..5e7851bc57 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/SerializationFieldInfo.cs @@ -0,0 +1,166 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Provides a methods of representing imaginary fields +** which are unique to serialization. In this case, what we're +** representing is the private members of parent classes. We +** aggregate the RuntimeFieldInfo associated with this member +** and return a managled form of the name. The name that we +** return is .parentname.fieldname +** +** +============================================================*/ + +namespace System.Runtime.Serialization { + + using System; + using System.Reflection; + using System.Globalization; + using System.Diagnostics.Contracts; + using System.Threading; +#if FEATURE_REMOTING + using System.Runtime.Remoting.Metadata; +#endif //FEATURE_REMOTING + + internal sealed class SerializationFieldInfo : FieldInfo { + + internal const String FakeNameSeparatorString = "+"; + + private RuntimeFieldInfo m_field; + private String m_serializationName; + + public override Module Module { get { return m_field.Module; } } + public override int MetadataToken { get { return m_field.MetadataToken; } } + + internal SerializationFieldInfo(RuntimeFieldInfo field, String namePrefix) { + Contract.Assert(field!=null, "[SerializationFieldInfo.ctor]field!=null"); + Contract.Assert(namePrefix!=null, "[SerializationFieldInfo.ctor]namePrefix!=null"); + + m_field = field; + m_serializationName = String.Concat(namePrefix, FakeNameSeparatorString, m_field.Name); + } + + // + // MemberInfo methods + // + public override String Name { + get { + return m_serializationName; + } + } + + public override Type DeclaringType { + get { + return m_field.DeclaringType; + } + } + + public override Type ReflectedType { + get { + return m_field.ReflectedType; + } + } + + public override Object[] GetCustomAttributes(bool inherit) { + return m_field.GetCustomAttributes(inherit); + } + + public override Object[] GetCustomAttributes(Type attributeType, bool inherit) { + return m_field.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) { + return m_field.IsDefined(attributeType, inherit); + } + + // + // FieldInfo methods + // + public override Type FieldType { + get { + return m_field.FieldType; + } + } + + public override Object GetValue(Object obj) { + return m_field.GetValue(obj); + } + + [System.Security.SecurityCritical] + internal Object InternalGetValue(Object obj) { + RtFieldInfo field = m_field as RtFieldInfo; + if (field != null) + { + field.CheckConsistency(obj); + return field.UnsafeGetValue(obj); + } + else + return m_field.GetValue(obj); + } + + public override void SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) { + m_field.SetValue(obj, value, invokeAttr, binder, culture); + } + + [System.Security.SecurityCritical] + internal void InternalSetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) { + RtFieldInfo field = m_field as RtFieldInfo; + if (field != null) + { + field.CheckConsistency(obj); + field.UnsafeSetValue(obj, value, invokeAttr, binder, culture); + } + else + m_field.SetValue(obj, value, invokeAttr, binder, culture); + } + + internal RuntimeFieldInfo FieldInfo { + get { + return m_field; + } + } + + public override RuntimeFieldHandle FieldHandle { + get { + return m_field.FieldHandle; + } + } + + public override FieldAttributes Attributes { + get { + return m_field.Attributes; + } + } + +#if FEATURE_REMOTING + #region Legacy Remoting Cache + private RemotingFieldCachedData m_cachedData; + + internal RemotingFieldCachedData RemotingCache + { + get + { + // This grabs an internal copy of m_cachedData and uses + // that instead of looking at m_cachedData directly because + // the cache may get cleared asynchronously. This prevents + // us from having to take a lock. + RemotingFieldCachedData cache = m_cachedData; + if (cache == null) + { + cache = new RemotingFieldCachedData(this); + RemotingFieldCachedData ret = Interlocked.CompareExchange(ref m_cachedData, cache, null); + if (ret != null) + cache = ret; + } + return cache; + } + } + #endregion +#endif //FEATURE_REMOTING + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/SerializationInfo.cs b/src/mscorlib/src/System/Runtime/Serialization/SerializationInfo.cs new file mode 100644 index 0000000000..94e6825b51 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/SerializationInfo.cs @@ -0,0 +1,798 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: The structure for holding all of the data needed +** for object serialization and deserialization. +** +** +===========================================================*/ +namespace System.Runtime.Serialization +{ + + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Runtime.Remoting; +#if FEATURE_REMOTING + using System.Runtime.Remoting.Proxies; +#endif + using System.Globalization; + using System.Diagnostics.Contracts; + using System.Security; +#if FEATURE_CORECLR + using System.Runtime.CompilerServices; +#endif + + [System.Runtime.InteropServices.ComVisible(true)] + public sealed class SerializationInfo + { + private const int defaultSize = 4; + private const string s_mscorlibAssemblySimpleName = System.CoreLib.Name; + private const string s_mscorlibFileName = s_mscorlibAssemblySimpleName + ".dll"; + + // Even though we have a dictionary, we're still keeping all the arrays around for back-compat. + // Otherwise we may run into potentially breaking behaviors like GetEnumerator() not returning entries in the same order they were added. + internal String[] m_members; + internal Object[] m_data; + internal Type[] m_types; + private Dictionary<string, int> m_nameToIndex; + internal int m_currMember; + internal IFormatterConverter m_converter; + private String m_fullTypeName; + private String m_assemName; + private Type objectType; + private bool isFullTypeNameSetExplicit; + private bool isAssemblyNameSetExplicit; + private bool requireSameTokenInPartialTrust; + + [CLSCompliant(false)] + public SerializationInfo(Type type, IFormatterConverter converter) + : this(type, converter, false) + { + } + + [CLSCompliant(false)] + public SerializationInfo(Type type, IFormatterConverter converter, bool requireSameTokenInPartialTrust) + { + if ((object)type == null) + { + throw new ArgumentNullException("type"); + } + + if (converter == null) + { + throw new ArgumentNullException("converter"); + } + + Contract.EndContractBlock(); + + objectType = type; + m_fullTypeName = type.FullName; + m_assemName = type.Module.Assembly.FullName; + + m_members = new String[defaultSize]; + m_data = new Object[defaultSize]; + m_types = new Type[defaultSize]; + + m_nameToIndex = new Dictionary<string, int>(); + + m_converter = converter; + + this.requireSameTokenInPartialTrust = requireSameTokenInPartialTrust; + } + + public String FullTypeName + { + get + { + return m_fullTypeName; + } + set + { + if (null == value) + { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + + m_fullTypeName = value; + isFullTypeNameSetExplicit = true; + } + } + + public String AssemblyName + { + get + { + return m_assemName; + } + [SecuritySafeCritical] + set + { + if (null == value) + { + throw new ArgumentNullException("value"); + } + Contract.EndContractBlock(); + if (this.requireSameTokenInPartialTrust) + { + DemandForUnsafeAssemblyNameAssignments(this.m_assemName, value); + } + m_assemName = value; + isAssemblyNameSetExplicit = true; + } + } + + [SecuritySafeCritical] + public void SetType(Type type) + { + if ((object)type == null) + { + throw new ArgumentNullException("type"); + } + Contract.EndContractBlock(); + + if (this.requireSameTokenInPartialTrust) + { + DemandForUnsafeAssemblyNameAssignments(this.ObjectType.Assembly.FullName, type.Assembly.FullName); + } + + if (!Object.ReferenceEquals(objectType, type)) + { + objectType = type; + m_fullTypeName = type.FullName; + m_assemName = type.Module.Assembly.FullName; + isFullTypeNameSetExplicit = false; + isAssemblyNameSetExplicit = false; + } + } + + private static bool Compare(byte[] a, byte[] b) + { + // if either or both assemblies do not have public key token, we should demand, hence, returning false will force a demand + if (a == null || b == null || a.Length == 0 || b.Length == 0 || a.Length != b.Length) + { + return false; + } + else + { + for (int i = 0; i < a.Length; i++) + { + if (a[i] != b[i]) return false; + } + + return true; + } + } + + [SecuritySafeCritical] + internal static void DemandForUnsafeAssemblyNameAssignments(string originalAssemblyName, string newAssemblyName) + { +#if !FEATURE_CORECLR + if (!IsAssemblyNameAssignmentSafe(originalAssemblyName, newAssemblyName)) + { + CodeAccessPermission.Demand(PermissionType.SecuritySerialization); + } +#endif + } + + internal static bool IsAssemblyNameAssignmentSafe(string originalAssemblyName, string newAssemblyName) + { + if (originalAssemblyName == newAssemblyName) + { + return true; + } + + AssemblyName originalAssembly = new AssemblyName(originalAssemblyName); + AssemblyName newAssembly = new AssemblyName(newAssemblyName); + + // mscorlib will get loaded by the runtime regardless of its string casing or its public key token, + // so setting the assembly name to mscorlib must always be protected by a demand + if (string.Equals(newAssembly.Name, s_mscorlibAssemblySimpleName, StringComparison.OrdinalIgnoreCase) || + string.Equals(newAssembly.Name, s_mscorlibFileName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return Compare(originalAssembly.GetPublicKeyToken(), newAssembly.GetPublicKeyToken()); + } + + public int MemberCount + { + get + { + return m_currMember; + } + } + + public Type ObjectType + { + get + { + return objectType; + } + } + + public bool IsFullTypeNameSetExplicit + { + get + { + return isFullTypeNameSetExplicit; + } + } + + public bool IsAssemblyNameSetExplicit + { + get + { + return isAssemblyNameSetExplicit; + } + } + + public SerializationInfoEnumerator GetEnumerator() + { + return new SerializationInfoEnumerator(m_members, m_data, m_types, m_currMember); + } + + private void ExpandArrays() + { + int newSize; + Contract.Assert(m_members.Length == m_currMember, "[SerializationInfo.ExpandArrays]m_members.Length == m_currMember"); + + newSize = (m_currMember * 2); + + // + // In the pathological case, we may wrap + // + if (newSize < m_currMember) + { + if (Int32.MaxValue > m_currMember) + { + newSize = Int32.MaxValue; + } + } + + // + // Allocate more space and copy the data + // + String[] newMembers = new String[newSize]; + Object[] newData = new Object[newSize]; + Type[] newTypes = new Type[newSize]; + + Array.Copy(m_members, newMembers, m_currMember); + Array.Copy(m_data, newData, m_currMember); + Array.Copy(m_types, newTypes, m_currMember); + + // + // Assign the new arrys back to the member vars. + // + m_members = newMembers; + m_data = newData; + m_types = newTypes; + } + + public void AddValue(String name, Object value, Type type) + { + if (null == name) + { + throw new ArgumentNullException("name"); + } + + if ((object)type == null) + { + throw new ArgumentNullException("type"); + } + Contract.EndContractBlock(); + + AddValueInternal(name, value, type); + } + + public void AddValue(String name, Object value) + { + if (null == value) + { + AddValue(name, value, typeof(Object)); + } + else + { + AddValue(name, value, value.GetType()); + } + } + + public void AddValue(String name, bool value) + { + AddValue(name, (Object)value, typeof(bool)); + } + + public void AddValue(String name, char value) + { + AddValue(name, (Object)value, typeof(char)); + } + + + [CLSCompliant(false)] + public void AddValue(String name, sbyte value) + { + AddValue(name, (Object)value, typeof(sbyte)); + } + + public void AddValue(String name, byte value) + { + AddValue(name, (Object)value, typeof(byte)); + } + + public void AddValue(String name, short value) + { + AddValue(name, (Object)value, typeof(short)); + } + + [CLSCompliant(false)] + public void AddValue(String name, ushort value) + { + AddValue(name, (Object)value, typeof(ushort)); + } + + public void AddValue(String name, int value) + { + AddValue(name, (Object)value, typeof(int)); + } + + [CLSCompliant(false)] + public void AddValue(String name, uint value) + { + AddValue(name, (Object)value, typeof(uint)); + } + + public void AddValue(String name, long value) + { + AddValue(name, (Object)value, typeof(long)); + } + + [CLSCompliant(false)] + public void AddValue(String name, ulong value) + { + AddValue(name, (Object)value, typeof(ulong)); + } + + public void AddValue(String name, float value) + { + AddValue(name, (Object)value, typeof(float)); + } + + public void AddValue(String name, double value) + { + AddValue(name, (Object)value, typeof(double)); + } + + public void AddValue(String name, decimal value) + { + AddValue(name, (Object)value, typeof(decimal)); + } + + public void AddValue(String name, DateTime value) + { + AddValue(name, (Object)value, typeof(DateTime)); + } + + internal void AddValueInternal(String name, Object value, Type type) + { + if (m_nameToIndex.ContainsKey(name)) + { + BCLDebug.Trace("SER", "[SerializationInfo.AddValue]Tried to add ", name, " twice to the SI."); + throw new SerializationException(Environment.GetResourceString("Serialization_SameNameTwice")); + } + m_nameToIndex.Add(name, m_currMember); + + // + // If we need to expand the arrays, do so. + // + if (m_currMember >= m_members.Length) + { + ExpandArrays(); + } + + // + // Add the data and then advance the counter. + // + m_members[m_currMember] = name; + m_data[m_currMember] = value; + m_types[m_currMember] = type; + m_currMember++; + } + + /*=================================UpdateValue================================== + **Action: Finds the value if it exists in the current data. If it does, we replace + ** the values, if not, we append it to the end. This is useful to the + ** ObjectManager when it's performing fixups. + **Returns: void + **Arguments: name -- the name of the data to be updated. + ** value -- the new value. + ** type -- the type of the data being added. + **Exceptions: None. All error checking is done with asserts. Although public in coreclr, + ** it's not exposed in a contract and is only meant to be used by corefx. + ==============================================================================*/ +#if FEATURE_CORECLR + // This should not be used by clients: exposing out this functionality would allow children + // to overwrite their parent's values. It is public in order to give corefx access to it for + // its ObjectManager implementation, but it should not be exposed out of a contract. + public +#else + internal +#endif + void UpdateValue(String name, Object value, Type type) + { + Contract.Assert(null != name, "[SerializationInfo.UpdateValue]name!=null"); + Contract.Assert(null != value, "[SerializationInfo.UpdateValue]value!=null"); + Contract.Assert(null != (object)type, "[SerializationInfo.UpdateValue]type!=null"); + + int index = FindElement(name); + if (index < 0) + { + AddValueInternal(name, value, type); + } + else + { + m_data[index] = value; + m_types[index] = type; + } + + } + + private int FindElement(String name) + { + if (null == name) + { + throw new ArgumentNullException("name"); + } + Contract.EndContractBlock(); + BCLDebug.Trace("SER", "[SerializationInfo.FindElement]Looking for ", name, " CurrMember is: ", m_currMember); + int index; + if (m_nameToIndex.TryGetValue(name, out index)) + { + return index; + } + return -1; + } + + /*==================================GetElement================================== + **Action: Use FindElement to get the location of a particular member and then return + ** the value of the element at that location. The type of the member is + ** returned in the foundType field. + **Returns: The value of the element at the position associated with name. + **Arguments: name -- the name of the element to find. + ** foundType -- the type of the element associated with the given name. + **Exceptions: None. FindElement does null checking and throws for elements not + ** found. + ==============================================================================*/ + private Object GetElement(String name, out Type foundType) + { + int index = FindElement(name); + if (index == -1) + { + throw new SerializationException(Environment.GetResourceString("Serialization_NotFound", name)); + } + + Contract.Assert(index < m_data.Length, "[SerializationInfo.GetElement]index<m_data.Length"); + Contract.Assert(index < m_types.Length, "[SerializationInfo.GetElement]index<m_types.Length"); + + foundType = m_types[index]; + Contract.Assert((object)foundType != null, "[SerializationInfo.GetElement]foundType!=null"); + return m_data[index]; + } + + [System.Runtime.InteropServices.ComVisible(true)] + private Object GetElementNoThrow(String name, out Type foundType) + { + int index = FindElement(name); + if (index == -1) + { + foundType = null; + return null; + } + + Contract.Assert(index < m_data.Length, "[SerializationInfo.GetElement]index<m_data.Length"); + Contract.Assert(index < m_types.Length, "[SerializationInfo.GetElement]index<m_types.Length"); + + foundType = m_types[index]; + Contract.Assert((object)foundType != null, "[SerializationInfo.GetElement]foundType!=null"); + return m_data[index]; + } + + // + // The user should call one of these getters to get the data back in the + // form requested. + // + + [System.Security.SecuritySafeCritical] // auto-generated + public Object GetValue(String name, Type type) + { + + if ((object)type == null) + { + throw new ArgumentNullException("type"); + } + Contract.EndContractBlock(); + + RuntimeType rt = type as RuntimeType; + if (rt == null) + throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType")); + + Type foundType; + Object value; + + value = GetElement(name, out foundType); +#if FEATURE_REMOTING + if (RemotingServices.IsTransparentProxy(value)) + { + RealProxy proxy = RemotingServices.GetRealProxy(value); + if (RemotingServices.ProxyCheckCast(proxy, rt)) + return value; + } + else +#endif + if (Object.ReferenceEquals(foundType, type) || type.IsAssignableFrom(foundType) || value == null) + { + return value; + } + + Contract.Assert(m_converter != null, "[SerializationInfo.GetValue]m_converter!=null"); + + return m_converter.Convert(value, type); + } + + [System.Security.SecuritySafeCritical] // auto-generated + [System.Runtime.InteropServices.ComVisible(true)] + internal Object GetValueNoThrow(String name, Type type) + { + Type foundType; + Object value; + + Contract.Assert((object)type != null, "[SerializationInfo.GetValue]type ==null"); + Contract.Assert(type is RuntimeType, "[SerializationInfo.GetValue]type is not a runtime type"); + + value = GetElementNoThrow(name, out foundType); + if (value == null) + return null; +#if FEATURE_REMOTING + if (RemotingServices.IsTransparentProxy(value)) + { + RealProxy proxy = RemotingServices.GetRealProxy(value); + if (RemotingServices.ProxyCheckCast(proxy, (RuntimeType)type)) + return value; + } + else +#endif + if (Object.ReferenceEquals(foundType, type) || type.IsAssignableFrom(foundType) || value == null) + { + return value; + } + + Contract.Assert(m_converter != null, "[SerializationInfo.GetValue]m_converter!=null"); + + return m_converter.Convert(value, type); + } + + public bool GetBoolean(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(bool))) + { + return (bool)value; + } + return m_converter.ToBoolean(value); + } + + public char GetChar(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(char))) + { + return (char)value; + } + return m_converter.ToChar(value); + } + + [CLSCompliant(false)] + public sbyte GetSByte(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(sbyte))) + { + return (sbyte)value; + } + return m_converter.ToSByte(value); + } + + public byte GetByte(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(byte))) + { + return (byte)value; + } + return m_converter.ToByte(value); + } + + public short GetInt16(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(short))) + { + return (short)value; + } + return m_converter.ToInt16(value); + } + + [CLSCompliant(false)] + public ushort GetUInt16(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(ushort))) + { + return (ushort)value; + } + return m_converter.ToUInt16(value); + } + + public int GetInt32(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(int))) + { + return (int)value; + } + return m_converter.ToInt32(value); + } + + [CLSCompliant(false)] + public uint GetUInt32(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(uint))) + { + return (uint)value; + } + return m_converter.ToUInt32(value); + } + + public long GetInt64(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(long))) + { + return (long)value; + } + return m_converter.ToInt64(value); + } + + [CLSCompliant(false)] + public ulong GetUInt64(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(ulong))) + { + return (ulong)value; + } + return m_converter.ToUInt64(value); + } + + public float GetSingle(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(float))) + { + return (float)value; + } + return m_converter.ToSingle(value); + } + + + public double GetDouble(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(double))) + { + return (double)value; + } + return m_converter.ToDouble(value); + } + + public decimal GetDecimal(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(decimal))) + { + return (decimal)value; + } + return m_converter.ToDecimal(value); + } + + public DateTime GetDateTime(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(DateTime))) + { + return (DateTime)value; + } + return m_converter.ToDateTime(value); + } + + public String GetString(String name) + { + Type foundType; + Object value; + + value = GetElement(name, out foundType); + if (Object.ReferenceEquals(foundType, typeof(String)) || value == null) + { + return (String)value; + } + return m_converter.ToString(value); + } + + internal string[] MemberNames + { + get + { + return m_members; + } + + } + + internal object[] MemberValues + { + get + { + return m_data; + } + } + + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/SerializationInfoEnumerator.cs b/src/mscorlib/src/System/Runtime/Serialization/SerializationInfoEnumerator.cs new file mode 100644 index 0000000000..6b256a6e1f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/SerializationInfoEnumerator.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: A formatter-friendly mechanism for walking all of +** the data in a SerializationInfo. Follows the IEnumerator +** mechanism from Collections. +** +** +============================================================*/ +namespace System.Runtime.Serialization { + using System; + using System.Collections; + using System.Diagnostics.Contracts; + + // + // The tuple returned by SerializationInfoEnumerator.Current. + // +[System.Runtime.InteropServices.ComVisible(true)] + public struct SerializationEntry { + private Type m_type; + private Object m_value; + private String m_name; + + public Object Value { + get { + return m_value; + } + } + + public String Name { + get { + return m_name; + } + } + + public Type ObjectType { + get { + return m_type; + } + } + + internal SerializationEntry(String entryName, Object entryValue, Type entryType) { + m_value = entryValue; + m_name = entryName; + m_type = entryType; + } + } + + // + // A simple enumerator over the values stored in the SerializationInfo. + // This does not snapshot the values, it just keeps pointers to the + // member variables of the SerializationInfo that created it. + // +[System.Runtime.InteropServices.ComVisible(true)] + public sealed class SerializationInfoEnumerator : IEnumerator { + String[] m_members; + Object[] m_data; + Type[] m_types; + int m_numItems; + int m_currItem; + bool m_current; + + internal SerializationInfoEnumerator(String[] members, Object[] info, Type[] types, int numItems) { + Contract.Assert(members!=null, "[SerializationInfoEnumerator.ctor]members!=null"); + Contract.Assert(info!=null, "[SerializationInfoEnumerator.ctor]info!=null"); + Contract.Assert(types!=null, "[SerializationInfoEnumerator.ctor]types!=null"); + Contract.Assert(numItems>=0, "[SerializationInfoEnumerator.ctor]numItems>=0"); + Contract.Assert(members.Length>=numItems, "[SerializationInfoEnumerator.ctor]members.Length>=numItems"); + Contract.Assert(info.Length>=numItems, "[SerializationInfoEnumerator.ctor]info.Length>=numItems"); + Contract.Assert(types.Length>=numItems, "[SerializationInfoEnumerator.ctor]types.Length>=numItems"); + + m_members = members; + m_data = info; + m_types = types; + //The MoveNext semantic is much easier if we enforce that [0..m_numItems] are valid entries + //in the enumerator, hence we subtract 1. + m_numItems = numItems-1; + m_currItem = -1; + m_current = false; + } + + public bool MoveNext() { + if (m_currItem<m_numItems) { + m_currItem++; + m_current = true; + } else { + m_current = false; + } + return m_current; + } + + /// <internalonly/> + Object IEnumerator.Current { //Actually returns a SerializationEntry + get { + if (m_current==false) { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen")); + } + return (Object)(new SerializationEntry(m_members[m_currItem], m_data[m_currItem], m_types[m_currItem])); + } + } + + public SerializationEntry Current { //Actually returns a SerializationEntry + get { + if (m_current==false) { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen")); + } + return (new SerializationEntry(m_members[m_currItem], m_data[m_currItem], m_types[m_currItem])); + } + } + + public void Reset() { + m_currItem = -1; + m_current = false; + } + + public String Name { + get { + if (m_current==false) { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen")); + } + return m_members[m_currItem]; + } + } + public Object Value { + get { + if (m_current==false) { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen")); + } + return m_data[m_currItem]; + } + } + public Type ObjectType { + get { + if (m_current==false) { + throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumOpCantHappen")); + } + return m_types[m_currItem]; + } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Serialization/StreamingContext.cs b/src/mscorlib/src/System/Runtime/Serialization/StreamingContext.cs new file mode 100644 index 0000000000..ef4a096a51 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Serialization/StreamingContext.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** ValueType: StreamingContext +** +** +** Purpose: A value type indicating the source or destination of our streaming. +** +** +===========================================================*/ +namespace System.Runtime.Serialization { + + using System.Runtime.Remoting; + using System; + [Serializable] +[System.Runtime.InteropServices.ComVisible(true)] + public struct StreamingContext { + internal Object m_additionalContext; + internal StreamingContextStates m_state; + + public StreamingContext(StreamingContextStates state) + : this (state, null) { + } + + public StreamingContext(StreamingContextStates state, Object additional) { + m_state = state; + m_additionalContext = additional; + } + + public Object Context { + get { return m_additionalContext; } + } + + public override bool Equals(Object obj) { + if (!(obj is StreamingContext)) { + return false; + } + if (((StreamingContext)obj).m_additionalContext == m_additionalContext && + ((StreamingContext)obj).m_state == m_state) { + return true; + } + return false; + } + + public override int GetHashCode() { + return (int)m_state; + } + + public StreamingContextStates State { + get { return m_state; } + } + } + + // ********************************************************** + // Keep these in sync with the version in vm\runtimehandles.h + // ********************************************************** +[Serializable] +[Flags] +[System.Runtime.InteropServices.ComVisible(true)] + public enum StreamingContextStates { + CrossProcess=0x01, + CrossMachine=0x02, + File =0x04, + Persistence =0x08, + Remoting =0x10, + Other =0x20, + Clone =0x40, + CrossAppDomain =0x80, + All =0xFF, + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs b/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs new file mode 100644 index 0000000000..99e30b5488 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs @@ -0,0 +1,485 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** +** Purpose: This class is used to determine which binary compatibility +** behaviors are enabled at runtime. A type for +** tracking which target Framework an app was built against, or an +** appdomain-wide setting from the host telling us which .NET +** Framework version we should emulate. +** +** +===========================================================*/ +using System; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Runtime.Versioning +{ + // Provides a simple way to test whether an application was built against specific .NET Framework + // flavors and versions, with the intent of allowing Framework developers to mimic behavior of older + // Framework releases. This allows us to make behavioral breaking changes in a binary compatible way, + // for an application. This works at the per-AppDomain level, not process nor per-Assembly. + // + // To opt into newer behavior, applications must specify a TargetFrameworkAttribute on their assembly + // saying what version they targeted, or a host must set this when creating an AppDomain. Note + // that command line apps don't have this attribute! + // + // To use this class: + // Developers need to figure out whether they're working on the phone, desktop, or Silverlight, and + // what version they are introducing a breaking change in. Pick one predicate below, and use that + // to decide whether to run the new or old behavior. Example: + // + // if (BinaryCompatibility.TargetsAtLeast_Phone_V7_1) { + // // new behavior for phone 7.1 and other releases where we will integrate this change, like .NET Framework 4.5 + // } + // else { + // // Legacy behavior + // } + // + // If you are making a breaking change in one specific branch that won't be integrated normally to + // all other branches (ie, say you're making breaking changes to Windows Phone 8 after .NET Framework v4.5 + // has locked down for release), then add in specific predicates for each relevant platform. + // + // Maintainers of this class: + // Revisit the table once per release, perhaps at the end of the last coding milestone, to verify a + // default policy saying whether all quirks from a particular flavor & release should be enabled in + // other releases (ie, should all Windows Phone 8.0 quirks be enabled in .NET Framework v5)? + // + // History: + // Here is the order in which releases were made along with some basic integration information. The idea + // is to track what set of compatibility features are present in each other. + // While we cannot guarantee this list is perfectly linear (ie, a feature could be implemented in the last + // few weeks before shipping and make it into only one of three concommittent releases due to triaging), + // this is a good high level summary of code flow. + // + // Desktop Silverlight Windows Phone + // .NET Framework 3.0 -> Silverlight 2 + // .NET Framework 3.5 + // Silverlight 3 + // Silverlight 4 + // .NET Framework 4 Phone 8.0 + // .NET Framework 4.5 Phone 8.1 + // .NET Framework 4.5.1 Phone 8.1 + // + // (Note: Windows Phone 7.0 was built using the .NET Compact Framework, which forked around v1 or v1.1) + // + // Compatibility Policy decisions: + // If we cannot determine that an app was built for a newer .NET Framework (ie, the app has no + // TargetFrameworkAttribute), then quirks will be enabled to emulate older behavior. + // As such, your test code should define the TargetFrameworkAttribute (which VS does for you) + // if you want to see the new behavior! + [FriendAccessAllowed] + internal static class BinaryCompatibility + { + // Use this for new behavior introduced in the phone branch. It will do the right thing for desktop & SL. + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Phone_V7_1 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Phone_V7_1; } } + + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Phone_V8_0 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Phone_V8_0; } } + + // Use this for new behavior introduced in the Desktop branch. It will do the right thing for Phone & SL. + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_1 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_1; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_2 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_2; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_3 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_3; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_4 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_4; } } + + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V5_0 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V5_0; } } + + // Use this for new behavior introduced in the Silverlight branch. It will do the right thing for desktop & Phone. + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Silverlight_V4 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Silverlight_V4; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Silverlight_V5 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Silverlight_V5; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Silverlight_V6 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Silverlight_V6; } } + + [FriendAccessAllowed] + internal static TargetFrameworkId AppWasBuiltForFramework { + [FriendAccessAllowed] + get { + Contract.Ensures(Contract.Result<TargetFrameworkId>() > TargetFrameworkId.NotYetChecked); + + if (s_AppWasBuiltForFramework == TargetFrameworkId.NotYetChecked) + ReadTargetFrameworkId(); + + return s_AppWasBuiltForFramework; + } + } + + // Version number is major * 10000 + minor * 100 + build (ie, 4.5.1.0 would be version 40501). + [FriendAccessAllowed] + internal static int AppWasBuiltForVersion { + [FriendAccessAllowed] + get { + Contract.Ensures(Contract.Result<int>() > 0 || s_AppWasBuiltForFramework == TargetFrameworkId.Unspecified); + + if (s_AppWasBuiltForFramework == TargetFrameworkId.NotYetChecked) + ReadTargetFrameworkId(); + + Contract.Assert(s_AppWasBuiltForFramework != TargetFrameworkId.Unrecognized); + + return s_AppWasBuiltForVersion; + } + } + + #region private + private static TargetFrameworkId s_AppWasBuiltForFramework; + // Version number is major * 10000 + minor * 100 + build (ie, 4.5.1.0 would be version 40501). + private static int s_AppWasBuiltForVersion; + + readonly static BinaryCompatibilityMap s_map = new BinaryCompatibilityMap(); + + // For parsing a target Framework moniker, from the FrameworkName class + private const char c_componentSeparator = ','; + private const char c_keyValueSeparator = '='; + private const char c_versionValuePrefix = 'v'; + private const String c_versionKey = "Version"; + private const String c_profileKey = "Profile"; + + /// <summary> + /// BinaryCompatibilityMap is basically a bitvector. There is a boolean field for each of the + /// properties in BinaryCompatibility + /// </summary> + private sealed class BinaryCompatibilityMap + { + // A bit for each property + internal bool TargetsAtLeast_Phone_V7_1; + internal bool TargetsAtLeast_Phone_V8_0; + internal bool TargetsAtLeast_Phone_V8_1; + internal bool TargetsAtLeast_Desktop_V4_5; + internal bool TargetsAtLeast_Desktop_V4_5_1; + internal bool TargetsAtLeast_Desktop_V4_5_2; + internal bool TargetsAtLeast_Desktop_V4_5_3; + internal bool TargetsAtLeast_Desktop_V4_5_4; + internal bool TargetsAtLeast_Desktop_V5_0; + internal bool TargetsAtLeast_Silverlight_V4; + internal bool TargetsAtLeast_Silverlight_V5; + internal bool TargetsAtLeast_Silverlight_V6; + + internal BinaryCompatibilityMap() + { + AddQuirksForFramework(AppWasBuiltForFramework, AppWasBuiltForVersion); + } + + // The purpose of this method is to capture information about integrations & behavioral compatibility + // between our multiple different release vehicles. IE, if a behavior shows up in Silverlight version 5, + // does it show up in the .NET Framework version 4.5 and Windows Phone 8? + // Version number is major * 10000 + minor * 100 + build (ie, 4.5.1.0 would be version 40501). + private void AddQuirksForFramework(TargetFrameworkId builtAgainstFramework, int buildAgainstVersion) + { + Contract.Requires(buildAgainstVersion > 0 || builtAgainstFramework == TargetFrameworkId.Unspecified); + + switch (builtAgainstFramework) + { + case TargetFrameworkId.NetFramework: + case TargetFrameworkId.NetCore: // Treat Windows 8 tailored apps as normal desktop apps - same product + if (buildAgainstVersion >= 50000) + TargetsAtLeast_Desktop_V5_0 = true; + + // Potential 4.5 servicing releases + if (buildAgainstVersion >= 40504) + TargetsAtLeast_Desktop_V4_5_4 = true; + if (buildAgainstVersion >= 40503) + TargetsAtLeast_Desktop_V4_5_3 = true; + if (buildAgainstVersion >= 40502) + TargetsAtLeast_Desktop_V4_5_2 = true; + if (buildAgainstVersion >= 40501) + TargetsAtLeast_Desktop_V4_5_1 = true; + + if (buildAgainstVersion >= 40500) + { + TargetsAtLeast_Desktop_V4_5 = true; + // On XX/XX/XX we integrated all changes from the phone V7_1 into the branch from which contains Desktop V4_5, thus + // Any application built for V4_5 (or above) should have all the quirks for Phone V7_1 turned on. + AddQuirksForFramework(TargetFrameworkId.Phone, 70100); + // All Silverlight 5 behavior should be in the .NET Framework version 4.5 + AddQuirksForFramework(TargetFrameworkId.Silverlight, 50000); + } + break; + + case TargetFrameworkId.Phone: + if (buildAgainstVersion >= 80000) + { + // This is for Apollo apps. For Apollo apps we don't want to enable any of the 4.5 or 4.5.1 quirks + TargetsAtLeast_Phone_V8_0 = true; + //TargetsAtLeast_Desktop_V4_5 = true; + } + if (buildAgainstVersion >= 80100) + { + // For WindowsPhone 8.1 and SL 8.1 scenarios we want to enable both 4.5 and 4.5.1 quirks. + TargetsAtLeast_Desktop_V4_5 = true; + TargetsAtLeast_Desktop_V4_5_1 = true; + } + + if (buildAgainstVersion >= 710) + TargetsAtLeast_Phone_V7_1 = true; + break; + + case TargetFrameworkId.Silverlight: + if (buildAgainstVersion >= 40000) + TargetsAtLeast_Silverlight_V4 = true; + + if (buildAgainstVersion >= 50000) + TargetsAtLeast_Silverlight_V5 = true; + + if (buildAgainstVersion >= 60000) + { + TargetsAtLeast_Silverlight_V6 = true; + } + break; + + case TargetFrameworkId.Unspecified: + break; + + case TargetFrameworkId.NotYetChecked: + case TargetFrameworkId.Unrecognized: + Contract.Assert(false, "Bad framework kind"); + break; + default: + Contract.Assert(false, "Error: we introduced a new Target Framework but did not update our binary compatibility map"); + break; + } + } + } + + #region String Parsing + + // If this doesn't work, perhaps we could fall back to parsing the metadata version number. + private static bool ParseTargetFrameworkMonikerIntoEnum(String targetFrameworkMoniker, out TargetFrameworkId targetFramework, out int targetFrameworkVersion) + { + Contract.Requires(!String.IsNullOrEmpty(targetFrameworkMoniker)); + + targetFramework = TargetFrameworkId.NotYetChecked; + targetFrameworkVersion = 0; + + String identifier = null; + String profile = null; + ParseFrameworkName(targetFrameworkMoniker, out identifier, out targetFrameworkVersion, out profile); + + switch (identifier) + { + case ".NETFramework": + targetFramework = TargetFrameworkId.NetFramework; + break; + + case ".NETPortable": + targetFramework = TargetFrameworkId.Portable; + break; + + case ".NETCore": + targetFramework = TargetFrameworkId.NetCore; + break; + + case "WindowsPhone": + if (targetFrameworkVersion >= 80100) + { + // A TFM of the form WindowsPhone,Version=v8.1 corresponds to SL 8.1 scenario + // and gets the same quirks as WindowsPhoneApp\v8.1 store apps. + targetFramework = TargetFrameworkId.Phone; + } + else + { + // There is no TFM for Apollo or below and hence we assign the targetFramework to Unspecified. + targetFramework = TargetFrameworkId.Unspecified; + } + break; + + case "WindowsPhoneApp": + targetFramework = TargetFrameworkId.Phone; + break; + + case "Silverlight": + targetFramework = TargetFrameworkId.Silverlight; + // Windows Phone 7 is Silverlight,Version=v4.0,Profile=WindowsPhone + // Windows Phone 7.1 is Silverlight,Version=v4.0,Profile=WindowsPhone71 + if (!String.IsNullOrEmpty(profile)) + { + if (profile == "WindowsPhone") + { + targetFramework = TargetFrameworkId.Phone; + targetFrameworkVersion = 70000; + } + else if (profile == "WindowsPhone71") + { + targetFramework = TargetFrameworkId.Phone; + targetFrameworkVersion = 70100; + } + else if (profile == "WindowsPhone8") + { + targetFramework = TargetFrameworkId.Phone; + targetFrameworkVersion = 80000; + } + else if (profile.StartsWith("WindowsPhone", StringComparison.Ordinal)) + { + Contract.Assert(false, "This is a phone app, but we can't tell what version this is!"); + targetFramework = TargetFrameworkId.Unrecognized; + targetFrameworkVersion = 70100; + } + else + { + Contract.Assert(false, String.Format(CultureInfo.InvariantCulture, "Unrecognized Silverlight profile \"{0}\". What is this, an XBox app?", profile)); + targetFramework = TargetFrameworkId.Unrecognized; + } + } + break; + + default: + Contract.Assert(false, String.Format(CultureInfo.InvariantCulture, "Unrecognized Target Framework Moniker in our Binary Compatibility class. Framework name: \"{0}\"", targetFrameworkMoniker)); + targetFramework = TargetFrameworkId.Unrecognized; + break; + } + + return true; + } + + // This code was a constructor copied from the FrameworkName class, which is located in System.dll. + // Parses strings in the following format: "<identifier>, Version=[v|V]<version>, Profile=<profile>" + // - The identifier and version is required, profile is optional + // - Only three components are allowed. + // - The version string must be in the System.Version format; an optional "v" or "V" prefix is allowed + private static void ParseFrameworkName(String frameworkName, out String identifier, out int version, out String profile) + { + if (frameworkName == null) + { + throw new ArgumentNullException("frameworkName"); + } + if (frameworkName.Length == 0) + { + throw new ArgumentException(Environment.GetResourceString("Argument_StringZeroLength"), "frameworkName"); + } + Contract.EndContractBlock(); + + String[] components = frameworkName.Split(c_componentSeparator); + version = 0; + + // Identifer and Version are required, Profile is optional. + if (components.Length < 2 || components.Length > 3) + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameTooShort"), "frameworkName"); + } + + // + // 1) Parse the "Identifier", which must come first. Trim any whitespace + // + identifier = components[0].Trim(); + + if (identifier.Length == 0) + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameInvalid"), "frameworkName"); + } + + bool versionFound = false; + profile = null; + + // + // The required "Version" and optional "Profile" component can be in any order + // + for (int i = 1; i < components.Length; i++) + { + // Get the key/value pair separated by '=' + string[] keyValuePair = components[i].Split(c_keyValueSeparator); + + if (keyValuePair.Length != 2) + { + throw new ArgumentException(Environment.GetResourceString("SR.Argument_FrameworkNameInvalid"), "frameworkName"); + } + + // Get the key and value, trimming any whitespace + string key = keyValuePair[0].Trim(); + string value = keyValuePair[1].Trim(); + + // + // 2) Parse the required "Version" key value + // + if (key.Equals(c_versionKey, StringComparison.OrdinalIgnoreCase)) + { + versionFound = true; + + // Allow the version to include a 'v' or 'V' prefix... + if (value.Length > 0 && (value[0] == c_versionValuePrefix || value[0] == 'V')) + { + value = value.Substring(1); + } + Version realVersion = new Version(value); + // The version class will represent some unset values as -1 internally (instead of 0). + version = realVersion.Major * 10000; + if (realVersion.Minor > 0) + version += realVersion.Minor * 100; + if (realVersion.Build > 0) + version += realVersion.Build; + } + // + // 3) Parse the optional "Profile" key value + // + else if (key.Equals(c_profileKey, StringComparison.OrdinalIgnoreCase)) + { + if (!String.IsNullOrEmpty(value)) + { + profile = value; + } + } + else + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameInvalid"), "frameworkName"); + } + } + + if (!versionFound) + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameMissingVersion"), "frameworkName"); + } + } + + [System.Security.SecuritySafeCritical] + private static void ReadTargetFrameworkId() + { + String targetFrameworkName = AppDomain.CurrentDomain.GetTargetFrameworkName(); + + var overrideValue = System.Runtime.Versioning.CompatibilitySwitch.GetValueInternal("TargetFrameworkMoniker"); + if (!string.IsNullOrEmpty(overrideValue)) + { + targetFrameworkName = overrideValue; + } + + // Write to a local then to _targetFramework, after writing the version number. + TargetFrameworkId fxId; + int fxVersion = 0; + if (targetFrameworkName == null) + { +#if FEATURE_CORECLR + // if we don't have a value for targetFrameworkName we need to figure out if we should give the newest behavior or not. + if (CompatibilitySwitches.UseLatestBehaviorWhenTFMNotSpecified) + { + fxId = TargetFrameworkId.NetFramework; + fxVersion = 50000; // We are going to default to the latest value for version that we have in our code. + } + else +#endif + fxId = TargetFrameworkId.Unspecified; + } + else if (!ParseTargetFrameworkMonikerIntoEnum(targetFrameworkName, out fxId, out fxVersion)) + fxId = TargetFrameworkId.Unrecognized; + + s_AppWasBuiltForFramework = fxId; + s_AppWasBuiltForVersion = fxVersion; + } + #endregion String Parsing + + #endregion private + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/CompatibilitySwitch.cs b/src/mscorlib/src/System/Runtime/Versioning/CompatibilitySwitch.cs new file mode 100644 index 0000000000..b06c42437c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/CompatibilitySwitch.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Runtime.Versioning +{ + public static class CompatibilitySwitch + { + /* This class contains 3 sets of api: + * 1. internal apis : These apis are supposed to be used by mscorlib.dll and other assemblies which use the <runtime> section in config + * These apis query for the value of quirk not only in windows quirk DB but also in runtime section of config files, + * registry and environment vars. + * 2. public apis : These apis are supposed to be used by FX assemblies which do not read the runtime section of config files and have + * have their own section in config files or do not use configs at all. + * + * 3. specialized apis: These apis are defined in order to retrieve a specific value defined in CLR Config. That value can have specific look-up rules + * for the order and location of the config sources used. + * + * These apis are for internal use only for FX assmeblies. It has not been decided if they can be used by OOB components due to EULA restrictions + */ + [System.Security.SecurityCritical] + public static bool IsEnabled(string compatibilitySwitchName) + { + return IsEnabledInternalCall(compatibilitySwitchName, true); + } + + [System.Security.SecurityCritical] + public static string GetValue(string compatibilitySwitchName) + { + // This is used by AppContext.TryGetSwitch to check switch overrides in the Windows Quirk DB + // If this method is updated to check other locations than the DB, please ensure compat with + // the AppContext class. + return GetValueInternalCall(compatibilitySwitchName, true); + } + + [System.Security.SecurityCritical] + internal static bool IsEnabledInternal(string compatibilitySwitchName) + { + return IsEnabledInternalCall(compatibilitySwitchName, false); + } + + [System.Security.SecurityCritical] + internal static string GetValueInternal(string compatibilitySwitchName) + { + return GetValueInternalCall(compatibilitySwitchName, false); + } + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern string GetAppContextOverridesInternalCall(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool IsEnabledInternalCall(string compatibilitySwitchName, bool onlyDB); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string GetValueInternalCall(string compatibilitySwitchName, bool onlyDB); + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/ComponentGuaranteesAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/ComponentGuaranteesAttribute.cs new file mode 100644 index 0000000000..0f906d518a --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/ComponentGuaranteesAttribute.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Tracking whether a component signs up for a +** a strong contract spanning multiple versions. +** +===========================================================*/ +using System; + +namespace System.Runtime.Versioning { + + [Flags] + [Serializable] + public enum ComponentGuaranteesOptions + { + None = 0, + Exchange = 0x1, + Stable = 0x2, + SideBySide = 0x4, + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | + AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | + AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | + AttributeTargets.Constructor | AttributeTargets.Event, + AllowMultiple = false, Inherited = false)] + public sealed class ComponentGuaranteesAttribute : Attribute { + private ComponentGuaranteesOptions _guarantees; + + public ComponentGuaranteesAttribute(ComponentGuaranteesOptions guarantees) + { + _guarantees = guarantees; + } + + public ComponentGuaranteesOptions Guarantees { + get { return _guarantees; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs new file mode 100644 index 0000000000..0a9845d9c2 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Class: NonVersionableAttribute +** +** +** The [NonVersionable] attribute is applied to indicate that the implementation +** of a particular member or layout of a struct cannot be changed for given platform in incompatible way. +** This allows cross-module inlining of methods and data structures whose implementation +** is never changed in ReadyToRun native images. Any changes to such members or types would be +** breaking changes for ReadyToRun. +** +===========================================================*/ +using System; +using System.Diagnostics; + +namespace System.Runtime.Versioning { + + // This Conditional is here to strip the annotations for targets where ReadyToRun is not supported. + // If this attribute is ever made public, this Conditional should be removed. + [Conditional("FEATURE_READYTORUN")] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, + AllowMultiple = false, Inherited = false)] + sealed class NonVersionableAttribute : Attribute { + + public NonVersionableAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/ResourceAttributes.cs b/src/mscorlib/src/System/Runtime/Versioning/ResourceAttributes.cs new file mode 100644 index 0000000000..78a9ddbd07 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/ResourceAttributes.cs @@ -0,0 +1,237 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Purpose: Resource annotation rules. +** +===========================================================*/ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.Win32; +using System.Diagnostics.Contracts; + +namespace System.Runtime.Versioning +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + [Conditional("RESOURCE_ANNOTATION_WORK")] + public sealed class ResourceConsumptionAttribute : Attribute + { + private ResourceScope _consumptionScope; + private ResourceScope _resourceScope; + + public ResourceConsumptionAttribute(ResourceScope resourceScope) + { + _resourceScope = resourceScope; + _consumptionScope = _resourceScope; + } + + public ResourceConsumptionAttribute(ResourceScope resourceScope, ResourceScope consumptionScope) + { + _resourceScope = resourceScope; + _consumptionScope = consumptionScope; + } + + public ResourceScope ResourceScope { + get { return _resourceScope; } + } + + public ResourceScope ConsumptionScope { + get { return _consumptionScope; } + } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + [Conditional("RESOURCE_ANNOTATION_WORK")] + public sealed class ResourceExposureAttribute : Attribute + { + private ResourceScope _resourceExposureLevel; + + public ResourceExposureAttribute(ResourceScope exposureLevel) + { + _resourceExposureLevel = exposureLevel; + } + + public ResourceScope ResourceExposureLevel { + get { return _resourceExposureLevel; } + } + } + + + // Default visibility is Public, which isn't specified in this enum. + // Public == the lack of Private or Assembly + // Does this actually work? Need to investigate that. + [Flags] + public enum ResourceScope + { + None = 0, + // Resource type + Machine = 0x1, + Process = 0x2, + AppDomain = 0x4, + Library = 0x8, + // Visibility + Private = 0x10, // Private to this one class. + Assembly = 0x20, // Assembly-level, like C#'s "internal" + } + + + [Flags] + internal enum SxSRequirements + { + None = 0, + AppDomainID = 0x1, + ProcessID = 0x2, + CLRInstanceID = 0x4, // for multiple CLR's within the process + AssemblyName = 0x8, + TypeName = 0x10 + } + + public static class VersioningHelper + { + // These depend on the exact values given to members of the ResourceScope enum. + private const ResourceScope ResTypeMask = ResourceScope.Machine | ResourceScope.Process | ResourceScope.AppDomain | ResourceScope.Library; + private const ResourceScope VisibilityMask = ResourceScope.Private | ResourceScope.Assembly; + + [System.Security.SecuritySafeCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int GetRuntimeId(); + + public static String MakeVersionSafeName(String name, ResourceScope from, ResourceScope to) + { + return MakeVersionSafeName(name, from, to, null); + } + + [System.Security.SecuritySafeCritical] // auto-generated + public static String MakeVersionSafeName(String name, ResourceScope from, ResourceScope to, Type type) + { + ResourceScope fromResType = from & ResTypeMask; + ResourceScope toResType = to & ResTypeMask; + if (fromResType > toResType) + throw new ArgumentException(Environment.GetResourceString("Argument_ResourceScopeWrongDirection", fromResType, toResType), "from"); + + SxSRequirements requires = GetRequirements(to, from); + + if ((requires & (SxSRequirements.AssemblyName | SxSRequirements.TypeName)) != 0 && type == null) + throw new ArgumentNullException("type", Environment.GetResourceString("ArgumentNull_TypeRequiredByResourceScope")); + + // Add in process ID, CLR base address, and appdomain ID's. Also, use a character identifier + // to ensure that these can never accidentally overlap (ie, you create enough appdomains and your + // appdomain ID might equal your process ID). + StringBuilder safeName = new StringBuilder(name); + char separator = '_'; + if ((requires & SxSRequirements.ProcessID) != 0) { + safeName.Append(separator); + safeName.Append('p'); + safeName.Append(Win32Native.GetCurrentProcessId()); + } + if ((requires & SxSRequirements.CLRInstanceID) != 0) { + String clrID = GetCLRInstanceString(); + safeName.Append(separator); + safeName.Append('r'); + safeName.Append(clrID); + } + if ((requires & SxSRequirements.AppDomainID) != 0) { + safeName.Append(separator); + safeName.Append("ad"); + safeName.Append(AppDomain.CurrentDomain.Id); + } + if ((requires & SxSRequirements.TypeName) != 0) { + safeName.Append(separator); + safeName.Append(type.Name); + } + if ((requires & SxSRequirements.AssemblyName) != 0) { + safeName.Append(separator); + safeName.Append(type.Assembly.FullName); + } + return safeName.ToString(); + } + + private static String GetCLRInstanceString() + { + int id = GetRuntimeId(); + return id.ToString(CultureInfo.InvariantCulture); + } + + private static SxSRequirements GetRequirements(ResourceScope consumeAsScope, ResourceScope calleeScope) + { + SxSRequirements requires = SxSRequirements.None; + + switch(calleeScope & ResTypeMask) { + case ResourceScope.Machine: + switch(consumeAsScope & ResTypeMask) { + case ResourceScope.Machine: + // No work + break; + + case ResourceScope.Process: + requires |= SxSRequirements.ProcessID; + break; + + case ResourceScope.AppDomain: + requires |= SxSRequirements.AppDomainID | SxSRequirements.CLRInstanceID | SxSRequirements.ProcessID; + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeTypeBits", consumeAsScope), "consumeAsScope"); + } + break; + + case ResourceScope.Process: + if ((consumeAsScope & ResourceScope.AppDomain) != 0) + requires |= SxSRequirements.AppDomainID | SxSRequirements.CLRInstanceID; + break; + + case ResourceScope.AppDomain: + // No work + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeTypeBits", calleeScope), "calleeScope"); + } + + switch(calleeScope & VisibilityMask) { + case ResourceScope.None: // Public - implied + switch(consumeAsScope & VisibilityMask) { + case ResourceScope.None: // Public - implied + // No work + break; + + case ResourceScope.Assembly: + requires |= SxSRequirements.AssemblyName; + break; + + case ResourceScope.Private: + requires |= SxSRequirements.TypeName | SxSRequirements.AssemblyName; + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeVisibilityBits", consumeAsScope), "consumeAsScope"); + } + break; + + case ResourceScope.Assembly: + if ((consumeAsScope & ResourceScope.Private) != 0) + requires |= SxSRequirements.TypeName; + break; + + case ResourceScope.Private: + // No work + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeVisibilityBits", calleeScope), "calleeScope"); + } + + if (consumeAsScope == calleeScope) { + Contract.Assert(requires == SxSRequirements.None, "Computed a strange set of required resource scoping. It's probably wrong."); + } + + return requires; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkAttribute.cs new file mode 100644 index 0000000000..dbf98c08d8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkAttribute.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +/*============================================================ +** +** +** +** Purpose: Identifies which SKU and version of the .NET +** Framework that a particular library was compiled against. +** Emitted by VS, and can help catch deployment problems. +** +===========================================================*/ +using System; +using System.Diagnostics.Contracts; + +namespace System.Runtime.Versioning { + + [AttributeUsageAttribute(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class TargetFrameworkAttribute : Attribute { + private String _frameworkName; // A target framework moniker + private String _frameworkDisplayName; + + // The frameworkName parameter is intended to be the string form of a FrameworkName instance. + public TargetFrameworkAttribute(String frameworkName) + { + if (frameworkName == null) + throw new ArgumentNullException("frameworkName"); + Contract.EndContractBlock(); + _frameworkName = frameworkName; + } + + // The target framework moniker that this assembly was compiled against. + // Use the FrameworkName class to interpret target framework monikers. + public String FrameworkName { + get { return _frameworkName; } + } + + public String FrameworkDisplayName { + get { return _frameworkDisplayName; } + set { _frameworkDisplayName = value; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkId.cs b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkId.cs new file mode 100644 index 0000000000..0a5e668a92 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkId.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Purpose: Describe the target framework of the application or AppDomain. +** +===========================================================*/ +using System; +using System.Runtime.CompilerServices; + +namespace System.Runtime.Versioning +{ + // What type of .NET Framework was this application compiled against? + [FriendAccessAllowed] + internal enum TargetFrameworkId + { + NotYetChecked = 0, + Unrecognized = 1, // Unknown type, such as a new SKU (like watches or cars) + Unspecified = 2, // The TargetFrameworkAttribute was created in v4.0. And apps compiled outside VS will not have this attribute. + NetFramework = 3, // Desktop - Client or Server or ServerCore. + Portable = 4, // Portable Library v1 Note: We do not expect people to build executables against portable libraries! + NetCore = 5, // .NET Core = Windows 8 Immersive and Portable Library v2+ + Silverlight = 6, // Silverlight but not the Phone + Phone = 7, // Windows Phone 7 or higher + } +} |