.NET BackgroundWorker: I don't understand how ReportProgress works - c#

I created a BackgroundWorker (mainBw) in my UI (WPF) thread. It has an infinite loop where it sleeps for 1.5 sec and calls a function via Application.Current.Dispatcher.Invoke which just outputs text from the "global" text variable to a TextBox.
Also before the loop it created another (child) BackgroundWorker which ReportsProgress, in ProgressChanged event handler it modifies the text variable.
I thought that it will not work because there is no anything like WinForms Application.DoEvents() in the mainBw loop so it can't process the event handler. But it works. Why?
Here is the code:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WpfApplication6
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private BackgroundWorker mainBw = new BackgroundWorker();
private void Button_Click(object sender, RoutedEventArgs e)
{
mainBw.DoWork += MainBwOnDoWork;
mainBw.RunWorkerAsync();
btn.IsEnabled = false;
}
private string text = "abc";
private void MainBwOnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += BwOnDoWork;
bw.ProgressChanged += BwOnProgressChanged;
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync();
while (true)
{
Thread.Sleep(1500);
text += " main ";
Application.Current.Dispatcher.Invoke(new Action(() => { WriteToUIThread(); }));
}
}
private void WriteToUIThread()
{
tbox.Text = DateTime.Now + " " + text + Environment.NewLine + tbox.Text;
}
private void BwOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
text += e.UserState.ToString();
}
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
while (true)
{
Thread.Sleep(3000);
(sender as BackgroundWorker).ReportProgress(0, "child");
}
}
}
}
// XAML
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Name="btn" Content="Button" HorizontalAlignment="Left" Height="105" Margin="43,47,0,0" VerticalAlignment="Top" Width="165" Click="Button_Click"/>
<TextBox Name="tbox" HorizontalAlignment="Left" Height="114" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" Margin="27,182,0,0"/>
</Grid>
</Window>

BackgroundWorker uses a universal way to get code to run on the UI thread, it uses the static SynchronizationContext.Current property to find a synchronization provider. ReportProgress() uses its Post() method to marshal the call.
If you run a Winforms app then the Current property will reference an instance of WindowsFormsSynchronizationContext class. Automatically installed when you create a Form or call Application.Run(). It uses Control.Begin/Invoke() to implement the Post and Send methods.
And if you run a WPF app then the Current property will reference an instance of DispatcherSynchronizationContext, it uses Dispatcher.Begin/Invoke().
So this just works automagically.

It works because the BackgroundWorker does work in a background thread (hence the name). Since it's not running in the UI thread, it's not blocking the UI thread, it is just sending short methods to be run in the UI thread every once in a while.
That said, it's still not a particularly well designed approach to solving the problem. If you want to run some code every 3 seconds just use a Timer instead. If you use the timer in the Forms namespace it will fire it's event in the UI thread on your behalf.

It works, because the BackgroundWorker runs - as its name says - in a background thread independently fromo the main UI thread.
The ReportProgress-event gets marshalled to the UI thread so that this event can easily be handled, without calling Invoke-methods on the controls involved.
Contraray, the Application.DoEvents()-method in WinForms allowed the process to handle other messages for perfoming long going operations in main thread, without using a background thread.

Related

WPF Progress Bar - Update Progress from ClickOnce Upgrade

I have been deploying updates for an application of mine with ClickOnce for a while. While I'm happy to be able to make improvements, I'm a little frustrated with the current progress bar. A little background - I have a XAML window class called "UpdateProgress" that I open when an update is being undertaken for the application. Here's the current code snippet I'm using right now, which does at least notify the user that progress is being made without freezing the application/crashing, but DOES NOT visually update the progress bar:
case UpdateStatuses.UpdateAvailable:
DialogResult dialogResult = System.Windows.Forms.MessageBox.Show("An update is available. Would you like to update the application now?", "Update available", MessageBoxButtons.OKCancel);
if (dialogResult.ToString() == "OK")
{
BackgroundWorker bgUpdate = new BackgroundWorker();
UpdateProgress updateNotify = new UpdateProgress();
bgUpdate.WorkerReportsProgress = true;
bgUpdate.DoWork += (uptSender, uptE) => { UpdateApplication();};
bgUpdate.ProgressChanged += (progSender, progE) => { updateNotify.updateProgress.Value = progE.ProgressPercentage; };
bgUpdate.RunWorkerCompleted += (comSender, comE) => {
updateNotify.Close();
applicationUpdated();
};
updateNotify.Show();
bgUpdate.RunWorkerAsync();
}
break;
Basically, I'm creating a background worker above, which runs the code below:
private static void UpdateApplication()
{
try
{
ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
//BackgroundWorker bgWorker = new BackgroundWorker();
//UpdateProgress updateNotify = new UpdateProgress();
//updateCheck.UpdateProgressChanged += (s, e) =>
//{
// updateNotify.updateProgress.Value = e.ProgressPercentage;
//};
//bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(UpdateComponent.noteUpdates);
//bgWorker.RunWorkerAsync();
//updateCheck.UpdateCompleted += (s, e) =>
//{
// updateNotify.Close();
// applicationUpdated();
//};
//updateNotify.Dispatcher.InvokeAsync(() =>
// {
//updateNotify.Show();
updateCheck.Update();
//});
//return null;
}
catch (DeploymentDownloadException dde)
{
System.Windows.MessageBox.Show("Cannot install the latest version of the application. Please check your network connection, or try again later. Error: " + dde);
//return null;
}
}
Quick explanation, currently I'm only creating an "ApplicationDeployment" instance called "updateCheck" and just having it run the update in this thread. What I've tried attempting before, is loading some of the commented code below, only to see the application crash when updating. Turns out, when debugging with a PROD instance of my application, it's due to the following error:
The calling thread cannot access this object because a different thread owns it.
Now, doing some digging, I've seen quite a few good reads about this. From what I understand, part of the problem is that I'm trying to run this code from a static class separated from my MainWindow and other UI classes. I'm doing this to try to keep my code clean and modular, but apparently, that comes with a price. I realize that one can bind the progress bar's progress percentage if it's in the code-behind of, say, the progress bar's window, but what if I'm trying to stick to running this in the static class I speak of instead? I've tried using things like the Dispatcher methods/BeginInvoke(), but unfortunately to end up with the same result.
Can someone give me the best suggestion on how to update the progress of my progress bar in a window with the percentage progress of an ApplicationDeployment instance's update routine?
Thanks a super ton in advance.
You're mis understanding the cause of your error. Any UI control should be updated from the thread that owns it.
First what you need to wrap is only the line of code that updates your progress bar.
Then you have two ways to wrap your call, either using IProgress<T> or Dispatcher. The former being quite cool as basically you're invoking an Action<T> and Progress<T> ensures to run it in the synchronization context it was instantiated, e.g. the UI thread. This is nice as basically you're abstracting things VS directly using the WPF Dispatcher.
Two really different approaches here, first is declared at caller then callee calls its Report method, second effectively wraps the call to UI in callee.
That's what you are executing during bgUpdate.ProgressChanged that needs to be taken care of.
And now if I were you I'd ditch BackgroundWorker in favor of Task since it's the preferred way to do that now, especially in WPF.
Smallest example using Task and IProgress:
Code:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<Button Content="DoWork" Click="Button1_Click" />
<ProgressBar Height="20" x:Name="ProgressBar1" Maximum="1.0"/>
</StackPanel>
</Window>
Code:
using System;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private async void Button1_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<double>(s => { ProgressBar1.Value = s; });
await Task.Run(() => DoWork(progress));
}
private static async Task DoWork(IProgress<double> progress = null)
{
const int count = 100;
for (var i = 0; i < count; i++)
{
await Task.Delay(50);
progress?.Report(1.0d / (count - 1) * i);
}
}
}
}
Now you're code doesn't even need to know about Dispatcher that is WPF-specific, code could be anywhere, update any framework.
You could also cancel the operation with Task.Run:
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=netframework-4.7.2

WPF, How do I force a textbox to update

I'm playing around with WPF. I am limited to the .Net framework 3.5. I want to update a text box with simple status text while I run some long method. No matter what I try, I cannot seem to get the text box to update until the long method has completed. I have tried threading / using the controls dispatcher etc. In the example below, I have reverted back to simply hiving off the long method to a thread but it still wont work. The TextStatus textbox never gets updated until after the long method (LoadDevices) has completed. Can someone tell me how to do this? Any help much appreciated.
private void UpdateButton_Click(object sender, RoutedEventArgs e)
{
UpdateStatus("Searching for devices, please wait . . .");
var t = new Thread(LoadDevices);
t.Start();
}
private void UpdateStatus(string status)
{
TextStatus.AppendText(status);
TextStatus.InvalidateVisual();
}
I think you are not providing enough code to figure out the problem. Still, fact is that your UI is blocked.
Try the following, maybe it helps you figure it out (not using Task since it's not available in .NET Framework 3.5). It tries to simulate your long running LoadDevices() method while keeping the UI responsive.
MainWindows.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="120"
Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Click="UpdateButtonClick" Grid.Row="0">Update</Button>
<TextBox Name="TextStatus" Text="" TextWrapping="Wrap" Grid.Row="1"></TextBox>
</Grid>
</Window>
MainWindows.xaml.cs
using System;
using System.Threading;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void UpdateButtonClick(object sender, RoutedEventArgs e)
{
UpdateStatus("Searching for devices, please wait");
var thread = new Thread(LoadDevices);
thread.Start();
}
private void LoadDevices()
{
// Your long running "load devices" implementation goes here
for (int i = 0; i < 15; i++)
{
Dispatcher.BeginInvoke((Action) (() => UpdateStatus(".")));
Thread.Sleep(250);
}
Dispatcher.BeginInvoke((Action)(() => UpdateStatus(" done")));
}
private void UpdateStatus(string status)
{
TextStatus.AppendText(status);
}
}
}
But yeah, you should prefer MVVM, Data binding, Commands, etc. and try to avoid stuffing logic into codebehind.
If you haven't tried delegates, that may be what you are looking for although it seems you may of tried this already. Inside your LoadDevices thread method, you could delegate back to invoke UpdateStatus with whatever text you want while your long method is running.
The other case I see with the wording of your question is something local to the method Update-status is trying to change the text by a call to it. However, it cannot for some reason.
This may be compleatly irrelevant to WPF, but in Forms:
I'm assuming you have your loading thread somewhere else. I don't see you calling a doWork() or equivalent method in the thread though. If you want to update the status as your loading thread loads devices you could do:
private delegate void UpdateStatusDel(string text); //This at your declarations
UpdateStatusHandler = new UpdateStatusDel(UpdateStatus); //To initialize the delegate to / //point to your update textbox function
//say you have
string updateText = "Loading 10% done";
//Then, in your thread you could invoke
[locationOfHandeler].Invoke(UpdateStatusHandler, new object[] { updateText });
Try using the Task library. You will need to download this for .NET 3.5 : http://www.microsoft.com/en-us/download/details.aspx?id=24940
Task task = new Task(new Action(LoadDevices));
task.Start();
There are several ways to do this:
http://dotnetcodr.com/2014/01/01/5-ways-to-start-a-task-in-net-c/

Working with Backgroundworker

I want to load a text file in WPF-RichTextbox using BackgroundWorker
Here is my code :
private delegate void update();
private void reader()
{
StreamReader str = new StreamReader("C:\\test.txt");
while (!str.EndOfStream)
{
richTextBox1.AppendText(str.ReadToEnd());
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progress wnd = new progress();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += delegate(object s, DoWorkEventArgs args)
{
update up = new update(reader);
richTextBox1.Dispatcher.Invoke(up);
};
bg.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
wnd.Close();
};
wnd.Show();
bg.RunWorkerAsync();
}
and for progress.xaml :
<Window x:Class="WpfApplication3.progress"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="progress" Height="192" Width="452" ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Grid>
<Label Content="loading ...." Height="28" HorizontalAlignment="Left" Margin="46,32,0,0" Name="label1" VerticalAlignment="Top" />
<ProgressBar Height="29" HorizontalAlignment="Left" Margin="46,78,0,0" Name="progressBar1" VerticalAlignment="Top" Width="325" IsIndeterminate="True" />
</Grid>
When I set wnd.showdialog() , it shows the indeterminate status of progress bar but the application does not load any text .
When I set wnd.show() , it loads the text but when loading , the progress bar freezes and
does not show indeterminate status.
Thanks for any help.
EDIT :
due to Servey and SLaks answers , i have updated the code but it has the same problem (it loads the text but when loading , the progress bar freezes and does not show indeterminate status.)
what is the problem? here is my updated code :
StringBuilder sb = new StringBuilder();
private void reader()
{
StreamReader str = new StreamReader("C:\\test.txt");
while (!str.EndOfStream)
{
sb.Append(str.ReadToEnd());
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progress wnd = new progress();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += delegate(object s, DoWorkEventArgs args)
{
reader();
};
bg.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
richTextBox1.AppendText(sb.ToString());
wnd.Close();
};
wnd.Show();
bg.RunWorkerAsync();
}
Ok I've had a look at your uploaded code and your problem is most probably with how you're doing the loads. As far as your loading goes, the following code does the job as you want it to:
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
Loaded -= OnLoaded;
var wnd = new ProgressWindow();
var dispatcher = wnd.Dispatcher;
Task.Factory.StartNew(() =>
{
using (var stream = new StreamReader("C:\\test.txt"))
return stream.ReadToEnd();
}).ContinueWith(x =>
{
//Main window dispatcher
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => richTextBox1.AppendText(x.Result)));
//progress window dispatcher
dispatcher.Invoke(DispatcherPriority.DataBind, new Action(wnd.Close));
});
wnd.ShowDialog();
}
While this works as intended, the freeze will happen regardless (i had a freeze of several seconds loading an 800kb text file). The problem is your use of RichTextBox. Judging by your code, you have no idea how wfp works and you're trying to work in a typical winform style.
RichTextBox in Wpf is very different from winforms RichTextBox. Similarly, a TextBlock is very different from a typical Label. If all you want is color and other formatting options, a TextBlock is very capable of doing that on its own and is far faster than building RichTextBox the way you're building.
If you must build RichTextBox from a text file, you need to learn about FlowDocument and how to create one from a text file yourself in order to avoid the freeze. The alternate to this solution will be running two UI threads (one for each window) and you're very likely to find that a lot more hassle than its worth.
Before attempting this sort of thing, you should sort your WPF basics out and learn the typical MVVM pattern before taking a dive into threaded WPF world. And again, avoid RTB in WPF if you can. The base controls in WPF are far more powerful than their winforms counterparts if you know how to use them.
You're not actually doing any work in the background. You're starting a background worker and the the only thing that the worker is doing in the background is scheduling an operation to run on the UI thread, and that operation is where you do all of your non-UI work. Since you're doing non-UI work on the UI thread, no other graphical operations can be performed until it finishes.
You'll want to read from the file in the BGW's do work event, store it in memory, and then in the Completed event, that runs in the UI thread, you can then place that data into the UI.

WPF Progressbar Stops after a Few Bars

In my WPF application i have to show a progressbar progress with in a timer tick event, which i am writing as below,
System.Windows.Forms.Timer timer;
public MainWindow()
{
timer = new System.Windows.Forms.Timer();
timer.Interval = 1000;
this.timer.Tick += new System.EventHandler(this.timer_Tick);
}
load event as below
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Value = DateTime.Now.Second;
progressBar1.Maximum = 700;
timer.Start();
}
And at last in tick event,
private void timer_Tick(object sender, EventArgs e)
{
Duration duration = new Duration(TimeSpan.FromSeconds(20));
//progress bar animation
System.Windows.Media.Animation.DoubleAnimation doubleanimation = new System.Windows.Media.Animation.DoubleAnimation(200.0, duration);
progressBar1.BeginAnimation(ProgressBar.ValueProperty, doubleanimation);
}
When the program's progressbar shows the progress for two-three bars and then it stops increment. Later there is no effect in the progress at all.
Why?
Since your ProgressBar doesn't relate to any particular behavior, it looks like a job for an indeterminate bar.
This other SO question provides some insight about it. In short, it's a XAML one-liner:
<!-- MinVal, MaxVal, Height needed for this to work -->
<ProgressBar x:Name="progressBar1" Margin="5" IsIndeterminate="True"
MinimumValue="0" MaximumValue="700" value="0" Height="20"/>
Then in code, you go like this:
progressBar1.IsIndeterminate = true; // start animation
progressBar1.IsIndeterminate = false; // stop animation
In my WPF application I have ... System.Windows.Forms.Timer timer;
That is the wrong type of timer. Use a DispatcherTimer instead.
When i execute my program progressbar shows the progress for two-three bars and then it stops
This surprises me, I wouldn't have expected it to work at all. This means you may have other problems too, like blocking the main (dispatcher) thread.
You are only setting the Value once, in the Loaded event:
progressBar1.Value = DateTime.Now.Second;
There is no change to progressBar1.Value in the Tick event. So it figures that it stops moving.
Use DispatcherTimer instead of Timer (Forms object), and use Value property of ProgressBar.
Try this:
MainWindows.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="55" Width="261">
<Grid>
<ProgressBar Name="pb" Maximum="60" />
</Grid>
</Window>
MainWindows.xaml.cs:
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
this.timer = new DispatcherTimer();
this.timer.Tick += timer_Tick;
this.timer.Interval = new System.TimeSpan(0, 0, 1);
this.timer.Start();
}
private void timer_Tick(object sender, System.EventArgs e)
{
this.pb.Value = System.DateTime.Now.Second % 100;
}
}
}
You can change the behaviour of the progress bar by changing the Value property (don't forget defining the Maximum property in the xaml).
I found this (WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI. link) to contain a great solution for my needs, albeit with a dialog box.
The one thing I found very useful was that the worker thread couldn't access the MainWindow's controls (in its own method). However, when using a delegate inside the main windows event handler, it was possible.
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
// Get a result from the asynchronous worker
T t = (t)args.Result
this.ExampleControl.Text = t.BlaBla;
};

C# threading issue

To play a bit with threading, delegates and backgroundworkers, I'm putting together a few small applications, I'm having a bit of trouble with one of them.
I've a Windows form, with a textbox, a button and a richttext.
When I press the button, the text in the textbox is used as a paramter to instantiate a class, like this:
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
backgroundWorker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.AppendText(response);
Application.DoEvents();
}
});
}).Start();
}
void OnUpdateTicker(string msg)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
label4.Text = msg;
Application.DoEvents();
});
}).Start();
}
}
When debugging I run into a 'textBox1.Lines' threw an exception of type 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'
Any tips on how to solve this problem?
First, there is no need to create new threads inside DoWork; the whole idea with the BackgroundWorker is that DoWork is executed on a separate thread. Second, since DoWork is executed on a separate thread and UI controls can be modified only on the UI thread, you need to invoke those updates correctly. So, a rewritten version of worker_DoWork could look like this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
}
}
Note how the code does not explicitly spawn any new threads, and also how the AppendText method call is done through a Control.Invoke call, forcing it to execute on the UI thread.
The main reason is that the textbox is not owned by the background thread.
Your UI thread owns all the UI objects, and you're spinning up a background thread when a button is pressed. That background thread should not have access to any UI objects.
If you want the value of the textbox to be used, you'll need to pass it to your background thread another way.
Have a look here for an explanation (and solution).
You can only update controls on the main thread from the main thread itself, unless you explicitly tell your program that it's ok to do, by using the .Invoke method of the control.
From: http://www.albahari.com/threading/part3.aspx
Control.Invoke
In a multi-threaded Windows Forms application, it's illegal to call a method or property on a control from any thread other than the one that created it. All cross-thread calls must be explicitly marshalled to the thread that created the control (usually the main thread), using the Control.Invoke or Control.BeginInvoke method. One cannot rely on automatic marshalling because it takes place too late – only when execution gets well into unmanaged code, by which time plenty of internal .NET code may already have run on the "wrong" thread – code which is not thread-safe.

Categories

Resources