Compartilhar via


Fornecer um serviço assíncrono do Visual Studio

Se desejar obter um serviço sem bloquear o thread da interface do usuário, você deverá criar um serviço assíncrono e carregar o pacote em um thread em segundo plano. Para isso, você pode usar um em vez de um AsyncPackage Package, e adicionar o serviço com os métodos assíncronos especiais do pacote assíncrono.

Para obter informações sobre como fornecer serviços síncronos do Visual Studio, consulte Como: Fornecer um serviço.

Implementar um serviço assíncrono

  1. Crie um projeto VSIX (File>New>Project Visual C#>Extensiblity>VSIX Project).> Nomeie o projeto TestAsync.

  2. Adicione um VSPackage ao projeto. Selecione o nó do projeto no Gerenciador de Soluções e clique em Adicionar>Novo item>Visual C# Items>Extensibility>Visual Studio Package. Nomeie este arquivo TestAsyncPackage.cs.

  3. Em TestAsyncPackage.cs, altere o pacote para herdar de em vez PackagedeAsyncPackage:

    public sealed class TestAsyncPackage : AsyncPackage
    
  4. Para implementar um serviço, você precisa criar três tipos:

    • Uma interface que identifica o serviço. Muitas dessas interfaces estão vazias, ou seja, não possuem métodos, pois são usadas apenas para consultar o serviço.

    • Uma interface que descreve a interface de serviço. Essa interface inclui os métodos a serem implementados.

    • Uma classe que implementa o serviço e a interface de serviço.

  5. O exemplo a seguir mostra uma implementação muito básica dos três tipos. O construtor da classe de serviço deve definir o provedor de serviços. Neste exemplo, vamos apenas adicionar o serviço ao arquivo de código do pacote.

  6. Adicione as seguintes diretivas de uso ao arquivo de pacote:

    using System.Threading;
    using System.Threading.Tasks;
    using System.Runtime.CompilerServices;
    using System.IO;
    using Microsoft.VisualStudio.Threading;
    using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider;
    using Task = System.Threading.Tasks.Task;
    
  7. Aqui está a implementação do serviço assíncrono. Observe que você precisa definir o provedor de serviços assíncrono em vez do provedor de serviços síncronos no construtor:

    public class TextWriterService : STextWriterService, ITextWriterService
    {
        private IAsyncServiceProvider asyncServiceProvider;
    
        public TextWriterService(IAsyncServiceProvider provider)
        {
            // constructor should only be used for simple initialization
            // any usage of Visual Studio service, expensive background operations should happen in the
            // asynchronous InitializeAsync method for best performance
            asyncServiceProvider = provider;
        }
    
        public async Task InitializeAsync(CancellationToken cancellationToken)
        {
            await TaskScheduler.Default;
            // do background operations that involve IO or other async methods
    
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
            // query Visual Studio services on main thread unless they are documented as free threaded explicitly.
            // The reason for this is the final cast to service interface (such as IVsShell) may involve COM operations to add/release references.
    
            IVsShell vsShell = this.asyncServiceProvider.GetServiceAsync(typeof(SVsShell)) as IVsShell;
            // use Visual Studio services to continue initialization
        }
    
        public async Task WriteLineAsync(string path, string line)
        {
            StreamWriter writer = new StreamWriter(path);
            await writer.WriteLineAsync(line);
            writer.Close();
        }
    }
    
    public interface STextWriterService
    {
    }
    
    public interface ITextWriterService
    {
        System.Threading.Tasks.Task WriteLineAsync(string path, string line);
    }
    

Cadastrar um serviço

Para registrar um serviço, adicione o ao pacote que fornece o ProvideServiceAttribute serviço. Diferente de registrar um serviço síncrono, você precisa garantir que o pacote e o serviço ofereçam suporte ao carregamento assíncrono:

  • Você deve adicionar o campo AllowsBackgroundLoading = true ao para garantir que o pacote possa ser inicializado de forma assíncrona Para obter mais informações sobre o PackageRegistrationAttribute, consulte Registrar e cancelar o PackageRegistrationAttribute registro de VSPackages.

  • Você deve adicionar o campo IsAsyncQueryable = true ao para garantir que a ProvideServiceAttribute instância de serviço possa ser inicializada de forma assíncrona.

    Aqui está um exemplo de um com um AsyncPackage registro de serviço assíncrono:

[ProvideService((typeof(STextWriterService)), IsAsyncQueryable = true)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(TestAsyncPackage.PackageGuidString)]
public sealed class TestAsyncPackage : AsyncPackage
{. . . }

Adicionar um serviço

  1. Em TestAsyncPackage.cs, remova o método e substitua o Initialize() InitializeAsync() método. Adicione o serviço e adicione um método de retorno de chamada para criar os serviços. Aqui está um exemplo do inicializador assíncrono adicionando um serviço:

    protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await base.InitializeAsync(cancellationToken, progress);
        this.AddService(typeof(STextWriterService), CreateTextWriterService);
    }
    
    

    Para tornar esse serviço visível fora deste pacote, defina o valor do sinalizador de promoção como true como o último parâmetro:this.AddService(typeof(STextWriterService), CreateTextWriterService, true);

  2. Adicione uma referência a Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll.

  3. Implemente o método de retorno de chamada como um método assíncrono que cria e retorna o serviço.

    public async Task<object> CreateTextWriterService(IAsyncServiceContainer container, CancellationToken cancellationToken, Type serviceType)
    {
        TextWriterService service = new TextWriterService(this);
        await service.InitializeAsync(cancellationToken);
        return service;
    }
    
    

Usar um serviço

Agora você pode obter o serviço e usar seus métodos.

  1. Mostraremos isso no inicializador, mas você pode obter o serviço em qualquer lugar que queira usar o serviço.

    protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await base.InitializeAsync(cancellationToken, progress);
        this.AddService(typeof(STextWriterService), CreateTextWriterService);
    
        ITextWriterService textService = await this.GetServiceAsync(typeof(STextWriterService)) as ITextWriterService;
        string userpath = @"C:\MyDir\MyFile.txt";
        await textService.WriteLineAsync(userpath, "this is a test");
    }
    
    

    Não se esqueça de mudar userpath para um nome de arquivo e caminho que faça sentido em sua máquina!

  2. Compile e execute o código. Quando a instância experimental do Visual Studio for exibida, abra uma solução. Isso faz com que o AsyncPackage para carregar automaticamente. Quando o inicializador for executado, você deverá localizar um arquivo no local especificado.

Usar um serviço assíncrono em um manipulador de comandos

Aqui está um exemplo de como usar um serviço assíncrono em um comando de menu. Você pode usar o procedimento mostrado aqui para usar o serviço em outros métodos não assíncronos.

  1. Adicione um comando de menu ao seu projeto. (No Gerenciador de Soluções, selecione o nó do projeto, clique com o botão direito do mouse e selecione Adicionar>Novo Comando Personalizado de Extensibilidade>de Item.)> Nomeie o arquivo de comando TestAsyncCommand.cs.

  2. O modelo de comando personalizado adiciona novamente o método ao arquivo TestAsyncPackage.cs para inicializar o Initialize() comando. No método, copie Initialize() a linha que inicializa o comando. Ele deverá ser parecido com:

    TestAsyncCommand.Initialize(this);
    

    Mova essa linha para o InitializeAsync() método no arquivo AsyncPackageForService.cs Como isso está em uma inicialização assíncrona, você deve alternar para o thread principal antes de inicializar o comando usando SwitchToMainThreadAsync. Agora, ele deve ser assim:

    
    protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
    {
        await base.InitializeAsync(cancellationToken, progress);
        this.AddService(typeof(STextWriterService), CreateTextWriterService);
    
        ITextWriterService textService =
           await this.GetServiceAsync(typeof(STextWriterService)) as ITextWriterService;
    
        string userpath = @"C:\MyDir\MyFile.txt";
        await textService.WriteLineAsync(userpath, "this is a test");
    
        await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
        TestAsyncCommand.Initialize(this);
    }
    
    
  3. Exclua o Initialize() método.

  4. No arquivo TestAsyncCommand.cs , localize o MenuItemCallback() método. Exclua o corpo do método.

  5. Adicione uma diretiva de uso:

    using System.IO;
    
  6. Adicione um método assíncrono chamado UseTextWriterAsync(), que obtém o serviço e usa seus métodos:

    private async System.Threading.Tasks.Task UseTextWriterAsync()
    {
        // Query text writer service asynchronously to avoid a blocking call.
        ITextWriterService textService =
           await AsyncServiceProvider.GlobalProvider.GetServiceAsync(typeof(STextWriterService))
              as ITextWriterService;
    
        string userpath = @"C:\MyDir\MyFile.txt";
        await textService.WriteLineAsync(userpath, "this is a test");
       }
    
    
  7. Chame este método do MenuItemCallback() método:

    private void MenuItemCallback(object sender, EventArgs e)
    {
        UseTextWriterAsync();
    }
    
    
  8. Compile a solução e inicie a depuração. Quando a instância experimental do Visual Studio for exibida, vá para o menu Ferramentas e procure o item de menu Invoke TestAsyncCommand. Quando você clica nele, o TextWriterService grava no arquivo especificado. (Você não precisa abrir uma solução, porque invocar o comando também faz com que o pacote seja carregado.)