Azure cognitive stops immediately after I start - c#

I am trying to transcribe a WAV file and return the text, but it stopped immediately when I start working
I have a button, that when I click it, will call this method, and it supposedly returns me the text of the wav file, but as you can see from the code, I have a Dbug.WriteLine in the start and the stop, and it will print stop immediately
public List<char> Letters { get; set; }
public StringBuilder Builder { get; set; }
public AzureTranscriptionService() {
Letters = new List<char>();
Builder = new StringBuilder();
}
public async Task<string> ConvertToTextAsync(
string FilePath,
string Language,
string WordDocName) {
//Configure speech service
var config = SpeechConfig.FromSubscription
(ConstantsHelpers.AZURE_KEY,
ConstantsHelpers.AZURE_REGION);
config.EnableDictation();
config.SpeechRecognitionLanguage = Language;
//Configure speech recognition
var stopRecognition = new TaskCompletionSource<string>();
using var audioConfig = AudioConfig.FromWavFileInput
(FilePath);
{
using var speechRecognizer =
new SpeechRecognizer(config, audioConfig);
speechRecognizer.Recognized += (sender, e) => {
if (e.Result.Reason == ResultReason.RecognizedSpeech) {
foreach (var c in e.Result.Text) {
Letters.Add(c);
}
}
};
speechRecognizer.SessionStarted += (sender, e) => {
Debug.WriteLine("------> Started");
};
speechRecognizer.SessionStopped += (sender, e) => {
Debug.WriteLine("Stop");
foreach (var item in Letters) {
Builder.Append(item);
}
};
speechRecognizer.Recognizing += (sender, e) => {
Debug.WriteLine(e.Result.Text);
stopRecognition.TrySetResult(0);
};
await speechRecognizer.StartContinuousRecognitionAsync();
// Waits for completion. Use Task.WaitAny to keep the task rooted.
await Task.WhenAny(new[] { stopRecognition.Task });
return Builder.ToString();
}
}
}
}
Update
I tried to change the code to this
private TaskCompletionSource<string>? SpeechRecognitionCompletionSource { get; set; }
private StringBuilder? RecognizedSpeechTextBuilder { get; set; } = new StringBuilder();
private SpeechRecognizer? SpeechRecognizer { get; set; }
public async Task<string> ConvertToTextAsync(string filePath,
string language,
string wordDocName) {
// Configure speech service
var config = SpeechConfig.FromSubscription(ConstantsHelpers.AZURE_KEY, ConstantsHelpers.AZURE_REGION);
config.EnableDictation();
config.SpeechRecognitionLanguage = language;
// Configure speech recognition
this.SpeechRecognitionCompletionSource = new TaskCompletionSource<string>();
using var audioConfig = AudioConfig.FromWavFileInput(filePath);
using (SpeechRecognizer = new SpeechRecognizer(config, audioConfig)) {
SpeechRecognizer.SessionStarted += OnSessionStarted!;
SpeechRecognizer.SessionStopped += OnSessionStopped!;
SpeechRecognizer.Recognizing += OnRecognizing!;
SpeechRecognizer.Recognized += OnRecognized!;
await this.SpeechRecognizer.StartContinuousRecognitionAsync();
// Wait for the 'SpeechRecognizer.StopContinuousRecognitionAsync' call
// before returning the result string.
// In this case, 'SpeechRecognizer.StopContinuousRecognitionAsync'
// is called from a Button.Click event handler.
return await SpeechRecognitionCompletionSource.Task;
}
}
// Stop speech recognition when the user clicks a button
private async void OnStopButtonClicked(object sender, RoutedEventArgs e) {
await SpeechRecognizer!.StopContinuousRecognitionAsync();
}
private void OnSessionStarted(object sender, SessionEventArgs e) {
Debug.WriteLine("------> Started");
}
private void OnSessionStopped(object sender, SessionEventArgs e) {
Debug.WriteLine("Stop");
SpeechRecognitionCompletionSource!.TrySetResult(RecognizedSpeechTextBuilder!.ToString());
}
private void OnRecognizing(object sender, SpeechRecognitionEventArgs e) {
Debug.WriteLine(e.Result.Text);
}
private void OnRecognized(object sender, SpeechRecognitionEventArgs e) {
if (e.Result.Reason != ResultReason.RecognizedSpeech) {
return;
}
foreach (var speechText in e.Result.Text) {
this.RecognizedSpeechTextBuilder!.Append(speechText);
}
}
}
}
and then I call this method in here'
private async void StartAction(object obj) {
IsBusy = true;
CanShow = Visibility.Visible;
var FileWithoutExtension = Path.GetFileNameWithoutExtension
(FilePath);
var AudioPath = FolderHelper.CreateFolder(ConstantsHelpers.AUDIO);
var DocumentPath = FolderHelper.CreateFolder();
var AudioFileNamePath = Path.Combine(AudioPath, $"{FileWithoutExtension}{ConstantsHelpers.WAV}");
var ConvertedAudioPath = AudioHelper.Converter(FilePath!, AudioFileNamePath);
var DocumentName = Path.Combine(DocumentPath, $"{FileWithoutExtension}{ConstantsHelpers.DOCX}");
var res = await AzureTranscription.ConvertToTextAsync(ConvertedAudioPath,
SelectedItem, DocumentName);
IsBusy = false;
StartCommand.RaiseCanExecuteChanged();
CanShow = Visibility.Hidden;
}
Update
I manage to get it working, but I need to return the text to my MainViewModel
public async Task ConvertToTextAsync(string FilePath, string FileName) {
List<char> Characers = new();
StringBuilder builder = new();
//Configure speech service
var config = SpeechConfig.FromSubscription
(ConstantsHelpers.AZURE_KEY, ConstantsHelpers.AZURE_REGION);
config.EnableDictation();
//Configure speech recognition
var taskCompleteionSource = new TaskCompletionSource<int>();
using var audioConfig = AudioConfig.FromWavFileInput(FilePath);
if (!string.IsNullOrEmpty(FileName)) {
using var speechRecognizer = new SpeechRecognizer(config, audioConfig);
speechRecognizer.Recognized += (sender, e) => {
if (e.Result.Reason == ResultReason.RecognizedSpeech) {
foreach (var item in e.Result.Text) {
Characers.Add(item);
}
}
};
speechRecognizer.SessionStarted += (sender, e) => {
Debug.WriteLine("--------> started");
};
speechRecognizer.SessionStopped += (sender, e) => {
Debug.WriteLine("-----------> stooped");
foreach (var item in Characers) {
builder.Append(item);
}
Debug.WriteLine(builder.ToString());
};
await speechRecognizer.StartContinuousRecognitionAsync()
.ConfigureAwait(false);
Task.WaitAny(new[] { taskCompleteionSource.Task });
await speechRecognizer.StopContinuousRecognitionAsync()
.ConfigureAwait(false);
}
return builder.ToString();
}
}
}

I seems like you are returning from the ConvertToTextAsync method prematurely. This is because you are transitioning the TaskCompletionSource too early. As a consequence you leave the method and therefore the using scope of the SpeechRecognizer, which as a result will stop the continuous speech recognition.
The idea is to complete the TaskCompletionSource not before the SpeechRecognizer has stopped (by calling the SpeechRecognizer.StopContinuousRecognitionAsync method). You can achieve this by calling TaskCompletionSource.TrySetResult from the SpeechRecognizer.SessionStopped event handler:
private TaskCompletionSource<string> SpeechRecognitionCompletionSource { get; set; }
private StringBuilder RecognizedSpeechTextBuilder { get; set; }
private SpeechRecognizer SpeechRecognizer { get; set; }
public async Task<string> ConvertToTextAsync(string filePath,
string language,
string wordDocName)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException(filePath);
}
// Configure speech service
var config = SpeechConfig.FromSubscription(ConstantsHelpers.AZURE_KEY, ConstantsHelpers.AZURE_REGION);
config.EnableDictation();
config.SpeechRecognitionLanguage = language;
// Configure speech recognition
this.SpeechRecognitionCompletionSource = new TaskCompletionSource<string>();
using var audioConfig = AudioConfig.FromWavFileInput(filePath);
using (this.SpeechRecognizer = new SpeechRecognizer(config, audioConfig))
{
this.SpeechRecognizer.SessionStarted += OnSessionStarted;
this.SpeechRecognizer.SessionStopped += OnSessionStopped;
this.SpeechRecognizer.Recognizing += OnRecognizing;
this.SpeechRecognizer.Recognized += OnRecognized;
this.SpeechRecognizer.Recognized += OnCanceled;
await this.SpeechRecognizer.StartContinuousRecognitionAsync();
// Wait for the 'SpeechRecognizer.StopContinuousRecognitionAsync' call
// before returning the result string.
// In this case, 'SpeechRecognizer.StopContinuousRecognitionAsync'
// is called from a Button.Click event handler.
return await this.SpeechRecognitionCompletionSource.Task;
}
}
// Stop speech recognition when the user clicks a button
private async void OnStopButtonClicked(object sender, RoutedEventArgs e)
{
await this.SpeechRecognizer.StopContinuousRecognitionAsync();
}
private void OnSessionStarted(object sender, SessionEventArgs e)
{
Debug.WriteLine("------> Started");
}
private void OnSessionStopped(object sender, SessionEventArgs e)
{
Debug.WriteLine("Stop");
this.SpeechRecognitionCompletionSource.TrySetResult(this.RecognizedSpeechTextBuilder.ToString());
}
private void OnRecognizing(object sender, SpeechRecognitionEventArgs e)
{
Debug.WriteLine(e.Result.Text);
}
private void OnRecognized(object sender, SpeechRecognitionEventArgs e)
{
if (e.Result.Reason != ResultReason.RecognizedSpeech)
{
return;
}
foreach (string speechText in e.Result.Text)
{
this.RecognizedSpeechTextBuilder.Append(speechText);
}
}
private void OnCanceled(object sender, SpeechRecognitionCanceledEventArgs e)
{
if(e.Reason == CancellationReason.EndOfStream)
{
await this.SpeechRecognizer.StopContinuousRecognitionAsync();
}
}

Related

Event Handler Fires Twice When Plugging/Unplugging USB Serial Port

I am writing a program which is supposed to detect when a USB serial device is plugged in and then log on to the new com port. The code below works wonderfully, but I have noticed in debugging the code and stepping through it that the event handler "DetectChange" fires twice. I'm not sure that this is normal, or an action of the debugger.
In any case, the code works, but I am new at event handling and I would like to make sure that I am not going to cause any issues as I add more code to actually read and write from the serial port.
(I got some of this code from stackoverflow, but I have misplaced my paper with names for attribution. If you see your code below, my heartfelt thanks.)
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
using System.Threading;
namespace SerialTest
{
public partial class Form1 : Form
{
SerialMethods serialMethods = new SerialMethods();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
loadCmdBox();
}
private void CmdBoxPort_SelectedIndexChanged(object sender, EventArgs e)
{
handleComPort();
}
private void handleComPort()
{
// Set the right port for the selected item.
// The portname is based on the "COMx" part of the string (SelectedItem)
string item = CmdBoxPort.SelectedItem.ToString();
// Search for the expression "(COM" in the "selectedItem" string
if (item.Contains("(COM"))
{
// Get the index number where "(COM" starts in the string
int indexOfCom = item.IndexOf("(COM");
// Set PortName to COMx based on the expression in the "selectedItem" string
// It automatically gets the correct length of the COMx expression to make sure
// that also a COM10, COM11 and so on is working properly.
string PortName = item.Substring(indexOfCom + 1, item.Length - indexOfCom - 2);
if (serialMethods._serialPort.IsOpen)
{
serialMethods._serialPort.Close();
serialMethods.Connect(PortName);
label5.Text = "Active Port: " + PortName;
}
else
{
serialMethods.Connect(PortName);
label5.Text = PortName;
}
}
else
return;
}
private void loadCmdBox()
{
// Get all serial (COM)-ports you can see in the devicemanager
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\cimv2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\"");
// Sort the items in the combobox
CmdBoxPort.Sorted = true;
// Add all available (COM)-ports to the combobox
foreach (System.Management.ManagementObject queryObj in searcher.Get().Cast<ManagementObject>())
{
CmdBoxPort.Items.Add(queryObj["Caption"]);
}
SerialPortService.PortsChanged += (sender1, changedArgs) => DetectChange(changedArgs.EventType);
label2.Text = "";
label3.Text = "";
label4.Text = "";
}
protected Task<Task> getSerPorts()
{
CmdBoxPort.Text = "";
CmdBoxPort.Update();
if (!String.IsNullOrEmpty(CmdBoxPort.Text))
{
handleComPort();
return Task.FromResult(Task.CompletedTask);
}
else
{
loadCmdBox();
return Task.FromResult(Task.CompletedTask);
}
}
private void ExitButton_Click(object sender, EventArgs e)
{
SerialPortService.CleanUp();
this.Close();
}
private void RefreshButton_Click(object sender, EventArgs e)
{
refresh();
}
protected Task<Task> refresh()
{
label2.Text = "";
label3.Text = "";
label4.Text = "";
CmdBoxPort.Items.Clear();
getSerPorts();
return Task.FromResult(Task.CompletedTask);
}
protected virtual void DetectChange(EventType changedArgs)
{
if (changedArgs == EventType.Insertion)
{
try
{
Task tr = (Task)Invoke(new Action( () => { getSerPorts(); }));
Task rr = (Task)Invoke(new Action(() => { refresh(); }));
}
catch (Exception ex) { MessageBox.Show("Exception at insertion invoke method " + ex, "Exception", MessageBoxButtons.OK); }
}
else if (changedArgs == EventType.Removal)
{
try
{
Task tr = (Task)Invoke(new Action( () => { getSerPorts(); }));
Task rr = (Task)Invoke(new Action(() => { refresh(); }));
}
catch (Exception ex) { MessageBox.Show("Exception at removal invoke method " + ex, "Exception", MessageBoxButtons.OK); }
}
return;
}
}
public static class SerialPortService
{
private static SerialPort _serialPort;
private static string[] _serialPorts;
private static ManagementEventWatcher arrival;
private static ManagementEventWatcher removal;
private static readonly SerialMethods SD = new SerialMethods();
static SerialPortService()
{
_serialPorts = SerialPort.GetPortNames();
MonitorDeviceChanges();
}
public static void CleanUp()
{
arrival.Stop();
removal.Stop();
}
public static event EventHandler<PortsChangedArgs> PortsChanged;
private static void MonitorDeviceChanges()
{
try
{
var deviceArrivalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
var deviceRemovalQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
arrival = new ManagementEventWatcher(deviceArrivalQuery);
removal = new ManagementEventWatcher(deviceRemovalQuery);
arrival.EventArrived += (o, args) => RaisePortsChangedIfNecessary(EventType.Insertion);
removal.EventArrived += (sender, eventArgs) => RaisePortsChangedIfNecessary(EventType.Removal);
// Start listening for events
arrival.Start();
removal.Start();
}
catch (ManagementException err)
{
MessageBox.Show("Management exception = " + err, "Info", MessageBoxButtons.OK);
}
}
private static void RaisePortsChangedIfNecessary(EventType eventType)
{
lock (_serialPorts)
{
var availableSerialPorts = SerialPort.GetPortNames();
if (eventType == EventType.Insertion)
{
var added = availableSerialPorts.Except(_serialPorts).ToArray();
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, added));
}
else if (eventType == EventType.Removal)
{
var removed = _serialPorts.Except(availableSerialPorts).ToArray();
_serialPorts = availableSerialPorts;
PortsChanged.Raise(null, new PortsChangedArgs(eventType, removed));
}
}
}
public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
handler?.Invoke(sender, args);
}
}
public enum EventType
{
Insertion,
Removal,
}
public class PortsChangedArgs : EventArgs
{
private readonly EventType _eventType;
private readonly string[] _serialPorts;
public PortsChangedArgs(EventType eventType, string[] serialPorts)
{
_eventType = eventType;
_serialPorts = serialPorts;
}
public string[] SerialPorts => _serialPorts;
public EventType EventType => _eventType;
}
}
Just took a short look at this. It seems like getSerPorts() will always execute loadCmdBox() (CmdBoxPort.Text = ""; ... if (!String.IsNullOrEmpty(CmdBoxPort.Text))) that will attach a new event handler (previous attached event handlers will not be removed by attaching a new one).
You should either remove the existing event handler befor attaching a new one or only attach the event handler once.

SQLite query blocking UI in BackgroundWorker

I've got a BackgroundWorker Queue for my Windows Forms app, which works fine with API calls, but querying a local SQLite database is blocking the UI. I realized a while ago that calling Invoke on an action runs it on the UI thread, so I've changed the DoWork function to be a simple function call on the BackgroundWorker's thread.
The problem is in the loadItems() function in my Form1 class. It queues a background worker to query the sqlite database.
What could be causing the UI block and what can I change to prevent it?
My custom Worker class:
using System.ComponentModel;
namespace EveMarket.Tasks
{
public class Worker : BackgroundWorker
{
public Worker(object Sender)
{
this.Sender = Sender;
}
public object Sender { get; set; }
}
}
My Task Manager class (manages the queue)
using System.ComponentModel;
namespace EveMarket.Tasks
{
public class TaskManager
{
public static Queue<Worker> taskQueue = new Queue<Worker>();
public static List<string> errors = new List<string>();
public static void QueueTask(
object item,
Action<object, Worker, DoWorkEventArgs> action,
Action<object, RunWorkerCompletedEventArgs> actionComplete,
Action<RunWorkerCompletedEventArgs> displayError,
Action<object, ProgressChangedEventArgs> progressChanged)
{
using (var worker = new Worker(item))
{
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.ProgressChanged += (sender, args) =>
{
progressChanged.Invoke(sender, args);
};
worker.DoWork += (sender, args) =>
{
action(sender, worker, args);
};
worker.RunWorkerCompleted += (sender, args) =>
{
if(args.Error != null)
{
displayError.Invoke(args);
} else
actionComplete.Invoke(sender, args);
taskQueue.Dequeue();
if (taskQueue.Count > 0)
{
startQueue();
}
};
taskQueue.Enqueue(worker);
if(taskQueue.Count == 1)
{
startQueue();
}
}
}
private static void startQueue()
{
var next = taskQueue.Peek();
next.ReportProgress(0, "");
next.RunWorkerAsync(next.Sender);
}
}
}
Database service class for Item:
using Microsoft.Data.Sqlite;
using Dapper;
namespace EveMarket.Database
{
public class Item
{
public Item() { }
public Item(int type_id, string name)
{
this.type_id = type_id;
this.name = name;
}
public string name { get; set; }
public int type_id { get; set; }
public static void insert(Item item)
{
using (var connection = new SqliteConnection(Connection.ConnectionString))
{
var sql = "insert into item (type_id, name) values (#type_id, #name)";
connection.ExecuteAsync(sql, item);
}
}
public static List<Item> getAll()
{
using (var connection = new SqliteConnection(Connection.ConnectionString))
{
var sql = "select * from item;";
List<Item> items = connection.Query<Item>(sql).ToList();
return items;
}
}
}
}
And the Form which is Queuing these tasks:
using EveMarket.Database;
using EveMarket.Tasks;
using EVEStandard;
namespace EveMarket
{
public partial class Form1 : Form
{
private EVEStandardAPI client;
private List<Item> items;
public Form1()
{
InitializeComponent();
this.items = new List<Item>();
client = new EVEStandardAPI("email#email.com", EVEStandard.Enumerations.DataSource.Tranquility, TimeSpan.FromSeconds(30));
}
private void Form1_Load(object sender, EventArgs e)
{
loadItems();
toolStripProgressBar1.Value = 50;
}
private void updateItemListToolStripMenuItem_Click(object sender, EventArgs e)
{
fetchItems();
}
private void fetchItems()
{
TaskManager.QueueTask(this,
// DoWork
(x, w, e) =>
{
List<long> ids = new List<long>();
for (int i = 1; i < 17; i++)
{
var task = client.Market.ListTypeIdsRelevantToMarketV1Async(10000002, page: i);
var page = task.Result.Model;
ids.AddRange(page);
w.ReportProgress((i * 100) / 16, "Fetching Market Item IDs Page: " + i);
}
List<List<long>> chunks = Utils.ChunkBy(ids, 1000);
int j = 1;
w.ReportProgress((j * 100) / chunks.Count, "Fetching Item Names ");
foreach (var chunk in chunks)
{
var task = client.Universe.GetNamesAndCategoriesFromIdsV3Async(chunk.ConvertAll(i => (int)i));
var names = task.Result.Model;
var types = names.FindAll((item) => item.Category == EVEStandard.Enumerations.CategoryEnum.inventory_type);
foreach (var type in types)
{
Item item = new Item(type.Id, type.Name);
Item.insert(item);
}
w.ReportProgress((j * 100) / chunks.Count, "Fetching Market Item Names Chunk: " + j);
j++;
}
},
// On Complete
(x, e) =>
{
loadItems();
this.toolStripStatusLabel1.Text = "Idle";
this.toolStripProgressBar1.Value = 0;
},
// On Error
(e) =>
{
MessageBox.Show("Error: " + e.Result.ToString());
},
// On Progress Change
(x, e) =>
{
this.toolStripProgressBar1.Value = e.ProgressPercentage;
this.toolStripStatusLabel1.Text = e.UserState.ToString();
});
}
private void loadItems()
{
TaskManager.QueueTask(this,
// DoWork
(x, w, e) =>
{
w.ReportProgress(50, "Loading Items");
List<Item> i = Item.getAll();
w.ReportProgress(100, "Items Loaded");
this.items = i;
},
// On Complete
(x, e) =>
{
foreach (var item in items)
{
itemBindingSource.Add(item);
}
itemBindingSource.EndEdit();
this.toolStripStatusLabel1.Text = "Idle";
this.toolStripProgressBar1.Value = 0;
},
// On Error
(e) =>
{
MessageBox.Show("Error: " + e.ToString());
},
// On Progress Change
(x, e) =>
{
this.toolStripProgressBar1.Value = e.ProgressPercentage;
this.toolStripStatusLabel1.Text = e.UserState.ToString();
});
}
}
}
Turns out it was the RunWorkerCompleted loop blocking the UI. I discovered that I could simply overwrite the datasource with the result of the Worker's execution, which works immediately.
itemBindingSource.DataSource = e.Result;

Trying to fetch email that cause NewMessageReceived to fire using MailSystem.NET library

I'm trying to fetch emails as soon as they arrive in my inbox using MailSystem.NET library. Everything works fine IMAP client gets connected but my NewMessageReceived event is never fired.
Please Help
Below is the code:
public static Imap4Client _imap = new Imap4Client();
public string SenderEmailAddress = System.Configuration.ConfigurationManager.AppSettings["EmailAddress"];
public string SenderEmailPassword = System.Configuration.ConfigurationManager.AppSettings["EmailPassword"];
public static Mailbox inbox = new Mailbox();
protected void Application_Start()
{
var worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(StartIdleProcess);
if (worker.IsBusy)
worker.CancelAsync();
worker.RunWorkerAsync();
}
private void StartIdleProcess(object sender, DoWorkEventArgs e)
{
try
{
if (_imap != null && _imap.IsConnected)
{
_imap.StopIdle();
_imap.Disconnect();
}
_imap = new Imap4Client();
_imap.NewMessageReceived += new NewMessageReceivedEventHandler(NewMessageReceived);
_imap.ConnectSsl("imap.gmail.com", 993);
_imap.Login(SenderEmailAddress, SenderEmailPassword);
inbox = _imap.SelectMailbox("inbox");
int[] ids = inbox.Search("UNSEEN");
inbox.Subscribe();
_imap.StartIdle();
}
catch (Exception ex)
{
}
}
public static void NewMessageReceived(object source, NewMessageReceivedEventArgs e)
{
int offset = e.MessageCount - 2;
Message message = inbox.Fetch.MessageObject(offset);
Debug.WriteLine("message subject: " + message.Subject);
// Do something with the source...
_imap.StopIdle();
}
I can't tell you the exact reason but it seems that interacting with the imapclient from the NewMessageReceived event just doesn't work.
In NewMessageReceived call _imap.StopIdle() then continue in your main execution flow and restart idle. Then use a boolean to drop out of the loop entirely.
private bool _stop = false;
private void StartIdle(object sender, DoWorkEventArgs e)
{
//Setup client
_imap = new Imap4Client();
_imap.NewMessageReceived += new NewMessageReceivedEventHandler(NewMessageReceived);
StartRepeatExecution();
}
public void StartRepeatExecution()
{
_imap.StartIdle();
if(_stop) return;
//Handle your new messages here! dummy code
var mailBox = _imap.SelectMailBox("inbox");
var messages = mailBox.SearchParse("").Last();
StartRepeatExecution();
}
public static void NewMessageReceived(object source, NewMessageReceivedEventArgs e)
{
//StopIdle will return to where _imap.StartIdle() was called.
_imap.StopIdle();
}
public void StopRepeatExecution()
{
_stop = true;
}

Async wait until form is loaded to continue

I am trying to get my form to wait until a particular part of my _Load method is finished before continuing. I have a few methods that are async, but I cannot figure out why I am not able to get the code to wait until fakeClickCheckUpdate is finished before continuing. Here are the main methods involved:
public myForm(string args)
{
InitializeComponent();
Load += myForm_Load;
}
private void myForm_Load(object s, EventArgs e)
{
this.fakeClickCheckUpdate();
loadFinished = true;
if (this.needsUpdate == true)
{
Console.WriteLine("Needs update...");
}
else
{
Console.WriteLine("update is false");
}
}
public void fakeClickCheckUpdate()
{
this.checkUpdateButton.PerformClick();
}
private async void checkUpdateButton_Click(object sender, EventArgs e)
{
await startDownload(versionLink, versionSaveTo);
await checkVersion();
Console.WriteLine(needsUpdate);
}
private async Task checkVersion()
{
string currVersion;
string newVersion;
using (StreamReader sr = new StreamReader(currVersionTxt))
{
currVersion = sr.ReadToEnd();
}
using (StreamReader nr = new StreamReader(versionSaveTo))
{
newVersion = nr.ReadToEnd();
}
if (!newVersion.Equals(currVersion, StringComparison.InvariantCultureIgnoreCase))
{
this.BeginInvoke((MethodInvoker)delegate
{
progressLabel.Text = "New version available! Please select 'Force Download'";
});
this.needsUpdate = true;
}
else
{
this.BeginInvoke((MethodInvoker)delegate
{
progressLabel.Text = "Your version is up-to-date. No need to update.";
});
this.needsUpdate = false;
}
}
Basically, I want it to check the current version with checkVersion and finish that before it tries to continue past loadFinished = true inside of myForm_Load. I have checkVersion set as an async Task so that the button click can use await on it. Is there any way to get the functionality I need with this code?
First, move your code out of your perform click action.
private async void checkUpdateButton_Click(object sender, EventArgs e)
{
await CheckForUpdate();
}
private async Task CheckForUpdate()
{
await startDownload(versionLink, versionSaveTo);
await checkVersion();
Console.WriteLine(needsUpdate);
}
You can then call that same function in your OnLoad.
private async void myForm_Load(object s, EventArgs e)
{
await CheckForUpdate();
loadFinished = true;
if (this.needsUpdate == true)
{
Console.WriteLine("Needs update...");
}
else
{
Console.WriteLine("update is false");
}
}

How to call WebBrowser Navigate to go through a number of urls?

To collect information on a webpage, I can use the WebBrowser.Navigated event.
First, navigate to the url:
WebBrowser wbCourseOverview = new WebBrowser();
wbCourseOverview.ScriptErrorsSuppressed = true;
wbCourseOverview.Navigate(url);
wbCourseOverview.Navigated += wbCourseOverview_Navigated;
Then process the webpage when Navigated is called:
void wbCourseOverview_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
//Find the control and invoke "Click" event...
}
The difficult part comes when I try to go through a string array of urls.
foreach (var u in courseUrls)
{
WebBrowser wbCourseOverview = new WebBrowser();
wbCourseOverview.ScriptErrorsSuppressed = true;
wbCourseOverview.Navigate(u);
wbCourseOverview.Navigated += wbCourseOverview_Navigated;
}
Here, because the page load takes time, wbCourseOverview_Navigated is never reached.
I tried to use the async await in C#5. Tasks and the Event-based Asynchronous Pattern (EAP) is found in here. Another example can be found in The Task-based Asynchronous Pattern.
The problem is WebClient has async method like DownloadDataAsync and DownloadStringAsync. But there is no NavigateAsync in WebBrowser.
Can any expert give me some advice? Thank you.
There is a post in StackOverflow (here). But, does anyone know how to implement that strut in its answer?
Update again.
Suggested in another post here in StackOverflow,
public static Task WhenDocumentCompleted(this WebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
browser.DocumentCompleted += (s, args) => tcs.SetResult(true);
return tcs.Task;
}
So I have:
foreach (var c in courseBriefs)
{
wbCourseOverview.Navigate(c.Url);
await wbCourseOverview.WhenDocumentCompleted();
}
It looks good until my web browser visits the second url.
An attempt was made to transition a task to a final state when it had already completed.
I know I must have made a mistake inside the foreach loop. Because the DocumentCompleted event has not been raised when it loops to the second round. What is the correct way to write this await in a foreach loop?
There is a post in StackOverflow (here). But, does anyone know how to implement that strut in its answer?
Ok, so you want some code with awaiter. I've made two pieces of code.
The first one uses TPL's built-in awaiter:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ProcessUrlsAsync(new[] { "http://google.com", "http://microsoft.com", "http://yahoo.com" })
.Start();
}
private Task ProcessUrlsAsync(string[] urls)
{
return new Task(() =>
{
foreach (string url in urls)
{
TaskAwaiter<string> awaiter = ProcessUrlAsync(url);
// or the next line, in case we use method *
// TaskAwaiter<string> awaiter = ProcessUrlAsync(url).GetAwaiter();
string result = awaiter.GetResult();
MessageBox.Show(result);
}
});
}
// Awaiter inside
private TaskAwaiter<string> ProcessUrlAsync(string url)
{
TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
{
// TODO: put custom processing of document right here
taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
});
webBrowser1.DocumentCompleted += handler;
taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });
webBrowser1.Navigate(url);
return taskCompletionSource.Task.GetAwaiter();
}
// (*) Task<string> instead of Awaiter
//private Task<string> ProcessUrlAsync(string url)
//{
// TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
// var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
// {
// taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
// });
// webBrowser1.DocumentCompleted += handler;
// taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });
// webBrowser1.Navigate(url);
// return taskCompletionSource.Task;
//}
And the next sample contains the sample implementation of awaiter struct Eric Lippert was talking about here.
public partial class Form1 : Form
{
public struct WebBrowserAwaiter
{
private readonly WebBrowser _webBrowser;
private readonly string _url;
private readonly TaskAwaiter<string> _innerAwaiter;
public bool IsCompleted
{
get
{
return _innerAwaiter.IsCompleted;
}
}
public WebBrowserAwaiter(WebBrowser webBrowser, string url)
{
_url = url;
_webBrowser = webBrowser;
_innerAwaiter = ProcessUrlAwaitable(_webBrowser, url);
}
public string GetResult()
{
return _innerAwaiter.GetResult();
}
public void OnCompleted(Action continuation)
{
_innerAwaiter.OnCompleted(continuation);
}
private TaskAwaiter<string> ProcessUrlAwaitable(WebBrowser webBrowser, string url)
{
TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
{
// TODO: put custom processing of document here
taskCompletionSource.SetResult(e.Url + ": " + webBrowser.Document.Title);
});
webBrowser.DocumentCompleted += handler;
taskCompletionSource.Task.ContinueWith(s => { webBrowser.DocumentCompleted -= handler; });
webBrowser.Navigate(url);
return taskCompletionSource.Task.GetAwaiter();
}
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ProcessUrlsAsync(new[] { "http://google.com", "http://microsoft.com", "http://yahoo.com" })
.Start();
}
private Task ProcessUrlsAsync(string[] urls)
{
return new Task(() =>
{
foreach (string url in urls)
{
var awaiter = new WebBrowserAwaiter(webBrowser1, url);
string result = awaiter.GetResult();
MessageBox.Show(result);
}
});
}
}
}
Hope this helps.
Instead of using wbCourseOverview_Navigated use webBrowser1_DocumentCompleted when fist URL load completed done your job and go to next url
List<string> urls = new List<string>();
int count = 0;
public Form1()
{
InitializeComponent();
webBrowser1.DocumentCompleted+=new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
}
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate(urls[count++]);
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//Do something
webBrowser1.Navigate(urls[count++]);
}

Categories