|
|
fjparisIII wrote: BackgroundWorker has 17 methods, 9 properties, and 4 events.
How many are inherited? I didn't derive from Component.
I didn't implement cancel ability...I'll add that next.
I plugged the class into an existing WPF app, replacing "BackgroundWorker" with
"MyWPFBackgroundWorker" and it worked fine.
But I just use the basic BackgroundWorker stuff - the 3 main events, and the
RunWorkerAsync() method(s).
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
fjparisIII wrote: there's probably just some feature you're using that I'm not aware of.
You're not missing anything.
I tried to demonstrate a simple BackgroundWorker-like class (I even used the same
event delegates, although I could have implemented my own) that runs the secondary
thread in STA state, that's it.
If you want/need to derive from BackgroundWorker and rewrite it all, go ahead.
If you want/need to implement every last bit of BackgroundWorker than feel free.
I implemented the features I commonly use.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
fjparisIII wrote: If you've got some canned code I can just pick up and shove into my app
that's what I tried to provide.
If you take the latest version I posted (below, the class called "WPFSTABackgroundWorker"),
add it to an app that uses BackgroundWorker, and replace any occurrence of "BackgroundWorker"
in your existing code with "WPFSTABackgroundWorker", it should work, in STA mode, unless you've
used some feature of BackgroundWorker that I didn't implement, which would be the Component class
stuff.
As for what I did -
I focused on the key convenience features of BackgroundWorker:
1) Handles starting the thread, providing a simple plug-in event handler for users
to provide the thread procedure code.
2) Provides progress update and thread end events on the UI thread.
3) Provides the ability to asynchronously cancel (in my second version)
My functional requirements:
1) I wanted it to replace BackgroundWorker and be source compatible with
BackgroundWorker, except for the class name.
2) I don't use any of the Component class features, so I saw no need to implement
that class like BackgroundWorker does.
A couple things that make my implementation not as good as BackgroundWorker
(I'm sure many more could be found):
1) Doesn't implement Component
2) I used the WPF Application class to raise events on the UI thread, so the
class is only usable in a WPF app.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
If an STA thread doesn't fix it:
Please post the exact exception message. Maybe the code that creates the
stream and calls Save() too (if the exception is still happening there)...
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Cancellation and other non-Component properties implemented...
public class WPFSTABackgroundWorker
{
private delegate void InvokerDelegate();
public event DoWorkEventHandler DoWork;
public event ProgressChangedEventHandler ProgressChanged;
public event RunWorkerCompletedEventHandler RunWorkerCompleted;
public bool WorkerReportsProgress { get; set; }
public bool IsBusy { get; protected set; }
public bool WorkerSupportsCancellation { get; set; }
private bool _cancellationPending;
public bool CancellationPending
{
get
{
bool ret;
lock (cancellock)
{
ret = _cancellationPending;
}
return ret;
}
protected set
{
lock (cancellock)
{
_cancellationPending = value;
}
}
}
public WPFSTABackgroundWorker()
{
IsBusy = false;
WorkerReportsProgress = false;
WorkerSupportsCancellation = false;
CancellationPending = false;
}
public void RunWorkerAsync()
{
RunWorkerAsync(null);
}
public void RunWorkerAsync(Object argument)
{
IsBusy = true;
threadargument = argument;
thread = new Thread(ThreadProc);
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Normal;
thread.Start(this);
}
public void CancelAsync()
{
if (!WorkerSupportsCancellation)
throw new InvalidOperationException("Attempted to CancelAsync when WorkerSupportsCancellation is false.");
CancellationPending = true;
}
public void ReportProgress(int percentProgress)
{
ReportProgress(percentProgress, null);
}
public void ReportProgress(int percentProgress, Object userState)
{
if (!WorkerReportsProgress)
throw new InvalidOperationException("Attempted to ReportProgress when WorkerReportsProgress is false.");
ProgressChangedEventHandler progresshandler = ProgressChanged;
if (progresshandler != null)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (InvokerDelegate)delegate
{
progresshandler(this, new ProgressChangedEventArgs(percentProgress, userState));
});
}
}
Thread thread;
Object threadargument;
static void ThreadProc(object context)
{
WPFSTABackgroundWorker worker = context as WPFSTABackgroundWorker;
DoWorkEventHandler workhandler = worker.DoWork;
if (workhandler != null)
{
workhandler(worker, new DoWorkEventArgs(worker.threadargument));
}
RunWorkerCompletedEventHandler completedhandler = worker.RunWorkerCompleted;
if (completedhandler != null)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (InvokerDelegate)delegate
{
completedhandler(worker, new RunWorkerCompletedEventArgs(null, null, worker.CancellationPending));
});
}
worker.IsBusy = false;
}
}
Mark Salsbery
Microsoft MVP - Visual C++
modified on Monday, June 1, 2009 8:23 PM
|
|
|
|
|
|
fjparisIII wrote: when I copy and paste your code into mine, it all comes out in one line. Do you know how I can get a copy of your code that is properly formatted?
Good question! I use Firefox and I haven't seen that happen.
Here's a copy of the file I am testing with[^]
Hope that works better!
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
|
|
Okay, I subscribed to the three events in C# code and ran the app and when it hit the BitmapEncoder.Save() call, my message box produced the following output:
It would probably fail on all images, but the first one it failed on is the following:
C:\temp\ImageScaling\SelectedImages\jpg\001.jpg
Reason: The image data generated an overflow during processing.
This is what I have also been getting with all versions of my code that I have tested with, including my own reimplementation of BackgroundWorker that I tested with yesterday. This time I took a breakpoint where I output this message so I could recover some details about the exception that my message box does not display:
HResult: -2146233066
innerException: {"Overflow or underflow in the arithmetic operation."}
message: The image data generated an overflow during processing.
So the overflow the Exception.Message property is talking about is an arithmetic overflow. So BitmapEncoder.Save() is probably getting an empty buffer or something with all zeroes in it and when it tries to encode the JPEG image data it generates an arithmetic overflow. So the real question is, why doesn't BitmapEncoder.Save() have the actual image data to work with?
I think this conclusively proves that whether BackgroundWorker runs as STA or MTA has nothing to do with my problem, what I expected all along.
modified on Sunday, June 7, 2009 1:15 PM
|
|
|
|
|
fjparisIII wrote: I think this conclusively proves that whether BackgroundWorker runs as STA or MTA has nothing to do with my problem
Right, but your very first post had this:
"The code that I have developed based on the above thread works 100%
of the time if it is executed inside of the WPF main application thread.
But it fails 100% of the time if it is executed inside of a worker thread.
The code fails on BitmapEncoder.Save(), throwing a "Cannot write to the
stream" exception. But I have to execute this code inside of a worker
thread because I have to display a progress bar: I'm updating the Copyright
metadata in a batch operation for thousands of images!"
That's what I was addressing...
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
There are a couple of posts you made I have to respond to. I'll do this one first because it's the easiest.
You were responding to the following quote from a previous post of mine:
Mark Salsbery wrote: your very first post had this:
"The code that I have developed based on the above thread works 100%
of the time if it is executed inside of the WPF main application thread.
But it fails 100% of the time if it is executed inside of a worker thread.
The code fails on BitmapEncoder.Save(), throwing a "Cannot write to the
stream" exception. But I have to execute this code inside of a worker
thread because I have to display a progress bar: I'm updating the Copyright
metadata in a batch operation for thousands of images!"
That's what I was addressing...
There are actually three places in my application where I call BitmapEncoder.Save() to capture image metadata that I need to add to an encoded image. One was already running in the main UI thread and was never a problem. The one I'm talking about in the above quote attempts to write "friendly" metadata tags in a batch operation with potentially thousands of files (e.g. the copyright notice). I solved that one with the "kludge" I talked about earlier, of pulling that small piece of code out of the background thread and executing it in the UI thread.
The one I'm still having problems with is a batch operation that scales images, where I have to transfer all of the metadata from the original image to the scaled image.
modified on Sunday, June 7, 2009 1:16 PM
|
|
|
|
|
fjparisIII wrote: So the real question is, why doesn't BitmapEncoder.Save() have the actual image data to work with?
That IS the real question
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
fjparisIII wrote: the next step of passing some source code to you via email and giving you my running application (which you'll be able to get from the Office Live site)
msalsbery at hotmail dot com
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
fjparisIII wrote: I just hope you don't run out of patience with me
Heh no problem - it's all a learning experience
fjparisIII wrote: how to do basic things like extending the namespace of XAML
Same as you did for the "cm" namespace in your sample above. For example
the code I posted was in the WPFTester namespace, so I use something like this:
xmlns:local="clr-namespace:WPFTester"
Note I didn't need to specify an assembly, because its in the same assembly as the XAML.
Anyway, I was under the impression from your previous posts that you were
already using a BackgroundWorker, and if so, you should have known what
RunWorkerAsync() does (your background thread wouldn't run without calling
it) and how to add a DoWork event handler, which is the code that runs on the
background thread.
I don't know how you do it from XAML - I use code, and it looks something like this:
<code>
WPFSTABackgroundWorker bw = new WPFSTABackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
...
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
<code>
WPFSTABackgroundWorker bw = sender as WPFSTABackgroundWorker;
for (int i = 0; i < 10; i++)
{
bw.ReportProgress(i * 10);
Thread.Sleep(500);
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
<code>
progrssBar.Value = e.ProgressPercentage;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
<code>
MyScaleButton.Content = "Thread completed!";
}
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
None of this is really relevant anyway - I did that class to fix the problem
you fixed with the kludge
There's no reason not to use BackgroundWorker if it works.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|
I'm here - I'm just saying since the potential MTA/STA thread problem in
your original post is no longer an issue, my thread class isn't going to
help you any - just use BackgroundWorker
fjparisIII wrote: I don't know how to get XAML to recognize my Page.Resources block when it is no longer BackgroundWorker.
Maybe a typo somewhere - I tried your XAML and it worked for me.
Here's a snippet (note I call mine "local" by convention)
<Window x:Class="WPFTester.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFTester"
Title="Window3" Background="SteelBlue" >
<Window.Resources>
<local:WPFSTABackgroundWorker
x:Key="backgroundWorker"
WorkerReportsProgress="True"
WorkerSupportsCancellation="True"
DoWork="BackgroundWorker_DoWork"
ProgressChanged="BackgroundWorker_ProgressChanged"
RunWorkerCompleted="BackgroundWorker_RunWorkerCompleted"
>
</local:WPFSTABackgroundWorker>
...
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
|