C# WPF - ApplicationExit calling an API - c#

I have to call an API when my WPF Applications is being closed.
I'm using Application_Exit event because I have to do this even closing all the windows, being shut down from Task Manager or Alt + F4.
I've tried several ways, but all the options has some issues (some of them are these):
OPTION A: Async event + await
private async void Application_Exit(object sender, ExitEventArgs e)
{
if (MonitorInstance.Instance.User != null)
{
Log.Information($"Disconnect starting");
var result = await _apiClient.Disconnect();
Log.Information($"Disconnect result: {result}");
}
}
This option just calls the HTTP CONNECT (which sometimes succeed and others is being cancelled) but not my PATCH request.
OPTION B: Blocking Thread with Result of Task.
private void Application_Exit(object sender, ExitEventArgs e)
{
if (MonitorInstance.Instance.User != null)
{
Log.Information($"Disconnect starting");
var result = _apiClient.Disconnect().Result;
Log.Information($"Disconnect result: {result}");
}
}
This effectively calls the API but the process continues running. Visual Studio continues showing that the App is running and this is not desired.
I think it's worth to mention that I'm calling the API (which uses SSL) using HttpClient which is being created in the Disconnect method and I'm using .NET Core 3.1.
My questions:
Is Application_Exit the proper event to do this?
How to program the Application_Exit event to fulfill all the scenarios?
Should I change the implementation for my call to do it directly without the HttpClient?

This a classic case of deadlock. Your code is running under a synchronization context, your await calls inside of _apiClient.Disconnect() try to resume on the UI thread, but the UI thread is blocked waiting synchronously on the task because of .Result.
There are multiple ways to get around that, but for your precise case I don't think it's useful to get fancy. Just use Task.Run to run your call in a safer context:
private void Application_Exit(object sender, ExitEventArgs e)
{
if (MonitorInstance.Instance.User != null)
{
Log.Information($"Disconnect starting");
var result = Task.Run(apiClient.Disconnect).Result;
Log.Information($"Disconnect result: {result}");
}
}

Related

ACR loading and ZXing handle result

In a very simple Xamarin android APP I scan a barcode with ZXing and then handle the result. The handle results takes a few seconds to process and during I want to show an ACR showLoading, but the activity indicator hides almost instantly when the scanner/camera is closed.
How can I keep the show loading open while it is handling the result?
Cleaned code:
async void onAddByScan(object sender, EventArgs e)
{
var options = new ZXing.Mobile.MobileBarcodeScanningOptions();
var scanner = new MobileBarcodeScanner();
ZXing.Result result = await scanner.Scan(options);
using (UserDialogs.Instance.Loading())
{
await HandleResultAsync(result);
}
}
async Task HandleResultAsync(ZXing.Result result)
{
//do something with the results
}
You could use InvokeOnMainThreadAsync method in the Xamarin.Essentials: MainThread class. The idea of this method is to not only execute a code on the UI/main thread, but to also to await it's code. This way you can have both async/await logic and main thread execution.
You could refer to the similar case https://stackoverflow.com/a/67483594/10768653.

Wait for async task on application exit from UI thread

I'm writing an UI app. If some exception happens I need to do some work before application exit.
So I subscribed to AppDomain.CurrentDomain.UnhandledException event.
AppDomain.CurrentDomain.UnhandledException += HandleException;
HandleException method performs async saving to remote(because there are no sync api).
private void HandleException(object sender, UnhandledExceptionEventArgs args)
{
foreach (var user in UsersCollection.ToArray())
{
try
{
foreach (var session in user.Sessions.ToArray())
{
try
{
SaveSessionAsync(session).Wait();
}
catch (Exception e)
{
Logger.Error("Can't save session: " + session, e);
}
}
}
catch (Exception e)
{
Logger.Error("Can't save sessions of user " + user, e);
}
}
}
Before application exit I need to be sure that I saved all sessions(tried, at least). But if I put Wait() there I get a deadlock and application never stops.
As far as I know, await may help me in normal situation(when I'm in UI thread but not in app termination state), but await does not awaits on application exit. So my saving tasks may be aborted. But I need them to finish.
Is there a way of waiting for guaranteed finish of SaveSessionAsync task without creating a deadlock?
BTW: SaveSessionAsync has Parse.com API inside
You only used part of the syntax you need to run it in a non-async method. Utilizing Task.Run() will run it in an asynchronous fashion without having to use await.
Try this:
Task.Run(() => SaveSessionAsync(session)).Wait();

Forcing web request to wait for an event callback

I have a web request (HttpRequest) which triggers a third library scanning method on my server that has an event handler attached to it:
scanner.OnScanComplete += scanner_OnScanComplete;
The web request will invoke scanner.Scan(files) but how can I force (or hook) the request to wait and get the results from scanner_OnScanComplete when the scan process is complete so it can return data to clients without having to send another web request to get this data?
void DoWork(HttpRequst request, var files)
{
var scanner = new Scanner()
scanner.OnScanComplete += scanner_OnScanComplete;
scan(files)
}
void scanner_OnScanComplete(object sender, EventArgs e)
{
var scanCompleted = true;
//Return scanCompleted somehow to the DoWork thread above
}
Do you have to use a HttpHandler or can you use other api's?
If you can use MVC4 or later then you can use an asynchronous Action Method to do this easily. Look here for an example of how to use them.
In addition to using an async Action Method you may need a way to await the event from the scanner. Using a Task Completion source as in this answer may be a good way to do that.
One way to do what you want is to store the completion of the task in a boolean member.
The boolean shall be marked volatile to avoid threading issues.
The risk of the approach is to lead to timeouts on client side if the scan processing is too long.
private volatile bool _finished;
void DoWork(HttpRequst request, var files)
{
var scanner = new Scanner();
scanner.OnScanComplete += scanner_OnScanComplete;
_finished= false;
scan(files)
while (!_finished) // wait for the scan completion
System.Threading.Thread.Sleep(1000); // avoid consuming 100% cpu
var scanData = Dothescanwork();
//Return scanData somehow to the DoWork thread above
}
void scanner_OnScanComplete(object sender, EventArgs e)
{
_finished= true;
}

External Function Call Blocks UI thread

I am working on an application that talks to a motion controller over ethernet.
To connect to the controller I use a library provided by the supplier, to connect you create an instance of the controller than then tell it to connect, this has the chance to block for a few seconds (with no controllable timeout) if there is no controller present. This cause freeze ups in the UI.
To avoid this I thought I would be able to use Tasks to run the connection in a different thread.
ConnectionTask = Task.Factory.StartNew(() =>
{
try
{
RMCLink rmc = RMCLink.CreateEthernetLink(DeviceType.RMC70, "192.168.0.55");
RMC.Connect();
}
catch
{
this.logger.Log("Failed to connect");
}
}, TaskCreationOptions.LongRunning);
This has no effect whatsoever and the UI still locks up.
I think I am using them properly as if I replace it with the below code the UI is fine even though the separate thread takes a few seconds before the message comes out.
ConnectionTask = Task.Factory.StartNew(() =>
{
int x = 1;
while (x != 0) x++;
this.logger.Log("Failed to connect");
}, TaskCreationOptions.LongRunning);
Is there any way I can identify what is going on and prevent calls that I do not know anything about their inner workings from locking the UI thread.
Use async/await, something along the lines of:
public async void MyButton_Click(object sender, EventArgs e)
{
await CreateEthernetLink();
this.logger.Log("Connected!");
}
private async Task CreateEthernetLink()
{
var task = Task.Run(() => {
try
{
RMCLink rmc = RMCLink.CreateEthernetLink(DeviceType.RMC70, "192.168.0.55");
rmc.Connect();
}
catch
{
this.logger.Log("Failed to connect");
}});
await task;
}
The await will capture the current thread (or SynchronizationContext - in this case the UI thread which is being blocked) and restore it after the async work has been completed.
So the threading is all handled for you behind the scenes and you should notice no difference in your application other than the fact that your application no longer freezes when performing connections.
EDIT: I also noticed in your code your initializing rmc but calling connect on RMC. I don't think this is correct.

wait for static callback complete

I've a scenario:
MyApp calls cameraCapture
that fires a callbackFunction
after the callbackFunction (I have a photo captured) completes, I do more stuff.
So I have to wait for callbackFunction to complete before executing another function. How could i do this?
Here my code:
private static readonly Plustek_Camera.PFNCK_EVENT staticFnCamera = fnPFNCK_EVENT;
public static bool fnPFNCK_EVENT(int iEvent, int iParam, IntPtr pUserData)
{
//capture picture and save to folder
}
//I implement callback start camera and fire a callback staticFnCamera
var _status = CameraCtrl.Start(CameraCtrl.ScanMode, CameraCtrl.Resolution, CameraCtrl.ImageFormat, CameraCtrl.Alignment, staticFnCamera);
//waiting for staticFnCamera complete make sure image produced
ReadPassPortText();
If I understand correctly, you have some camera control that provides an asynchronous API to start capturing an image, but you want to wait synchronously for that operation to complete.
If so, there are lots of different ways to accomplish what you're trying to do. One such way would be to use a TaskCompletionSource:
TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
var _status = CameraCtrl.Start(CameraCtrl.ScanMode, CameraCtrl.Resolution,
CameraCtrl.ImageFormat, CameraCtrl.Alignment,
(iEvent, iParam, pUserData) =>
{
staticFnCamera(iEvent, iParam, pUserData);
source.SetResult(true);
});
//waiting for staticFnCamera complete make sure image produced
await source.Task;
ReadPassPortText();
Note that the above uses await, which is valid only in an async method. You haven't provided enough context to show exactly how that would work in your code, but I strongly recommend following the above. That will avoid blocking the currently running thread; the async method will return at that point, letting the thread continue to run, and will be resumed at the ReadPassPortText(); statement when the operation completes.
If for some reason you simply cannot use the await in your method, you can instead simply do source.Task.Wait();. This will, of course, block the currently executing thread at that statement.
The above requires .NET 4.5. There are other approaches that work with earlier versions of .NET, but you would need to be specific about your requirements to make it worth trying to describe those.
Edit:
Since you are using .NET 4.0, and presumably Visual Studio 2010, the above won't work for you "out-of-the-box". One option is to download the Async CTP for Visual Studio, which will give you the C# 5.0 compiler that would enable the above. But if that's not feasible for you, another option is to just do what the compiler would do on your behalf, by replacing the last two lines above with the following:
source.Task.ContinueWith(task => ReadPassPortText(),
TaskScheduler.FromCurrentSynchronizationContext());
That would attach the continuation delegate that call ReadPassPortText() to the Task object from the TaskCompletionSource, specifying the current synchronization context as the source of the scheduler to use to actually run the continuation.
The method would return after calling ContinueWith() (just as it would in the await version, except that here it's written out explicitly instead of the compiler doing it for you). When the Task object is set to the completed state, the previously-registered continuation will be executed.
Note that your original question isn't very clear about the context. If the code is running in the UI thread, then using FromCurrentSynchronizationContext() is important and will ensure that the continuation is executed in the UI thread as well. Otherwise, you can probably get away without specifying a scheduler in the call to ContinueWith().
This demonstrates an async-await pattern that you can use. As Peter Duniho points out in the comments, you will have to adapt the pattern to the API that you're using. Try playing with it here at this fiddle to understand how these things work.
using System;
using System.Threading.Tasks;
public class MyApp
{
public static void Main()
{
Console.WriteLine("1. MyApp calls camera capture.");
CameraCaptureAsync().Wait();
}
public async static Task CameraCaptureAsync()
{
Console.WriteLine("2. That calls callbackFunction");
var task = CallbackFunction();
Console.WriteLine("4. In the meantime.");
Console.WriteLine("5. Do some other stuff. ");
await task;
Console.WriteLine("7. Process the " + task.Result);
DoMoreStuff();
}
public async static Task<string> CallbackFunction()
{
Console.WriteLine("3. Which takes a picture.");
await Task.Delay(100);
Console.WriteLine("6. After the callback functions completes");
return "Photograph";
}
public static void DoMoreStuff()
{
Console.WriteLine("8. Do more stuff.");
}
}
After try some implement callback waiting, i try to resolve by adding another form for capturing images (frmSecond).
frmFirst call frmSecond and waiting in 5 to 7 seconds to make sure capture completed.
after that processing ReadPassPortText()
frmFirst Code:
frmReadPassport frmReadPass = new frmReadPassport();
frmReadPass.ShowDialog();
ReadPassPortText();
frmSecondCode
private CAMERACTRL CameraCtrl = null;
//Add static for call from camera start , make sure this alive
private static Plustek_Camera.PFNCK_EVENT staticFnCamera ;
public frmReadPassport()
{
InitializeComponent();
staticFnCamera = fnPFNCK_EVENT;
}
Timer formClose = new Timer();
private void frmReadPassport_Load(object sender, EventArgs e)
{
CaptureImages();
formClose.Interval = 7000; // 7 sec
formClose.Tick += new EventHandler(formClose_Tick);
formClose.Start();
}
private void formClose_Tick(object sender, EventArgs e)
{
//free camera first
// check if camera start then stop
ReleaseResourceCamera();
staticFnCamera =null;
formClose.Stop();
formClose.Tick -= new EventHandler(formClose_Tick);
this.Dispose();
this.Close();
}
private void CaptureImages()
{
CameraCtrl = new CAMERACTRL();
CameraCtrl.LoadCameraDll();
CameraCtrl.GetDeviceList();
String temp = CameraCtrl.GetParameter();
CameraCtrl.Start(CameraCtrl.ScanMode,CameraCtrl.Resolution,CameraCtrl.ImageFormat, CameraCtrl.Alignment, staticFnCamera);
}
public static bool fnPFNCK_EVENT(int iEvent, int iParam, IntPtr UserData)
{
captureImage();
return true;
}
}

Categories