I'm somewhat new to C# and I'm having some issues retrieving a textbox value in an asynchronous method. I the thread to retrieve the text input in the UI and use it in the code. VS 2010 accepts my code but when I start to debug it gives me the following exception
Invalid cross-thread access. Ideas? Am I missing something?
public void Response_Completed(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
{
JObject rootObject = JObject.Load(new JsonTextReader(streamReader));
string tracknum = trackid.Text; // Invalid cross-thread access exception
string source = rootObject[tracknum]["source"].ToString();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//removed
});
}
}
Note: I'm attempting to do this on the Windows Phone 7 Platform
You should to get textbox value on UI thread, instead of other threads. Invoke method executes the specified delegate on the UI thread:
string tracknum = (string)trackid.Invoke(new Func<string>(() => trackid.Text));
Edit:
On Windows Phone:
string tracknum = string.Empty;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
tracknum = trackid.Text;
string source = rootObject[tracknum]["source"].ToString();
});
You'll receive such exception because you have tried to access an object from a different thread than that it was created on (tried to access the thread asynchronously)
To get around this, we'll need to execute a delegate on the thread that owns the control's underlying window handle. In such cases, we may use Invoke(Delegate method)
Example
public void Response_Completed(IAsyncResult result)
{
this.Invoke((MethodInvoker)delegate
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
{
JObject rootObject = JObject.Load(new JsonTextReader(streamReader));
string tracknum = trackid.Text; // Invalid cross-thread access exception
string source = rootObject[tracknum]["source"].ToString();
}
});
}
MethodInvoker provides a simple delegate that is used to invoke a method with a void parameter list. This delegate can be used when making calls to a control's Invoke method, or when you need a simple delegate but do not want to define one yourself.
Thanks,
I hope you find this helpful :)
Related
As the title says how can I save in string the result of the executed command?
SendCommand("server.hostname");
My code:
public void SendCommand(string command)
{
PacketModel packet = new PacketModel()
{
Identifier = 1,
Message = command,
Name = "RustManager"
};
string packetString = JsonConvert.SerializeObject(packet);
_webSocket.SendAsync(packetString, null);
}
public void GetServerHostname()
{
SendCommand("server.hostname");
}
Due to my small reputation I cannot comment - which is what I would have done before that.
Normally methods that end on Async are async and return a Task<T> type.
Using the await keyword makes your method async which is why you have to mark it as async in the method head.
Link to C#-Documentation on the await keyword
It is really hard to say how to get your code running since I don't have alot of information but maybe this helps:
public async void SendCommand(string command)
{
PacketModel packet = new PacketModel()
{
Identifier = 1,
Message = command,
Name = "RustManager"
};
string packetString = JsonConvert.SerializeObject(packet);
var result = await _webSocket.SendAsync(packetString, null);
}
EDIT 1:
After getting some new information here is my new answer:
You use this class for your websocket. If you look at the signiture of the "SendAsync" method you can see, that it returns void (which means "nothing"). So you will not be able to "Store some kind of information" here.
The method looks like this:
public void SendAsync (string data, Action<bool> completed)
{ [...] }
You will have to listen to the WebSocket and wait for a server-side response. It seems, that the library supports that via events:
ws.OnMessage += (sender, e) => {
...
};
So you can define an eventhandler to process the server-response.
If you would like to get the message data, you should access e.Data or e.RawData property.
e.Data property returns a string, so it is mainly used to get the text message data.
(source (GitHub Readme))
So to fullfil your wishes try the following:
1.) At initialization of your _websocket instance subscribe to the .OnMessageevent with a corresponding event handler. (Some information about that)
2.) Send your message as you do it now with SendAsync
3.) If your server responds with a message to the network socket, the OnMessageevent will fire and you will be able to get the Data from the Eventargument e
(I did not test this - but it should work since it is used this way in the examples)
I am trying to load many pages using the AngleSharp. The idea is that it loads a page, and if this page has a link to the next, loads the next page and so forth, the methods are described like bellow. But I am getting the inner exception:
Specified argument was out of the range of valid values.
Parameter name: index"
I believe is something related with Thread and syncrhronization.
public static bool ContainsNextPage(IDocument document)
{
String href = document.QuerySelectorAll(".prevnext a")[0].GetAttribute("href");
if (href == String.Empty)
return false;
else
return true;
}
public static string GetNextPageUrl(IDocument document)
{
return document.QuerySelectorAll(".prevnext a")[0].GetAttribute("href");
}
public static async Task<IDocument> ParseUrlSynch(string Url)
{
var config = new Configuration().WithDefaultLoader();
IDocument document = await BrowsingContext.New(config).OpenAsync(Url);
return document;
}
public static async Task<ConcurrentBag<IDocument>> GetAllPagesDOMs(IDocument initialDocument)
{
ConcurrentBag< IDocument> AllPagesDOM = new ConcurrentBag< IDocument>();
IDocument nextPageDOM;
IDocument currentDocument = initialDocument;
if (initialDocument != null)
{
AllPagesDOM.Add(initialDocument);
}
while (ContainsNextPage(currentDocument))
{
String nextPageUrl = GetNextPageUrl(currentDocument);
nextPageDOM = ParseUrlSynch(nextPageUrl).Result;
if (nextPageDOM != null)
AllPagesDOM.Add(nextPageDOM);
currentDocument = nextPageDOM;
}
return AllPagesDOM;
}
static void Main(string[] args)
{
List<IDocument> allPageDOMs = new List<IDocument>();
IDocument initialDocument = ParseUrlSynch(InitialUrl).Result;
List<String> urls = new List<string>();
List<Subject> subjects = new List<Subject>();
IHtmlCollection<IElement> subjectAnchors = initialDocument.QuerySelectorAll(".course_title a");
String[] TitleAndCode;
String Title;
String Code;
String Description;
IDocument currentDocument = initialDocument;
ConcurrentBag<IDocument> documents =
GetAllPagesDOMs(initialDocument).Result; //Exception in here
...
}
Error message is caused by this code:
document.QuerySelectorAll(".prevnext a")[0]
One of your documents doesn't have any anchors inside prevnext. Maybe it's first page, maybe the last, either way you need to check the array for it's length.
Also blocking call on async method is a bad practice and should be avoided. You'll get the deadlock in any UI app. The only reason you don't get it now is that you're in console app.
Your instincts are correct, if you are using this from an application with a non-default SynchronizationContext such as WPF, Win Forms, or ASP.NET then you will have a deadlock because you are synchronously blocking on an async Task returning function (this is bad and should be avoided). When the first await is reaching inside of the blocking call, it will try to post the continuation to the current SyncronizationContext, which will be already locked by the blocking call (if you use .ConfigureAwait(false) you avoid this, but that is a hack in this case).
A quick fix would be to use async all the way through by changing:
nextPageDOM = ParseUrlSynch(nextPageUrl).Result;
with:
nextPageDOM = await ParseUrlSynch(nextPageUrl);
After you get stung by this a few times, you'll learn to have alarm bells go off in your head every time you block an asynchronous method.
I try to wait for an HttpWebRequest to finish without writing a dozen AsyncCallbacks. For that I tried to handle the call in a Task and use WaitOne within it --> so the ui thread will not be blocked.
The Problem now is that there appears a NotSupportedException everytime I call it and I donĀ“t understand why. Can someone tell me more about that and maybe how to fix this issue?
Here the code:
Task.Factory.StartNew((x)=>
{
HttpWebRequest request = WebRequest.CreateHttp(baseUri + "/api/" + ControllerName);
request.Method = "POST";
request.ContentType = "application/json";
request.Headers["Session"] = SessionKey;
IAsyncResult GetRequestStreamResult = request.BeginGetRequestStream(null, null);
GetRequestStreamResult.AsyncWaitHandle.WaitOne(); //<-- That causes the exception
using (Stream RequestStream = request.EndGetRequestStream(GetRequestStreamResult))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(Parameter.GetType());
serializer.WriteObject(RequestStream, Parameter);
}
Best regards
Christoph
I found this article. That pointed me to the direction that maybe it is no common silverlight issue, but a Problem with the actual implementation of the IAsyncResult which is of System.Net.Browser.OHWRAsyncResult. And this simply throws a NotSupportedException in any case when accessing the AsyncWaitHandle getter.
I helped me out by writing this small Extension method:
private static void WaitForIt(this IAsyncResult result)
{
while (!result.IsCompleted)
{
Thread.Sleep(50);
}
}
Not really pretty but it works...
Regards
Christoph
Use this handler...
while ((GetRequestStreamResult.AsyncWaitHandle.WaitOne(1000, true) == false)
&& (GetRequestStreamResult.IsCompleted == false))
{
// Do nothing
}
I'm working on WinRT. If an unhandled exception is thrown I want to write the message text to the storage.
I added an Event handler in 'App.xaml.cs', see the code.
The exception is caught but the last line, where the file is written, crashes again -> 'exception'!
Why? Any idea?
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
this.UnhandledException += App_UnhandledException;
}
async void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file= await folder.CreateFileAsync("crash.log",CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(file, e.Message); // <----- crash again -----
}
Thanks
Sunny
I've been wondering the same thing and stumbled across this quite early on in my search. I've figured out a way, hopefully this will prove useful to someone else too.
The problem is that await is returning control of the UI thread and the app's crashing. You need a deferral but there's no real way to get one.
My solution is to use the settings storage, instead. I'm assuming most people wanting to do this want to do something LittleWatson style, so here's some code modified from http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on-windows-phone-7.aspx for your convenience:
namespace YourApp
{
using Windows.Storage;
using Windows.UI.Popups;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
public class LittleWatson
{
private const string settingname = "LittleWatsonDetails";
private const string email = "mailto:?to=you#example.com&subject=YourApp auto-generated problem report&body=";
private const string extra = "extra", message = "message", stacktrace = "stacktrace";
internal static void ReportException(Exception ex, string extraData)
{
ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;
exceptionValues[extra] = extraData;
exceptionValues[message] = ex.Message;
exceptionValues[stacktrace] = ex.StackTrace;
}
internal async static Task CheckForPreviousException()
{
var container = ApplicationData.Current.LocalSettings.Containers;
try
{
var exceptionValues = container[settingname].Values;
string extraData = exceptionValues[extra] as string;
string messageData = exceptionValues[message] as string;
string stacktraceData = exceptionValues[stacktrace] as string;
var sb = new StringBuilder();
sb.AppendLine(extraData);
sb.AppendLine(messageData);
sb.AppendLine(stacktraceData);
string contents = sb.ToString();
SafeDeleteLog();
if (stacktraceData != null && stacktraceData.Length > 0)
{
var dialog = new MessageDialog("A problem occured the last time you ran this application. Would you like to report it so that we can fix the error?", "Error Report")
{
CancelCommandIndex = 1,
DefaultCommandIndex = 0
};
dialog.Commands.Add(new UICommand("Send", async delegate
{
var mailToSend = email.ToString();
mailToSend += contents;
var mailto = new Uri(mailToSend);
await Windows.System.Launcher.LaunchUriAsync(mailto);
}));
dialog.Commands.Add(new UICommand("Cancel"));
await dialog.ShowAsync();
}
}
catch (KeyNotFoundException)
{
// KeyNotFoundException will fire if we've not ever had crash data. No worries!
}
}
private static void SafeDeleteLog()
{
ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;
exceptionValues[extra] = string.Empty;
exceptionValues[message] = string.Empty;
exceptionValues[stacktrace] = string.Empty;
}
}
}
To implement it, you need to do the same as the link above says, but to ensure the data's here in case the url ever goes down:
App.xaml.cs Constructor (BEFORE the call to this.InitializeComponent()):
this.UnhandledException += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
Obviously if you already have an UnhandledException method you can throw the call to LittleWatson in there.
If you're on Windows 8.1, you can add a NavigationFailed call too. This needs to be in an actual page (typically MainPage.xaml.cs or whatever page is first opened):
xx.xaml.cs Constructor (any given page):
rootFrame.NavigationFailed += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
Lastly, you need to ask the user if they want to send the e-mail when the app re-opens. In your app's default Page's constructor (default: the page App.xaml.cs initializes):
this.Loaded += async (s, e) => await LittleWatson.CheckForPreviousException();
Or add the call to your OnLoad method if you already use it.
In this situation, await could be loosely translated to "do this job on another thread, and continue what you were doing while you wait for it to finish". Given that what your app was doing was crashing, you probably don't want it to continue doing that until you're done logging the problem. I'd suggest running your file IO synchronously in this case.
This may come a bit too late for the original question but...
as #Hans Passant suggested, avoiding await (i.e., running the FileIO.AppendTextAsync() synchronously), also seconded by #Jon, I would opt for this rather than the relatively too heavy code for LittleWatson. As the app is in some error handing state anyway (this should be a rare occurrence) I wouldn't put any blocking arising from synchronous (due to removing await) as a major downside.
Leaving the synchronous option to one side, the following await implementation worked for me:
Change await FileIO.AppendTextAsync(file, e.Message); to:
Task task = LogErrorMessage(file, e.Message)
task.Wait(2000); // adjust the ms value as appropriate
...
private async Task LogErrorMessage(StorageFile file, string errorMessage)
{
await FileIO.AppendTextAsync(file, errorMessage); // this shouldn't crash in App_UnhandledException as it did before
}
I have the following bits of code, scattered throughout my application. I'd really like to boilerplate it, and place it in either a static class, or some utility set of classes so I don't have all this duplication.
However, the small bits of the function are unique in such a way that I don't know how to refactor it.
private void callResponseCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response;
// End the get response operation
response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamReader = new StreamReader(streamResponse);
string responseData = streamReader.ReadToEnd();
streamResponse.Close();
streamReader.Close();
response.Close();
ExpectedResponseType regResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ExpectedResponseType>(responseData);
if (regResponse.ok == "0")
{
//error - handle the msg
//whether the user not loggin or not exist
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(CustomErrorMessage);
});
}
else
{
//check the variables unique to the ExpectedResponseType and do Stuff here;
}
}
catch (WebException e)
{
// Error treatment
// ...
Debug.WriteLine("error " + e);
}
I am most curious how to pass in "ExpectedResponseType", such that it might be any Class, (i.e., is there a way to pass in T?) or possibly how to fire events that can then be executed by the UI thread and handled appropriately.
Thanks.
edit: "ExpectedResponseType" or "T" is a large collection of classes for each type of server call. For example I have LoginResponse, RegisterResponse, GetFilesResponse, UpdateResponse, DownloadResponse, etc..
EDIT: I have removed earlier example as it would not work with the delegate signature.
In order to handle the checking of the parameters specific to the type T you will need to add a little abstraction, the cleanest way is probably to wrap your code in a templated class that allows the registration of a delegate for handling the checking, I'm sure this is a specific pattern but cannot recall which one:
public class ResponseHandler<T>
{
public ResponseHandler(Action<T> typeSpecificCheckFunction)
{
this.CheckVariables = typeSpecificCheckFunction;
}
Action<T> CheckVariables;
public void callResponseCallback(IAsyncResult asynchronousResult)
{
// stuff
T regResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseData);
CheckVariables(response);
// stuff
}
}
In response to you question about handling a large variety of T, perhaps the cleaned up code above clears it up, if not then this is what generics are for - provided you know what you are expecting in each case. So for each type you were expecting you would call it something along the lines of:
var handler = new ResponseHandler<ExpectedResponseType>( response =>
{
// code to check your response properties here
});
xxx.RegisterResponseCallback(handler.callResponseCallback);