question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

TaskDialog.ShowDialog() doesn't install WindowsFormsSynchronizationContext

See original GitHub issue
  • .NET Core Version: 5.0.100-preview.5.20255.2

  • Have you experienced this same bug with .NET Framework?: No

Problem description:

Currently, TaskDialog.ShowDialog() doesn’t install the WindowsFormsSynchronizationContext, so when showing the task dialog from a thread that didn’t call a Control constructor and which currently isn’t running a message loop e.g. with Form.ShowDialog() or Application.Run(), continuations of async methods that were initiated from an task dialog event won’t run on the same thread used to show the task dialog.

Expected behavior:

I think TaskDialog.ShowDialog should install the WindowsFormsSynchronizationContext before actually showing the dialog if SynchronizationContext.Current isn’t an WindowsFormsSynchronizationContext, and in that case, should also uninstall it after the native TaskDialogIndirect returns.

Note: It seems MessageBox.Show() also doesn’t install the WindowsFormsSynchronizationContext, but there it’s probably not that important since yout normally don’t handle events within MessageBox.Show().

What do you think?

Minimal repro:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    var mainThread = Thread.CurrentThread;

    var myPage = new TaskDialogPage();
    myPage.Created += async (s, e) =>
    {
        Console.WriteLine("TaskDialog.ShowDialog():");
        Console.WriteLine("Is WindowsFormsSynchronizationContext: " +
            (SynchronizationContext.Current is WindowsFormsSynchronizationContext));

        await Task.Yield();

        Console.WriteLine("Is Main Thread: " + (Thread.CurrentThread == mainThread));
        Console.WriteLine();
        myPage.BoundDialog!.Close();
    };
    TaskDialog.ShowDialog(myPage);

    using var myForm = new Form();
    myForm.Load += async (s, e) =>
    {
        Console.WriteLine("Form.ShowDialog():");
        Console.WriteLine("Is WindowsFormsSynchronizationContext: " +
            (SynchronizationContext.Current is WindowsFormsSynchronizationContext));

        await Task.Yield();

        Console.WriteLine("Is Main Thread: " + (Thread.CurrentThread == mainThread));
        Console.WriteLine();
        myForm.Close();
    };
    myForm.ShowDialog();
}

Actual output:

TaskDialog.ShowDialog():
Is WindowsFormsSynchronizationContext: False
Is Main Thread: False

Form.ShowDialog():
Is WindowsFormsSynchronizationContext: True
Is Main Thread: True

Expected output:

TaskDialog.ShowDialog():
Is WindowsFormsSynchronizationContext: True
Is Main Thread: True

Form.ShowDialog():
Is WindowsFormsSynchronizationContext: True
Is Main Thread: True

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:18 (18 by maintainers)

github_iconTop GitHub Comments

1reaction
kpreissercommented, May 8, 2020

Is TaskDialog handle based or a COM API?

AFAIK, TaskDialog is not a COM API. It’s a regular function TaskDialogIndirect in comctl32.dll.

(The documentation says that TaskDialog requires the single-threaded apartment (STA) model, but I didn’t experience any problems yet when showing the task dialog from threads using the MTA model. When I call GetWindowThreadProcessId with the task dialog handle from an MTA thread, it returns the same value as GetCurrentThreadId.)

Thank you!

1reaction
weltkantecommented, May 6, 2020

I’ve thought about it and think I like option (2) from my previous post most currently. The TaskDialog constructor -not ShowDialog- should do the same work the Control constructor does, installing a WFSC by calling InstallIfNeeded (but not uninstalling it once its done, since Controls don’t do that either).

This strategy of treating it like a Control as far as WinForms initialization is concerned should not introduce any new behavior and keep the risk minimal:

  • creating Controls is not a promise to show them, you could dispose them without showing them; similar, creating a TaskDialog is not a promis to actually show it
  • creating Controls on a thread before the message loop is installed is normal, you run the Form constructor with all Controls before running the Application message loop or a top level Form.ShowDialog

this makes TaskDialog just another variant of the existing code paths.

It doesn’t solve the shutdown problems but I think those are out of scope of this issue, if someone is to approach WFSC shutdown to make it more async-friendly it needs to be its own issue and be done across whole WinForms and not just one usecase like here.

Office integration can be treated similar to MessageBox, since (I assume, didn’t check) the message loop of TaskDialog is win32 and not WinForms it might be hard to negotiate the IMsoComponent state.

Sorry if I’m ranting a bit, got surprised by this issue and I’ve not got the time to think everything through before I wrote my first posts.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Windows Task Dialog · Issue #146 · dotnet/winforms
Hi, On Windows Vista and higher, the Task Dialog is available that ... ShowDialog() doesn't install WindowsFormsSynchronizationContext #3222.
Read more >
Why does calling anotherForm.ShowDialog() cause ...
ShowDialog it causes a new message loop to be generated for it and a new WindowsFormsSynchronizationContext is installed for it only if no ......
Read more >
TaskDialog.ShowDialog Method (System.Windows.Forms)
Shows the task dialog with the specified owner. ShowDialog(TaskDialogPage, TaskDialogStartupLocation). Shows the task dialog. C#
Read more >
Pro WPF - Windows Presentation Foundation in .NET 3.0
won't run rich WPF applications very well, especially ones that incorporate ... Load() to the Window type and then call its Show() or...
Read more >
Pro WPF: Windows Presentation Foundation in .NET 3.0 ...
The problem is that the user interface in traditional Windows applications isn't ... The next step is to call ShowDialog() to show the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found