2

I'm getting started with Visual Studio Extensions and would like to write status messages to the output window during certain milestones of my task to report it's progress.

I'm using a command to trigger the task from a context menu. Here is a mocked up example of it's Execute method:

private void Execute(object sender, EventArgs e)
{
    ThreadHelper.ThrowIfNotOnUIThread();

    var paneGuid = Guid.NewGuid();

    var outputWindow = (IVsOutputWindow)Package.GetGlobalService(typeof(SVsOutputWindow));
    outputWindow.CreatePane(ref paneGuid, "My Window", 1, 1);
    outputWindow.GetPane(ref paneGuid, out var outputPane);

    outputPane.Activate();

    outputPane.OutputString("Starting...\n");

    Thread.Sleep(2000);

    outputPane.OutputString("1...\n");

    Thread.Sleep(2000);

    outputPane.OutputString("2...\n");

    Thread.Sleep(2000);

    outputPane.OutputString("3...\n");

    Thread.Sleep(2000);

    outputPane.OutputString("Finished");
}

Rather than showing the output as it is written, the output window is shown after the Execute method has finished.

How do I get it to show straight away and to write messages periodically?

UPDATE: SOLUTION

As suggested below by JHBonarius the solution is to move the logic into an async task and to call the UI thread to write to the output window as required:

private void Execute(object sender, EventArgs e)
{
    ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
    {
        var paneGuid = Guid.NewGuid();

        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        var outputWindow = (IVsOutputWindow)Package.GetGlobalService(typeof(SVsOutputWindow));
        outputWindow.CreatePane(ref paneGuid, "My Window", 1, 1);
        outputWindow.GetPane(ref paneGuid, out var outputPane);
        outputPane.Activate();

        outputPane.OutputString("Starting...\n");
        await TaskScheduler.Default;

        // Call to some async task
        await Task.Delay(2000);

        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        outputPane.OutputString("1...\n");
        await TaskScheduler.Default;

        // Call to some async task
        await Task.Delay(2000);

        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        outputPane.OutputString("2...\n");
        await TaskScheduler.Default;

        // Call to some async task
        await Task.Delay(2000);

        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        outputPane.OutputString("3...\n");
        await TaskScheduler.Default;

        // Call to some async task
        await Task.Delay(2000);

        await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
        outputPane.OutputString("Finished");
        await TaskScheduler.Default;
    });
}

Thanks, Jay

JayJayson
  • 23
  • 3
  • You could look into simplifying this using `await Task.Delay(2000);` instead of thread sleep. – JHBonarius Mar 24 '21 at 13:00
  • Thanks, that's an interesting alternative, although in this case Thread.Sleep(2000); was just being used as a placeholder to replicate the behaviour. Those will be replaced with various awaitable async tasks. – JayJayson Mar 24 '21 at 17:25

1 Answers1

0

You are on the UI thread and don't return control (but instead lock the thread with sleep). Thus the UI cannot render. You might want to rewrite this to separate methods or an async method.

JHBonarius
  • 10,824
  • 3
  • 22
  • 41