3
\$\begingroup\$

This is a fairly straight forward question. I have a C# WPF MVVM application that call a C++ DLL to do some compilation/crunching. To update the UI C# application for progress updates I am using callback delegates.

public static class Callbacks
{
    public static ConsoleCallback ConsoleOutputCallback { get; set; }

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void ConsoleCallback(string message);
}

where the implementation is

Callbacks.ConsoleOutputCallback = (message) => 
    {
        console.Document.Text += message;
    };

where Document is of type TextDocument. I am then passing this object into the C++ to be able to invoke it and update the UI. This works great, however, for one control I am getting a standard

A first chance exception of type 'System.InvalidOperationException' ... TextDocument can be accessed only from the thread that owns it.

Now, I understand this error. To get around it I have done the following

TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Callbacks.ConsoleOutputCallback = (message) => 
    {
        Task.Factory.StartNew(() =>
        {
            console.Document.Text += message;
        }, CancellationToken.None,
           TaskCreationOptions.None,
           scheduler);
    };

which works. However, it would be nicer to look at if I could do a similar thing that I could do with a Control (which TextDocument Document is not) like

public static void PerformSafely<T>(this Control target, Action<T> action, T parameter)
{
    if (target.InvokeRequired)
        target.Invoke(action, parameter);
    else
        action(parameter);
}

but I cannot do this with TextDocument as it does not inherit from Control. Is there a nicer way to do what I am doing?

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

The TPL is unfortunately verbose at times. The only way I can think of to make it look "nicer" is to point ConsoleOutputCallback to a named method and delegate the creation of the Task to another method:

public static void MyMethod()
{
    Callbacks.ConsoleOutputCallback = ConsoleOutputCallback;
}

public static void ConsoleOutputCallback(string message)
{
    var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

    Dispatch(
        msg => console.Document.Text += msg, 
        message, 
        scheduler
    );

    Dispatch(
        msg => someOtherNonControl.Text += msg,
        message,
        scheduler
    );
}

public static void Dispatch<T>(Action<T> action, T parameter, TaskScheduler scheduler)
{
    Task.Factory.StartNew(
        () => action(parameter), 
        CancellationToken.None, 
        TaskCreationOptions.None, 
        scheduler
    );
}
\$\endgroup\$
1
  • \$\begingroup\$ Nice George, I like it... \$\endgroup\$
    – MoonKnight
    Jun 26, 2014 at 13:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.