Asynchronous methods and a event - c#

I use awesomium to automate a site. I tried to use asynchronous programming, because I didn't want my GUI to freeze, but I have a problem at a event (a popup appear and I want to make some actions in this popup until I close it) when the application doesn't continue how I want. After the event is fired I want my application to continue with the event method (webc_ShowCreatedWebView and after with popupTwitter(method), but I find that when executing JavaScript code, the control returns in the While from the first method. How could I do that after the earnpoints method is called and the events is fired to finish the event and the method and after that the control return in the while.
private async void button4_Click(object sender, EventArgs e)
{
Twitter twitter = new Twitter(webView);
twitter.Login(webView);
webView.ShowCreatedWebView += webc_ShowCreatedWebView;
addmefast.Login(webView);
int i = 0;
while (i < 10)
{
Task earnpoints = EarnPoints(webView);
await earnpoints;
//Here i don't want to continue until EarnPoints method > webc_ShowCreatedWebView event > popupTwitter method it's finished.
i++;
}
}
public async Task EarnPoints(IWebView web)
{
web.Source = "http://addmefast.com/free_points/twitter".ToUri();
await Task.Delay(3000);
web.ExecuteJavascript("document.getElementsByClassName('single_like_button btn3-wrap')[0].click();"); //event fired: webc_ShowCreatedWebView
}
async void webc_ShowCreatedWebView(object sender, ShowCreatedWebViewEventArgs e)
{
WebView view = new WebView(e.NewViewInstance);
await popupTwitter(view);
}
async Task popupTwitter(WebView view)
{
Popupform FormTwitter = new Popupform(view);
FormTwitter.Show();
await Task.Delay(6000);
FormTwitter.Twitter();
await Task.Delay(2000);
FormTwitter.Close();
await Task.Delay(4000);
}

I also had problems implementing async methods with awesomium, but got it working.
First I made this wrapper. Have to be created on the main thread.
public class AsyncWebView
{
public static SynchronizationContext _synchronizationContext;
private readonly WebView _webView;
public AsyncWebView()
{
_synchronizationContext = SynchronizationContext.Current;
_webView = WebCore.CreateWebView(1024, 900);
}
public async Task Navigate(String url)
{
Debug.WriteLine("Navigating");
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
FrameEventHandler handler = (sender, args) =>
{
Debug.WriteLine(args.Url);
if (!_webView.IsNavigating && !_webView.IsLoading)
tcs.SetResult(true);
};
_webView.LoadingFrameComplete += handler;
_synchronizationContext.Send(SetWebViewSource, url);
await tcs.Task;
_webView.LoadingFrameComplete -= handler;
Debug.WriteLine("Done");
}
private void SetWebViewSource(object url)
{
_webView.Source = new Uri((string)url);
}
}
Usage:
async Task test()
{
await webView.Navigate("http://www.nytimes.com");
Debug.WriteLine("All done");
}
Just make sure you have a SynchronizationContext where the AsyncWebView constructor is called from.

Related

Cefsharp Shutdown incorrect

Exception: Cef.Shutdown must be called on the same thread that Cef.Initialize was called - typically your UI thread. If you called Cef.Initialize on a Thread other than the UI thread then you will need to call Cef.Shutdown on the same thread. Cef.Initialize was called on ManagedThreadId: 1where Cef.Shutdown is being called on ManagedThreadId: 4
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
}
private static async Task MainAsync()
{
List<string> urls = new List<string>();
urls.Add("https://google.com");
CefSharpWrapper wrapper = new CefSharpWrapper();
wrapper.InitializeBrowser();
foreach (string url in urls)
{
await wrapper.GetResultAfterPageLoad(url);
}
wrapper.ShutdownBrowser();
}
}
public sealed class CefSharpWrapper
{
private ChromiumWebBrowser _browser;
public void InitializeBrowser()
{
Cef.Initialize(new CefSettings());
_browser = new ChromiumWebBrowser();
AutoResetEvent waitHandleOnBrowserInitialized = new AutoResetEvent(false);
EventHandler onBrowserInitialized = null;
onBrowserInitialized = async (sender, e) =>
{
_browser.BrowserInitialized -= onBrowserInitialized;
waitHandleOnBrowserInitialized.Set();
};
_browser.BrowserInitialized += onBrowserInitialized;
waitHandleOnBrowserInitialized.WaitOne();
}
public Task<bool> GetResultAfterPageLoad(string pageUrl)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
EventHandler<LoadingStateChangedEventArgs> onPageLoaded = null;
// An event that is fired when the first page is finished loading.
// This returns to us from another thread.
onPageLoaded = async (sender, args) =>
{
// Check to see if loading is complete - this event is called twice, one when loading starts
// second time when it's finished
// (rather than an iframe within the main frame).
if (!args.IsLoading)
{
// Remove the load event handler, because we only want one snapshot of the initial page.
_browser.LoadingStateChanged -= onPageLoaded;
tcs.SetResult(true);
}
};
_browser.LoadingStateChanged += onPageLoaded;
_browser.Load(pageUrl);
return tcs.Task;
}
public void ShutdownBrowser()
{
// Clean up Chromium objects. You need to call this in your application otherwise
// you will get a crash when closing.
Cef.Shutdown();
}
}
Amaitland, thanks for the reply.
class Program
{
static void Main(string[] args)
{
CefSharpWrapper.InitializeBrowser();
MainAsync().Wait();
CefSharpWrapper.ShutdownBrowser();
}
private static async Task MainAsync()
{
List<string> urls = new List<string>();
urls.Add("https://google.com");
CefSharpWrapper wrapper = new CefSharpWrapper();
foreach (string url in urls)
{
await wrapper.GetResultAfterPageLoad(url);
}
}
}
public sealed class CefSharpWrapper
{
private static ChromiumWebBrowser _browser;
public static void InitializeBrowser()
{
Cef.Initialize(new CefSettings());
_browser = new ChromiumWebBrowser();
AutoResetEvent waitHandleOnBrowserInitialized = new AutoResetEvent(false);
EventHandler onBrowserInitialized = null;
onBrowserInitialized = async (sender, e) =>
{
_browser.BrowserInitialized -= onBrowserInitialized;
waitHandleOnBrowserInitialized.Set();
};
_browser.BrowserInitialized += onBrowserInitialized;
waitHandleOnBrowserInitialized.WaitOne();
}
public Task<bool> GetResultAfterPageLoad(string pageUrl)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
EventHandler<LoadingStateChangedEventArgs> onPageLoaded = null;
// An event that is fired when the first page is finished loading.
// This returns to us from another thread.
onPageLoaded = async (sender, args) =>
{
// Check to see if loading is complete - this event is called twice, one when loading starts
// second time when it's finished
// (rather than an iframe within the main frame).
if (!args.IsLoading)
{
// Remove the load event handler, because we only want one snapshot of the initial page.
_browser.LoadingStateChanged -= onPageLoaded;
tcs.SetResult(true);
}
};
_browser.LoadingStateChanged += onPageLoaded;
_browser.Load(pageUrl);
return tcs.Task;
}
public static void ShutdownBrowser()
{
// Clean up Chromium objects. You need to call this in your application otherwise
// you will get a crash when closing.
Cef.Shutdown();
}
}

C# WPF program button click run a task until another button click stop or until cancel token valid

I am creating a WPF app where I want to have a global bool im assuming, on the first button click I’ll set this bool to true and I want it to run a task (continuously call an API method) until I click the button again and it stops it. What would be the best way to do this?
private bool running = false;
private async void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
if (!running)
{
running = true;
}
else
running = false;
if (running)
{
RunningNrunnin(running);
//tradeClient.GetTradeHistory();
}
}
public void RunningNrunnin(bool running)
{
if (running)
{
Task task = new Task(() =>
{
while (running)
{
GetTradeHistory();
Thread.Sleep(2000);
}
});
task.Start();
}
}
Added Below
I would like to call a method over and over until the user creates a cancel request on a thread in the background. I currently had it so I can call a action (a counter) and update the GUI each second but when I try to do this same thing with a method call it executes only once.
// Here is the method I want to call continously until canceled
private async void HistoryTest()
{
cancellationToken = new CancellationTokenSource();
task = Task.Factory.StartNew(async () =>
{
while (true)
{
cancellationToken.Token.ThrowIfCancellationRequested();
await Client2.GetHistory();
await Task.Delay(2000);
}
}, cancellationToken.Token);
}
public async Task GetHistory()
{
try
{
var response = await Client.Service.GetDataAsync
(
ProductType.BtcUsd,
5,
1
);
}
catch(Exception)
{
throw;
}
}
I made a little console test app to test this so I had to change the method signatures (static) and can't use ButtonClick on a console. I simulated the button click by putting as sleep between the programatic "button click".
This might get you started.
private static bool isRunning = false;
private static int clickCounter = 0;
private static int iterationsCounter = 0;
static void Main(string[] args)
{
Console.WriteLine(“Start”);
for(int i = 0; i < 7; i++)
{
BtnTrade1_Buy_Click();
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine(“END”);
}
private static async Task BtnTrade1_Buy_Click()
{
iterationsCounter = 0;
isRunning = !isRunning;
Console.WriteLine($"Ha: {isRunning} {clickCounter++}");
await RunningNrunnin();
}
private static async Task RunningNrunnin()
{
await Task.Run(() => Runit());
}
private static void Runit()
{
while (isRunning)
{
GetTradeHistory();
System.Threading.Thread.Sleep(100);
}
}
private static void GetTradeHistory()
{
Console.WriteLine($"Hello Test {iterationsCounter++}");
}
Of course you wouldn't need all the counters and the Console.WriteLine() stuff. They are there to allow you to visualize what is happening.
Let me know if you need more info.
You don't need to do anything else inside the BtnTrade1_Buy_Click event handler, beyond toggling the isRunning field:
private bool _isRunning;
private void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
_isRunning = !_isRunning;
}
The Task that is getting the trade history in a loop, needs to be started only once. You could start it in the Window_Loaded event. Storing the Task in a private field is a good idea, in case you decide to await it at some point, but if you are handling the exceptions inside the task it's not necessary.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_ = StartTradeHistoryLoopAsync(); // Fire and forget
}
private async Task StartTradeHistoryLoopAsync()
{
while (true)
{
var delayTask = Task.Delay(2000);
if (_isRunning)
{
try
{
await Task.Run(() => GetTradeHistory()); // Run in the ThreadPool
//GetTradeHistory(); // Alternative: Run in the UI thread
}
catch (Exception ex)
{
// Handle the exception
}
}
await delayTask;
}
}
Don't forget to stop the task when the window is closed.
private void Window_Closed(object sender, EventArgs e)
{
_isRunning = false;
}
This will stop the calls to GetTradeHistory(), but will not stop the loop. You may need to add one more private bool field to control the loop itself:
while (_alive) // Instead of while (true)

What is the best patter to call async methods after app start? (Fire&Forget in Constructor?)

In my application I want to setup my ViewModels by queried data (from async methods) right after my application started.
In my case I have a .NET Framework WPF application and after the start I want to begin loading the data by async methods. After awaiting the data I want to update my view models.
What is the best way and the best entry point to do this?
My first approach is the following:
public App()
{
// Do synchronous initializing stuff
// Load data and initialize by viewmodel with it
#pragma warning disable CS4014
ProgramStateViewModel.Instance.LoadDataAndSetupAsync();
#pragma warning restore CS4014
}
The problem with this is I got the compiler warning and I have to handle it by suppressing it.
Actually I want a Fire-and-Forget call, but the method I am calling is async and returns a Task.
How to do it better?
You can attach an async Startup event handler:
public App()
{
Startup += async (s, e) =>
{
await ProgramStateViewModel.Instance.LoadDataAndSetupAsync();
};
}
Try the following, using discards:
public App()
{
// Do synchronous initializing stuff
// Load data and initialize by viewmodel with it
_ = ProgramStateViewModel.Instance.LoadDataAndSetupAsync();
}
By using discards, you can return the Task and you don't declare anything else that will not be used.
Be aware that even without the warning the dangers still apply.
Especially if you have an error in your background work which well not be caught. To avoid this situation, please check another SO question here on managing exceptions on the separate thread.
A possibility is to use the OnStartup function in the App.xaml.cs file. For the asynchronous call, various methods exist, following some methods to make an asynchronous call.
Using BackgroundWorker
public partial class App : Application
{
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
BackgroundWorker _backgroundWorker;
if (_backgroundWorker != null)
{
_backgroundWorker.CancelAsync();
}
_backgroundWorker = CreateBackgroundWorker();
_backgroundWorker.RunWorkerAsync();
}
}
private BackgroundWorker CreateBackgroundWorker()
{
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += _backgroundWorker_DoWork;
bw.ProgressChanged += _backgroundWorker_ProgressChanged;
bw.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
return bw;
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
}
private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
Using Thread
Method 1
myThread = new Thread(() => CustomTaskAsync());
myThread.Start();
private void CustomTaskAsync()
{
...............
}
Method 2
Thread thread = new Thread(new ThreadStart(CustomTaskAsync));
thread.Start();
public void CustomTaskAsync()
{
..................
}
Method 3
Thread thread = new Thread(CustomTaskAsync);
thread.Start();
public void CustomTaskAsync()
{
...............
}
Using Task
Method 1
bool result ;
Task<bool> task = Task.Run<bool>(async () => await CustomTaskAsync());
str = task.Result;
public async Task<bool> CustomTaskAsync()
{
bool result;
result= await Task.Factory.StartNew(() => CustomTask());
return result;
}
private bool CustomTask()
{
bool result;
return result;
}
Method 2
CustomTaskAsync().Wait();
public async Task CustomTaskAsync()
{
await Task.Run(() => {
.................
});
}

Close the form only after task has been completed

I have a cleanup task that deletes a big file on exit.
private async Task DoCleanup()
{
await Task.Run(() =>
{
File.Delete(FilePath);
});
}
Now I want to await that task on exit (FormClosing event), but the form closes before the file was entirely deleted.
I've tried to cancel the event and exit the application manually, like this:
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = true;
await DoCleanup();
Application.Exit();
}
But the Task is called over and over again (like an infinite loop). What should I do?
You can make an event that calls a function and bind the event to Application.Close() when the Task completes.
public async static Task Main(string[] args)
{
YourClassName classInstance = new YourClassName();
// Bind the event to something
classInstance .CompletedTaskEvent += (s, e) => Console.WriteLine("Completed work");
// Start the work
await classInstance.DoCleanup();
Console.ReadLine();
}
public class YourClassName
{
// Some event
public event EventHandler CompletedTaskEvent;
public async Task DoCleanup()
{
await Task.Run(async () =>
{
await Task.Delay(2500);
// When the task completes invoke the event
// And pass the current class instance to the sender
// And you can add any kind of event argument you want
// however I recommend you make the event generic and then pass the argument type
CompletedTaskEvent?.Invoke(this, eventArguments);
});
}
}

DeadLock on task.Wait() with Task which edit UI

I'm trying to find some solutions to my problem here, but with no result (or I just do not get them right) so if anyone could help / explain i will be really gratefull.
I'm just developing a tool for system administrators using Win Form and now I need to create a continuous ping on the selected machine which is running on the background. There is an indicator for Online status on UI which I need to edit with background ping. So right now I'm in this state:
Class A (Win form):
ClassB activeRelation = new ClassB();
public void UpdateOnline(Relation pingedRelation)
{
//There is many Relations at one time, but form shows Info only for one...
if (activeRelation == pingedRelation)
{
if (p_Online.InvokeRequired)
{
p_Online.Invoke(new Action(() =>
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure
));
}
else
{
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure;
}
}
}
//Button for tunring On/Off the background ping for current machine
private void Btn_PingOnOff_Click(object sender, EventArgs e)
{
Button btn = (sender is Button) ? sender as Button : null;
if (btn != null)
{
if (activeRelation.PingRunning)
{
activeRelation.StopPing();
btn.Image = Properties.Resources.Switch_Off;
}
else
{
activeRelation.StartPing(UpdateOnline);
btn.Image = Properties.Resources.Switch_On;
}
}
}
Class B (class thats represent relation to some machine)
private ClassC pinger;
public void StartPing(Action<Relation> action)
{
pinger = new ClassC(this);
pinger.PingStatusUpdate += action;
pinger.Start();
}
public void StopPing()
{
if (pinger != null)
{
pinger.Stop();
pinger = null;
}
}
Class C (background ping class)
private bool running = false;
private ClassB classb;
private Task ping;
private CancellationTokenSource tokenSource;
public event Action<ClassB> PingStatusUpdate;
public ClassC(ClassB classB)
{
this.classB = classB;
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ping = PingAction(token);
running = true;
}
public void Stop()
{
if (running)
{
tokenSource.Cancel();
ping.Wait(); //And there is a problem -> DeadLock
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
private async Task PingAction(CancellationToken ct)
{
bool previousResult = RemoteTasks.Ping(classB.Name);
PingStatusUpdate?.Invoke(classB);
while (!ct.IsCancellationRequested)
{
await Task.Delay(pingInterval);
bool newResult = RemoteTasks.Ping(classB.Name);
if (newResult != previousResult)
{
previousResult = newResult;
PingStatusUpdate?.Invoke(classB);
}
}
}
So the problem is in deadlock when I cancel token and Wait() for task to complete -> it's still running, but While(...) in task is finished right.
You have a deadlock because ping.Wait(); blocks UI thread.
You should wait for task asynchronously using await.
So, if Stop() is event handler then change it to:
public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
If it is not:
public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
As mentioned by #JohnB async methods should have Async suffix so, the method should be named as StopAsync().
Similar problem and solution are explained here - Do Not Block On Async Code
You should avoid synchronous waiting on tasks, so you should always use await with tasks instead of Wait() or Result. Also, as pointed by #Fildor you should use async-await all the way to avoid such situations.

Categories

Resources