File: src\VisualStudio\IntegrationTest\Harness\XUnitShared\InProcess\InProcComponent.cs
Web Access
Project: src\src\VisualStudio\IntegrationTest\Harness\XUnit\Microsoft.VisualStudio.Extensibility.Testing.Xunit.csproj (Microsoft.VisualStudio.Extensibility.Testing.Xunit)
// 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 Xunit.InProcess
{
    using System;
    using System.Threading;
    using System.Windows;
    using System.Windows.Threading;
    using Microsoft.VisualStudio.Shell.Interop;
    using Xunit.Harness;
    using DTE = EnvDTE.DTE;
 
    /// <summary>
    /// Base class for all components that run inside of the Visual Studio process.
    /// <list type="bullet">
    /// <item>Every in-proc component should provide a public, static, parameterless "Create" method.
    /// This will be called to construct the component in the VS process.</item>
    /// <item>Public methods on in-proc components should be instance methods to ensure that they are
    /// marshalled properly and execute in the VS process. Static methods will execute in the process
    /// in which they are called.</item>
    /// </list>
    /// </summary>
    internal abstract class InProcComponent : MarshalByRefObject
    {
        protected InProcComponent()
        {
        }
 
        private static Dispatcher CurrentApplicationDispatcher
            => Application.Current.Dispatcher;
 
        protected static void BeginInvokeOnUIThread(Action action)
#pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs
            => CurrentApplicationDispatcher.BeginInvoke(action, DispatcherPriority.Background);
#pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs
 
        protected static void InvokeOnUIThread(Action action)
        {
            if (CurrentApplicationDispatcher.CheckAccess())
            {
                // Invoke the action directly
                action();
            }
            else
            {
#pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs
                CurrentApplicationDispatcher.Invoke(action, DispatcherPriority.Background);
#pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs
            }
        }
 
        protected static T InvokeOnUIThread<T>(Func<T> action)
        {
            if (CurrentApplicationDispatcher.CheckAccess())
            {
                // Invoke the action directly
                return action();
            }
            else
            {
#pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs
                return CurrentApplicationDispatcher.Invoke(action, DispatcherPriority.Background);
#pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs
            }
        }
 
        protected static TInterface GetGlobalService<TService, TInterface>()
            where TService : class
            where TInterface : class
            => InvokeOnUIThread(() => (TInterface)new OleServiceProvider(GetDTE()).GetService(typeof(TService)));
 
        protected static DTE GetDTE()
            => InvokeOnUIThread(() => (DTE)GlobalServiceProvider.ServiceProvider.GetService(typeof(SDTE)));
 
        protected static bool IsCommandAvailable(string commandName)
            => GetDTE().Commands.Item(commandName).IsAvailable;
 
        protected static void ExecuteCommand(string commandName, string args = "")
            => GetDTE().ExecuteCommand(commandName, args);
 
        /// <summary>
        /// Waiting for the application to 'idle' means that it is done pumping messages (including WM_PAINT).
        /// </summary>
        protected static void WaitForApplicationIdle()
#pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs
            => CurrentApplicationDispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
#pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs
 
        protected static void WaitForSystemIdle()
#pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs
            => CurrentApplicationDispatcher.Invoke(() => { }, DispatcherPriority.SystemIdle);
#pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs
 
        // Ensure InProcComponents live forever
        public override object? InitializeLifetimeService() => null;
    }
}