Edit

Write to the Output window

Write status, diagnostic, and informational messages to a custom channel in the Visual Studio Output window.

Note

This guide uses the VisualStudio.Extensibility SDK. See Choose the right extensibility model for model comparison.

Full example

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace OutputWindowSample;

using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;
using Microsoft.VisualStudio.Extensibility.Documents;

/// <summary>
/// A sample command for displaying a test message in the output window.
/// </summary>
[VisualStudioContribution]
public class TestOutputWindowCommand : Command
{
    private OutputChannel? outputChannel;

    /// <inheritdoc />
    public override CommandConfiguration CommandConfiguration => new("%OutputWindowSample.TestOutputWindowCommand.DisplayName%")
    {
        Placements = [CommandPlacement.KnownPlacements.ToolsMenu],
        Icon = new(ImageMoniker.KnownValues.ToolWindow, IconSettings.IconAndText),
    };

    /// <inheritdoc />
    public override async Task InitializeAsync(CancellationToken cancellationToken)
    {
        this.outputChannel = await this.Extensibility.Views().Output.CreateOutputChannelAsync(Strings.OutputWindowDisplayName, cancellationToken);
        await base.InitializeAsync(cancellationToken);
    }

    /// <inheritdoc />
    public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
    {
        if (this.outputChannel != null)
        {
            await this.outputChannel.WriteLineAsync("This is a test of the output window.");
        }
    }
}

Tip

See the full working sample: OutputWindowSample ↗

Related concepts: Contributions and configurations — how output window channels are registered

Understand the example

Create a channel and write to it

[VisualStudioContribution]
internal class MyCommand : Command
{
    private OutputChannel? outputChannel;

    public override async Task InitializeAsync(CancellationToken ct)
    {
        this.outputChannel = await this.Extensibility.Views().Output
            .CreateOutputChannelAsync("My Extension", ct);
    }

    public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken ct)
    {
        if (this.outputChannel != null)
        {
            await this.outputChannel.Writer.WriteLineAsync("Operation started...");
            // ... do work ...
            await this.outputChannel.Writer.WriteLineAsync("Operation complete.");
        }
    }
}

Writing options

The OutputChannel provides three ways to write:

Method Use when
outputChannel.Writer.WriteLineAsync() Standard text output (most common)
outputChannel.WriteLineAsync() Direct channel write (convenience)
outputChannel.PipeWriter.WriteAsync() High-performance binary/bulk output

The Writer property is a standard System.IO.TextWriter ↗ that supports Write(), WriteAsync(), WriteLine(), and WriteLineAsync().

For high-performance scenarios, use the PipeWriter property (System.IO.Pipelines.PipeWriter ↗):

var bytes = new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes("Binary output message"));
await this.outputChannel.PipeWriter.WriteAsync(bytes, cancellationToken);

Tips

✅ DO

  • Call CreateOutputChannelAsync() once during initialization (e.g., InitializeAsync)
  • Store the OutputChannel as a field for reuse across methods
  • Use descriptive channel names that match your extension name

❌ DON'T

  • Don't call CreateOutputChannelAsync() multiple times with the same name — it throws an exception
  • Don't assume the Output window is visible — users must open it via View > Output
  • Don't write high-frequency messages (floods the UI and hurts performance)

🔧 Troubleshooting

Symptom Fix
Exception on channel creation You called CreateOutputChannelAsync twice with same name; create once in InitializeAsync
Messages not visible User must select your channel from the "Show output from" dropdown
Null reference on channel Ensure InitializeAsync completed before using the channel

Next steps