MessageBox.Show sporadically don't show on catching exception - c#

I am writing a Visual Studio extension in C# and I get a strange behavior on managing exception and displaying error messages. Basically, I just want to add some details to the exception message to help me investigate in case of a problem.
It all starts from a command on a context menu item and I suspect it may be related to threads management behind the async/await mechanism. But I am not sure I guess correctly and I am not able to find any solution. HELP!
It starts from my menu item callback:
internal sealed class My_RunAnalysis
{
//...
public static async Task InitializeAsync(AsyncPackage package)
{
// Switch to the main thread - the call to AddCommand in PS_RunAnalysis's constructor requires
// the UI thread.
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
OleMenuCommandService commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService;
Instance = new My_RunAnalysis(package, commandService);
}
//...
private async void ExecuteAsync(object sender, EventArgs e)
{
try
{
await My_ViewModel.RunAnalysisAsync();
}
catch (Exception exc)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
MessageBox.Show(exc.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
//...
class My_ViewModel
{
async public static Task RunAnalysisAsync()
{
await My_Model.GetResultsListAsync();
}
}
//...
class My_Model
async public static Task GetResultsListAsync()
{
ResultsList = new My_ResultsList();
var rawResultsList = await QueryServerAsync<RawResultsListResponse>("GET", My_Request.GetResults());
//...
}
async public static Task<JsonResponse> QueryServerAsync<JsonResponse>(string method,
string request)
{
try
{
HttpResponseMessage response;
switch (method)
{
case "GET":
response = await _httpClient.GetAsync(request);
break;
case "POST":
default:
StringContent httpContent = new StringContent("", Encoding.UTF8, "application/json");
response = await _httpClient.PostAsync(request, httpContent);
break;
}
if (!response.IsSuccessStatusCode) //<<<<<<CASE #1
{
throw new My_Exception(
response.ReasonPhrase,
"Exception while querying server for " + request);
}
string serializedJson = await response.Content.ReadAsStringAsync();
// CASE #2>>>>>
var jsonResponse = serializer.Deserialize<JsonResponse>(serializedJson);
return jsonResponse;
}
catch (Exception e)
{
throw new My_Exception(
e.Message,
"Exception while querying server for " + request);
}
}
The strange thing is that:
When an error occurs in case #1 and I create a custom exception (my server responded but there was an internal error and I have a clean error code), the MessageBox in the catch of My_ViewModel::RunAnalysisAsync() will show correctly and immediately.
When a native exception occurs in case #2 (my server responded with malformed json and I get an exception from serializer.Deserialize), the MessageBox in the catch of My_ViewModel::RunAnalysisAsync() will not show, the IDE will hang for around 15s before restarting (and still not show the MessageBox).
Any idea what's wrong?
Thanks!
EDIT:
Seeing that the template for my custom command initializes also with SwitchToMainThreadAsync, I have tried to do the same with the Execute method. I updated the code above but it still does not work: an exception thrown by serializer.Deserialize will still freeze the UI for 10 to 15s and the MessageBox will not show!
Also note that the debugger can step immediately on "await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);" and go on next step to MessageBox. I would tend to suppose it means that the switch to the main thread is immediate but there is still something wrong...
Any idea what's wrong? I really need to capture exceptions a reliable way...

I could not find any explanation to the MessageBox working on a case and not on the other one. I ended up going to some log solution using FileStream.WriteAsync. Hence everything keeps async and I don't have to use MessageBox anymore.

use await JoinableTaskFactory.SwitchToMainThreadAsync(); to switch to the main thread JoinableTaskFactory is a member of AsyncPackage.
If it still doesn't work try
public static void ShowMessageBox(string title, string text)
{
Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
IVsUIShell uiShell = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShell)) as IVsUIShell;
Guid clsid = Guid.Empty;
int result;
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
0,
ref clsid,
title,
text,
string.Empty,
0,
OLEMSGBUTTON.OLEMSGBUTTON_OK,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
OLEMSGICON.OLEMSGICON_INFO,
0, // false
out result));
}

Related

I am not getting any output and i don't know why

using System;
using System.Text;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
YourClient client = new YourClient();
client.Put();
}
public class YourClient
{
private readonly HttpClient _client;
public YourClient()
{
_client = new HttpClient();
}
public async Task Put() // must be async
{
using (var request = new HttpRequestMessage(HttpMethod.Put, "https://api.minecraftservices.com/minecraft/profile/name/egg"))
{
request.Headers.Add("Authorization", "Bearer token");
request.Content = new StringContent("body", Encoding.UTF8, "content-type");
using (var response = await _client.SendAsync(request))
{
var data = await response.Content.ReadAsStringAsync();
var code = response.StatusCode;
Console.WriteLine(Convert.ToString(code));
// do something with data
}
}
}
}
}
}
I'm not getting any output and I don't know why. I'm trying to print the response code of the request but nothing is output, is it to do with my method?
I have tried printing hi after Client.Put() and it was printed, so I know that my code is actually running, I just don't know why it isn't printing the status code ...
The excellent comment by Prolog points out one of two issues. If your Console app is built on < C# 7.1 you will need a workaround to prevent the app from exiting (before the request has time to process) so in this case add Console.ReadKey() as the very last line. This will spin the message loop until you hit a key. But this is not the main issue and I would like to offer a couple of debugging tips.
The big issue is this:
If I run your code, your http request is failing and is throwing a System.FormatException
Usually this type of exception is not set to Break when Thrown. (You can verify this by looking in the Exception Settings window.) Unfortunately, this is giving you a silent failure in this case, so you must take matters into your own hands to observe it.
Suggestions for debugging your code
Use a try-catch block around any code that has any likelihood of failing.
Use System.Diagnostics.Debug.Assert which will cause your program to break on a line if any condition expression evaluates to false (but only when you're running in Debug mode not Release mode).
Add output statements to trace execution. Using Debug.WriteLine will send messages to the Output window (but again, only in Debug mode). Alternatively, since we have a Console app here, I'm using the main app window to output trace statements.
Example using 1-3:
public async Task Put() // must be async
{
Console.WriteLine("Begin Put()");
try
{
using (var request = new HttpRequestMessage(HttpMethod.Put, "https://api.minecraftservices.com/minecraft/profile/name/egg"))
{
request.Headers.Add("Authorization", "Bearer token");
request.Content = new StringContent("body", Encoding.UTF8, "content-type");
using (var response = await _client.SendAsync(request))
{
var data = await response.Content.ReadAsStringAsync();
var code = response.StatusCode;
Console.WriteLine(Convert.ToString(code));
// do something with data
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Assert(condition: false, message: ex.Message);
}
Console.WriteLine("End Put()");
}
Now, if I run the code it will break and show what the problem is.
Use the Exception Settings window to turn on all exceptions (if in doubt). Now the code will break on the exact line that is the problem.
Verify that you are Setting Authorization Header of HttpClient correctly as this may be part of the root cause of the exception.
Finally, if you continue after the Debug.Assert you will see the following text in your console which will confirm whether your Put method has had a chance to complete or not.
Hope these suggestions help you solve this problem and future ones!
// This workaround for C# versions below 7.1 attempts to
// mimic an `async Main` method.
static void Main(string[] args)
{
RunAsync();
Console.ReadKey();
}
private static async void RunAsync()
{
YourClient client = new YourClient();
// Put() should be awaited inside an async method
await client.Put();
}

Visual Studio This application is in break mode

I am doing this:
public async Task Update(Entity input)
{
ValidateUpdate(input);
await UpdateAsync(input);
}
public async void ValidateUpdate()
{
// Some Logic
int adminCount = await _dbContext.AdminEntities
.CountAsync();
if(adminCount == 0)
{
throw new AppUserException("The tenant must always have at least one admin.");
}
}
And when I hit the exception visual studio shows it to me, but then when I click on "continue execution" VS shows me a page "The application is in break mode". If I click again on "continue execution" VS just stops running the application.
When I throw the same exception in other parts of my app it simply gets returned as response of my HTTP request. I don't understand why.
I found the issue, it is because I made my method async without changing the void return type to Task and I didn't await the validation.
This is the fixed example:
public async Task Update(Entity input)
{
await ValidateUpdateAsync(input);
await UpdateAsync(input);
}
public async Task ValidateUpdateAsync()
{
// Some Logic
int adminCount = await _dbContext.AdminEntities
.CountAsync();
if(adminCount == 0)
{
throw new AppUserException("The tenant must always have at least one admin.");
}
}
I was in a rush and forgot to check the basics. I hope this will at least help someone else or my future self when I inevitably make the same mistake again.

taskcanceledexception a task was canceled

I am receiving error
taskcanceledexception a task was canceled without any inner exception details, and I am not receiving taskcanceled exception in Sentry. How can I see what the stack trace for this exception is or what changes I need to make to the code ?
Thanks
private T CallDiffbotAndDeserialise<T>(string baseUrl, string pageUrl, int maxTags, int minimumTagConfidencePercentage)
{
var client = diffBotConnection.GetClient();
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
HttpResponseMessage response = client.GetAsync($"?token={settings.DiffBotToken}&maxTags={maxTags}&tagConfidence={minimumTagConfidencePercentage / 100}&url={Uri.EscapeDataString(pageUrl)}&ts={DateTime.Now.ToSafeCacheString()}").Result;
string responseString = response.Content.ReadAsStringAsync().Result;
T diffBotResponse = JsonConvert.DeserializeObject<T>(responseString);
return diffBotResponse;
}
catch (AggregateException e) // If the task is cancelled or times out
{
return default(T);
};
}
API connection:
public abstract class APIConnection : IDisposable
{
protected HttpClient Client;
private bool disposed = false;
protected APIConnection() : this(3000) { }
protected APIConnection(int timeOut)
{
Client = new HttpClient()
{
Timeout = TimeSpan.FromMilliseconds(timeOut)
};
}
public HttpClient GetClient() => Client;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
Client.Dispose();
}
disposed = true;
}
}
You are calling .Result which always throws AggregateException.
That means you are not only catching TaskCancelledException or OperationCancelledException, you'll catch anything thrown by the two calls to .Result.
Since you are handling the exception and hiding the fact it ever happened (by catch and returning) Sentry won't know about it. If you want to send that event to Sentry, you'd need to call the Sentry client manually.
With SharpRaven:
var ravenClient = new RavenClient("dsn"); // Initialize the client
ravenClient.CaptureEvent(new SentryEvent(exception));
With the new SDK Sentry is developing (which is still a preview release):
// Initialize the SDK only once, at the start of the app
using (SentrySdk.Init("dsn"))
{
SentrySdk.AddBreadcrumb($"Starting a web request to: {baseUrl}");
try
{
// make request
}
catch (Exception e)
{
SentrySdk.CaptureException(exception);
}
}
In this example I added a breadcrumb which in case of an event (for example capturing an exception explicitly like above) is sent together with the event.
Also note that the new SDK automatically detects exceptions that go unhandled. That is not the case of your exemple since you are explicitly catching it.
I think it's important to mention that ideally you would avoid blocking the thread by calling .Result and instead would use async/await.
The await keyword unwraps the Exception from the faulted Task.
That means that your catch block could now catch OperationCancelledException instead. Any other error like a total failure to connect to the server would not go into your catch block and instead would bubble up the stack.

Handling exceptions in xamarin

I've encountered a few dificulties trying to handle some errors in the best way possible. One of my cases for example is NullReferenceException.
To be more clear let me explain it in a few words. When I make a call to a server to receive some pieces of information, in some cases the server might have some problems and it will return of course null.
What I've done is I display an alert to let the user know that he can try later again. After this I try to send him in the previous page for example. And after all this my app still crushes.
What I'd like to do is to simply display the alert and then let the user stay in the same page without the application crushing.
These are a few pieces of my code:
tasks.cs
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
await Application.Current.MainPage.Navigation.PopAsync(); //actually I would like to stay in the same page
}
}
return ideas;
}
view.xaml.cs
private async void Button_Clicked(object sender, EventArgs e)
{
Tasks ts = new Tasks();
var ideas = await ts.GetIdeasAsync();
if (ideas == null)
{
Debug.WriteLine("hello");
//do nothing since the display alert is already shown
}
else
{
//code here
}
I would really appreciate if anyone can guide me to a "best-practice" approach. Thanks :)
You are declaring ideas in the try block, and then trying to access it in the catch block, where it is out of scope. (Visual Studio should give an Intellisense error)
Also, whenever manipulating the UI, you should always do it on the main thread. so move your DisplayAlert() code into
Device.BeginInvokeOnMainThread(async () =>
{
// await DisplayAlert(); move it into here
});
In addition, any PopAsync or PushAsync calls should also be done on the main UI thread. But calling PopAsync after an asynchronous call to an API not be a good idea, as the user may have already pressed the back button by the time the call returns.
As for the NullReferenceException, check to see if json is null before passing it to the DeserializeObject() function.
The problem it was pretty obvious actually since I after catch-ing the exception I would later continue with the code. So what I did was :
public async Task<List<Idea>> GetIdeaAsync(string accesToken)
{
List<Idea> ideas = null;
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesToken);
var json = await client.GetStringAsync("http://www.getdata.de/api/ideas/");
var ideas = JsonConvert.DeserializeObject<List<Idea>>(json);
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Server Error", "There has been an server error. Please try later.", "OK");
if (ideas == null)
{
//actually I would like to stay in the same page
return null; //-- added this line
}
}
return ideas;
}
Maybe it's not the best idea but it's working for me. Any other approach would be highly appreciated. :)

Exception Thrown from Thread Pool Thread shown as Unhandled

I am firing off a Task of on a background thread pool thread via the following method
private async Task LoadCoreMatchDataAsync()
{
string errorMessage = String.Empty;
...
try
{
if (!HasConnection)
return;
IProgress<string> progressIndicator = new Progress<string>(LoadProgress);
EventsCollection = new BindableCollection<Taurus.MatchDetails>(
await MatchDataService.GetCollectionAsync(
this.client, progressIndicator, this.token));
...
}
catch (TimeoutException toe)
{
errorMessage = String.Format(
"Retrieval of the MatchDetails using connection " +
"\"{0}\" failed with the following TimeoutException: \"{1}\".",
this.ConnectionString,
toe.Message);
}
catch (OperationCanceledException)
{
// Do nothing, cancel silently.
}
// Display any errors.
if (!String.IsNullOrEmpty(errorMessage))
{
await dialogManager.ShowDialog<MessageDialogResult>(
new MessageBoxViewModel("Connection Timeout", errorMessage, mbs));
HasConnection = false;
}
}
where the GetCollectionAsync(...) method is
public async Task<BindableCollection<Taurus.MatchDetails>> GetCollectionAsync(
MongoClient client, IProgress<string> progressIndicator, CancellationToken token)
{
return await Task.Factory.StartNew<BindableCollection<Taurus.MatchDetails>>(() =>
{
... // Somewhere in here we get a TimeoutException thrown.
}, token);
}
the problem is that when in my call to await MatchDataService.GetCollectionAsync(...) I get an expected TimeoutException, VS2012 throws "a TimeoutException was unhandled by user code" message, when clearly I am handling the exception in the "continuation" in the correct way. If I continue rather than break, the exception is indeed caught and I get my expected error message. I am just not sure why VS2012 is telling me that the exception is unahandled?
I am essentially doing what is described clearly in on of Jon Skeets answers here https://stackoverflow.com/a/19865613/626442.
Thanks for your time.
You have "Just My Code" turned on (which I mentioned in my answer in the other question you reference). The debugger is indicating that "User code" (Your code) did not handle the exception--which is true. The exception is caught by the framework code and placed into a Task.
Turn off the "Just My Code" debugger setting (in my opinion, it is a feature that only causes confusion and has very limited usefulness).

Categories

Resources