I'm getting InvalidOperationException. This code is running in a thread. I've delegate it to the UI thread to handle the UI. Is the problem occuring because of my resources?
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/LiarDice;component/" + imagePath);
logo.EndInit();
currentGuessDice.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate()
{
currentGuessDice.Source = logo;
}));
I changed my code slightly a little and now the error changes to:
Part URI cannot end with a forward slash.
New Code:
currentGuessDice.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate()
{
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/LiarDice;component/" + imagePath);
logo.EndInit();
currentGuessDice.Source = logo;
}));
Besides that your imagePath variable my be null, the problem in your original code is that you create a BitmapImage in a thread other than the UI thread, and then use it in the UI thread.
BitmapImage is a DispatcherObject, which means that is has thread affinity. Since it is also a Freezable, you can freeze it in order to make it accessible to threads other than the one in which it was created:
var logo = new BitmapImage(
new Uri("pack://application:,,,/LiarDice;component/" + imagePath));
logo.Freeze(); // here
currentGuessDice.Dispatcher.Invoke(new Action(() => currentGuessDice.Source = logo));
Related
I am creating a download application in C# that downloads files from Amazon AWS S3 storage. I am able to download the file without issue, but I am trying to create a progress event.
To create the event I am using the following code within the download function:
Application.DoEvents();
response2.WriteObjectProgressEvent += displayProgress;
Application.DoEvents();
The event handler I created is as follows:
private void displayProgress(object sender, WriteObjectProgressArgs args)
{
// Counter for Event runs
label7.BeginInvoke(new Action(() => label7.Text = (Convert.ToInt32(label7.Text)+1).ToString()));
Application.DoEvents();
// transferred bytes
label4.BeginInvoke(new Action(() => label4.Text = args.TransferredBytes.ToString()));
Application.DoEvents();
// progress bar
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = args.PercentDone));
Application.DoEvents();
}
The issue is that it only updates when a file it downloaded, but the event runs more often. When I download the last file (12MB); lable7 (event counter) jumps from 3 to 121, so I know it is running, but just not updating.
I have also tried just a 'standard' Invoke, but I had the same result.
Additional Code of the function:
AmazonS3Config S3Config = new AmazonS3Config
{
ServiceURL = "https://s3.amazonaws.com"
};
var s3Client = new AmazonS3Client(stuff, stuff, S3Config);
ListBucketsResponse response = s3Client.ListBuckets();
GetObjectRequest request = new GetObjectRequest();
request.BucketName = "dr-test";
request.Key = locationoffile[currentdownload];
GetObjectResponse response2 = s3Client.GetObject(request);
response2.WriteObjectProgressEvent += displayProgress;
string pathlocation = Path.GetDirectoryName(Directory.GetCurrentDirectory()) + "\\" + Instrument[currentdownload] + "\\" + NewFileName[currentdownload];
response2.WriteResponseStreamToFile(pathlocation);
You're not using the asynchronous call for GetObject or WriteResponseStreamToFile, so the UI thread (that you're calling it from) is going to be blocked, which means that it can't update the progress (regardless of those DoEvents calls, which are generally considered evil and you should avoid).
Without actually being able to try it myself, here's what I think you need to do.
private async void Button_Click(object sender, EventArgs e)
{
foreach(...download....in files to download){
AmazonS3Config S3Config = new AmazonS3Config
{
ServiceURL = "https://s3.amazonaws.com"
};
var s3Client = new AmazonS3Client(stuff, stuff, S3Config);
ListBucketsResponse response = s3Client.ListBuckets();
GetObjectRequest request = new GetObjectRequest();
request.BucketName = "dr-test";
request.Key = locationoffile[currentdownload];
GetObjectResponse response2 = await s3Client.GetObjectAsync(request, null);
response2.WriteObjectProgressEvent += displayProgress;
string pathlocation = Path.GetDirectoryName(Directory.GetCurrentDirectory()) + "\\" + Instrument[currentdownload] + "\\" + NewFileName[currentdownload];
await response2.WriteResponseStreamToFileAsync(pathlocation, null);
}
}
The two nulls that I added are for cancellation tokens, I can't tell from the AWS docs if it's allowed to pass a null token, if not please create one and pass it in.
I'am using WPF with multi threading. Problem is during execution of the MAIN thread hangs, like its waiting for something or infinite loop in main UI. I don't know if there's any other approach when it comes to multi threading in WPF. See my code below:
Thread myNewThread1 = new Thread(() => ping(IP1, img_m));
Thread myNewThread2 = new Thread(() => ping(IP2, img_h));
Thread myNewThread3 = new Thread(() => ping(IP3, img_c));
Thread myNewThread4 = new Thread(() => ping(IP4, img_b));
Thread myNewThread5 = new Thread(() => ping(IP5, img_e));
myNewThread1.Start();
myNewThread2.Start();
myNewThread3.Start();
myNewThread4.Start();
myNewThread5.Start();
private void ping(string IP, Image img)
{
this.Dispatcher.Invoke(() =>
{
Ping p = new Ping();
var r = p.Send(IP, 1000, new byte[5]);
if (r.Status == IPStatus.Success)
{
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("subonline.gif", UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
}
else
{
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("suboffline.gif", UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
}
});
Thread.Sleep(500);
ping(IP, img);
}
Your main thread hangs because of Dispatcher.Invoke wrong usage, it should be used for UI logic only, so you should move out the Ping-oriented logic out of it.
Do not use the BackgroundWorker for this, it's an obsolete and heavy construction you don't really need. Also, do not a thread for pinging, this is wrong approach, and that's why:
Pinging operation is network-related, and the thread you're using for wait the remote server to response simply wastes the system resources, as it does absolutely nothing except wait. So you should switch to asynchronous approach for this.
You either should subscribe to Ping.PingCompleted event and call the SendAsync method, like this:
private void ping(string IP, MediaTypeNames.Image img)
{
Ping p = new Ping();
PingReply r;
// lambda delegate here, may use event handler instead
p.PingCompleted += (sender, args) => { PingCompleted(args, img); };
r = p.SendAsync(IP, 1000, new byte[5], null);
}
private void PingCompleted(PingCompletedEventArgs args, MediaTypeNames.Image img)
{
this.Dispatcher.Invoke(() =>
{
string imageAddress;
if (args.Reply.Status == IPStatus.Success)
{
imageAddress = "subonline.gif";
}
else
{
imageAddress = "suboffline.gif";
}
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(imageAddress, UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
});
}
or you should use async/await feature which was introduced exactly for such cases (code partially from this answer):
// async event handler
private async void btn_Click(object sender, EventArgs e)
{
// async call to all the ips
var results = await PingAsync(new List<string> { IP1, IP2, IP3, IP4, IP5 });
// here we do not need the Dispatcher as await will restore UI context for you
PingCompleted(results[0], img_m);
// etc
}
private void PingCompleted(PingReply r, MediaTypeNames.Image img)
{
string imageAddress;
if (r.Status == IPStatus.Success)
{
imageAddress = "subonline.gif";
}
else
{
imageAddress = "suboffline.gif";
}
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(imageAddress, UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
}
// helper method
private async Task<PingReply[]> PingAsync(List<string> theListOfIPs)
{
Ping pingSender = new Ping();
var tasks = theListOfIPs.Select(ip => pingSender.SendPingAsync(ip, 1000, new byte[5]));
return await Task.WhenAll(tasks);
}
Do not include the call to p.Send method in the dispatcher - currently only the Thread.Sleep(500) is done in the background and everything else is done in the UI thread.
Also, I believe that because of the Thread.Sleep the function never gives the UI thread a chance to work so the pinging never occurs. Replace the use of Thread.Sleep with a Thread.Timer object.
I am trying to get the thumbnails for files in a directory asynchronously. All files except PDFs seem to work async.
if (System.IO.File.Exists(filePath))
{
var task = await Task.Factory.StartNew(async () =>
{
using(ShellFile shellFile = ShellFile.FromFilePath(filePath))
{
ImageSource source = shellFile.Thumbnail.MediumBitmapSource;
source.Freeze();
return source;
}
});
image.Dispatcher.Invoke(() => image.Source = task.Result);
}
All other files return correctly. However, if I call all of this code a second time if image.source == null then it works fine.
edit
My working code after Hans Passant's answer
var thread = new Thread(() =>
{
using(ShellFile shellFile = ShellFile.FromFilePath(filePath))
{
ImageSource source = shellFile.Thumbnail.MediumBitmapSource;
source.Freeze();
}
image.Dispatcher.Invoke(() => image.Source = source);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Thanks!
I am trying to use a FolderBrowserDialog as it was mentioned here:
var dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
If I call the Dialog when a button is pressed, it works just fine. But I want to open the Dialog in the middle of my code (there is an incoming file through a socket, so between receiving it and saving it I try to get the path to save it to), and it simply won't happen.
Here is the part of the code where it is called:
byte[] clientData = new byte[1024 * 5000];
int receivedBytesLen = clientSocket.Receive(clientData);
var dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
string filePath = dialog.SelectedPath;
int fileNameLen = BitConverter.ToInt32(clientData, 0);
string fileName = Encoding.ASCII.GetString(clientData, 4, fileNameLen);
BinaryWriter bWrite = new BinaryWriter(File.Open(filePath + "/" + fileName, FileMode.Append)); ;
bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);
bWrite.Close();
How should I try to open the Dialog for it to work?
As others stated you are most likely in a separate thread when trying to call a UI dialog.
In the code you posted you can use the WPF method BeginInvoke with a new Action that will force the FolderBrowserDialog to be invoked in a UI thread.
System.Windows.Forms.DialogResult result = new DialogResult();
string filePath = string.Empty;
var invoking = Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
var dialog = new System.Windows.Forms.FolderBrowserDialog();
result = dialog.ShowDialog();
filePath = dialog.SelectedPath;
}));
invoking.Wait();
If you are creating a separate thread you can set the ApartmentState to STA and this will allow you to call UI dialogs without having to invoke.
Thread testThread = new Thread(method);
testThread.SetApartmentState(ApartmentState.STA);
testThread.Start();
Because you're getting the STA exception, it means you are probably running on a background thread.
InvokeRequired/BeginInvoke pattern to call the dialog:
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new MethodInvoker(ShowDialog)); // where ShowDialog is your method
}
see: http://www.yoda.arachsys.com/csharp/threads/winforms.shtml.
see: Single thread apartment issue
I'm getting a strange AccessViolationException in a WPF application. Since there is no user code on the stack I'm not really sure how to go about troubleshooting this. It also only happens every 2-3 days which adds to the troubleshooting complexity.
at MS.Win32.PresentationCore.UnsafeNativeMethods+MILUnknown.Release(IntPtr)
at MS.Win32.PresentationCore.UnsafeNativeMethods+MILUnknown.ReleaseInterface(IntPtr ByRef)
at System.Windows.Media.SafeMILHandle.ReleaseHandle()
at System.Windows.Media.Imaging.BitmapSourceSafeMILHandle.ReleaseHandle()
at System.Runtime.InteropServices.SafeHandle.InternalFinalize()
at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean)
at System.Runtime.InteropServices.SafeHandle.Finalize()
My image manipulation code (slightly simplified)
private int timerCount;
private void TimerProc() // called from a timer obviously
{
if(Interlocked.Increment(ref timerCount) !=0)
{
Interlocked.Decrement(ref timerCount);
return;
}
try
{
byte[] img = FetchImageFromExternalSource(); //returns a jpeg image
this.Image = LoadImage(new MemoryStream(img));
}
finally
{
Interlocked.Decrement(ref timerCount);
}
}
private BitmapImage LoadImage(Stream inputStream)
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = inputStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
private BitmapImage image;
public BitmapImage Image
{
get
{
return image;
}
set
{
if (image!= value)
{
this.dispatcher.BeginInvoke(new Action(()=>
{
image = value;
PropertyChanged(this,new PropertyChangedEventArgs("Image"));
}),null);
}
}
}
Any advice on if I'm hitting a known bug (.NET 4) or if there is some problem with the code that I've missed would be appreciated.
System.Threading.Timer invokes the callback on a background thread, not on the UI thread, which the BitmapImage class is probably expecting. Try using DispatcherTimer.