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!
Related
I detect faces in image files inside a folder using Emgu CV. I'm using a foreach loop for this. However, the form does not respond until the whole process is finished.
I use threads to avoid this. However, the thread is not working as I expected. The loop ends before the rendering of the images is finished.
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
Thread th = new Thread(() => ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath));
th.Start();
}
Even if I do it this way, it works like it was before using threads.
th.Start();
th.Join();
The simplest thing is to change from Thread to Task (and correctly dispose of your image) like this:
foreach (var item in files)
{
using (Image img = Image.FromFile(item))
{
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
await Task.Run(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath));
}
}
That MUST be done in a async Task method (or async void event handler) to allow the use of the await keyword.
This approach will run each imageprocessmulti one after the other without blocking the UI.
If you want to run all of the imageprocessmulti in parallel, then you're best off making a list of tasks and awaiting them all at once. Like this:
List<Task> tasks =
(
from item in files
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
select Task.Run(() =>
{
using (Image img = Image.FromFile(item))
{
ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath);
}
})
).ToList();
await Task.WhenAll(tasks);
My preferred approach is to use Microsoft's Reactive Framework - NuGet System.Reactive - and then do this:
IObservable<Unit> query =
from item in files.ToObservable(Scheduler.Default)
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
from unit in Observable.Using(
() => Image.FromFile(item),
img => Observable.Start(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath)))
select unit;
await query.ToArray();
All of these approaches should work for you.
Here's one way to approach it using async/await, for example, with a button click event:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
}
});
// ...more code here to run after all images have been processed...
MessageBox.Show("Done!");
}
Here's an alternate version to show progress, not sure what you tried:
private async void button1_Click_1(object sender, EventArgs e)
{
progressBar1.Visible = true;
progressBar1.Value = 0;
int counter = 0;
int total = files.Length;
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
counter++;
int p = (int)((double)counter / total * 100);
progressBar1.Invoke((MethodInvoker) delegate {
progressBar1.Value = p;
});
}
});
// ...more code here to run after all images have been processed...
progressBar1.Visible = false;
MessageBox.Show("Done!");
}
I have a function that works great when opening JPG and PNG files, but locks my computer up when opening a CR2 file. The CRS files are coming from my Canon EOS Rebel T6 Camera and they are big, see:
but I feel like it should not lock my computer up and it should not move this slow.
here is my code :
public async Task OpenFile()
{
OriginalImage = null;
await SetProcessBarInformation(0, "Openning Image file");
OpenFileDialog _openFileDialog = new OpenFileDialog();
if (_openFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
await SetProcessBarInformation(20, "Openning Image file");
FileName = System.IO.Path.GetFileName(_openFileDialog.FileName);
BitmapImage tempOriginalImage = await GetPicture(_openFileDialog.FileName);
Height = (int)tempOriginalImage.Height;
Width = (int)tempOriginalImage.Width;
await SetProcessBarInformation(80, "Openning Image file");
await Task.Run(() =>
{
OriginalImage = tempOriginalImage;
});
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
}
protected async Task<BitmapImage> GetPicture(string tempImage)
{
BitmapImage timage = null;
await Task.Run(() =>
{
timage = new BitmapImage(new Uri(tempImage));
timage.Freeze();
});
return timage;
}
First of all, sorry because I am so new at C# and I decided to make this question because I have been choked in this for hours.
I have an GUI that works with Google Cloud Speech services and make a Speech-to-Text operation. I share with you the whole method that runs when a button is clicked:
private async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
GoogleCredential googleCredential;
using (Stream m = new FileStream(#"..\..\credentials.json", FileMode.Open))
googleCredential = GoogleCredential.FromStream(m);
var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
googleCredential.ToChannelCredentials());
var speech = SpeechClient.Create(channel);
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 48000,
LanguageCode = "es-ES",
},
InterimResults = true,
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
//Textbox1.Text = alternative.Transcript;
}
}
}
});
waveIn.StartRecording();
Console.WriteLine("Speak now.");
Result_Tone.Text = "Speak now:\n\n";
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
My problem is that I want to update the content of the Textbox1control but it doesn´t work. It writes perfectly the output into the console with the line Console.WriteLine(alternative.Transcript); but not into my textbox.
If someone could help I would appreciate so much his help.
The problem is that you're using Task.Run, which means your code will be running on a thread-pool thread.
Instead of calling Task.Run(), just move that code into a separate async method:
async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
while (await responses.MoveNext(default(CancellationToken)))
{
foreach (var result in responses.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}
}
Then call that method directly (without Task.Run) from code that's already on the UI thread (e.g. an event handler).
The async machinery will make sure that after the await expression, you're back on the UI thread (the same synchronization context). So the assignment to the Text property will occur on the UI thread, and all should be well.
For example:
// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
var stream = client.StreamingRecognize();
// Send the initial config request
await stream.WriteAsync(...);
// Presumably you want to send audio data...
StartSendingAudioData(stream);
await DisplayResponses(stream.ResponseStream);
}
Tasks run on seperate threads, so you must Invoke an action that will be performed on the control's thread
Textbox1.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
Edit: For WPF, I believe the equivalent is
Textbox1.Dispatcher.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
have you tried using Dispatcher.InvokeASync()?
await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}});
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 have a code which should take all pictures from folder, put them into object named "PhotoInspection", add some informations and put this object into list. See the code below
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
bool isOn = tsOnOff.IsOn;
TextBlock tbChosen = new TextBlock();
tbChosen = lbInspections.SelectedItem as TextBlock;
string chosen = tbChosen.Text;
AllInspectionPhotos aip = new AllInspectionPhotos();
var folders = await ApplicationData.Current.LocalFolder.GetFoldersAsync();
if (isOn)
{
foreach (var item in folders)
{
if (item.Name == "ONLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photos = await inspectionFolder.GetFilesAsync();
foreach (var photo in photos)
{
using (Stream stream = await photo.OpenStreamForReadAsync())
{
PhotoInspection phtInsp = new PhotoInspection();
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
aip.Photos.Add(phtInsp);
}
}
}
}
}
}
}
else
{
foreach (var item in folders)
{
if (item.Name == "OFFLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photoset = await inspectionFolder.GetFilesAsync();
foreach (var photo in photoset)
{
PhotoInspection phtInsp = new PhotoInspection();
using (Stream stream = await photo.OpenStreamForReadAsync())
{
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//await App._client.SendPhotoAsync(phtInsp);
aip.Photos.Add(phtInsp); //fires error here
});
}
}
}
}
}
}
await App._client.SendAllPhotosAsync(aip);
}
However when I try to add object into list, I get "attempted to read write protected memory" error. PhotoInspection object is filled with relevant data and everything looks good before adding to list. Thanks for any help
The problem is due to the Dispatcher.RunAsync call, which returns control to the calling routine as soon as the aip.Photos.Add() lambda is queued, and not when it has completed. This results in the aip object being disposed before the lambda has actually run.
The MSDN page for CoreDispatcher.RunAsync says:
[...] it schedules the work on the UI thread and returns control to the caller immediately.
and:
To spin off a worker thread from the UI thread, do not use this method (CoreDispatcher::RunAsync). Instead, use one of the Windows::System::Threading::ThreadPool::RunAsync method overloads.
It also provides the following code example, and the comment is from Microsoft!
//DO NOT USE THIS CODE.
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await signInDialog.ShowAsync();
});
// Execution continues here before the call to ShowAsync completes.