I am trying to Send Error reports with hockeyapp without having to let the whole app crash and burn. I dont think the HockeyApp.WPF library has this capability, so I started to mess around with implementing my own CrashHandler.
This quickly got confusing and very hackey. Does anyone have any code examples for this? At my current rate I will end up reproducing half of the HockeyApp Library, so I would appreciate some help.
I am not posting my code because I don't think it will help and its too much.
Edit: now I will post a shortened version of code that doesnt seem to work:
private static void HandleException(Exception e) {
try {
string crashID = Guid.NewGuid().ToString();
String filename = String.Format("{0}{1}.log", CrashFilePrefix, crashID);
CrashLogInformation logInfo = new CrashLogInformation() {
PackageName = Application.Current.GetType().Namespace,
Version = ((HockeyClient)HockeyClient.Current).VersionInfo,
OperatingSystem = Environment.OSVersion.Platform.ToString(),
Windows = Environment.OSVersion.Version.ToString() + Environment.OSVersion.ServicePack,
Manufacturer = "",
Model = ""
};
ICrashData crash = ((HockeyClient)HockeyClient.Current).CreateCrashData(e);
using (FileStream stream = File.Create(Path.Combine(GetPathToHockeyCrashes(), filename))) {
crash.Serialize(stream);
stream.Flush();
}
}
catch (Exception ex) {
((HockeyClient)HockeyClient.Current).HandleInternalUnhandledException(ex);
}
}
private static string GetPathToHockeyCrashes() {
string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
if (!path.EndsWith("\\")) { path += "\\"; }
path += "HockeyCrashes\\";
if (!Directory.Exists(path)) { Directory.CreateDirectory(path); }
return path;
}
private struct CrashLogInformation {
/// <summary>
/// name of app package
/// </summary>
public string PackageName;
/// <summary>
/// version of app
/// </summary>
public string Version;
/// <summary>
/// os
/// </summary>
public string OperatingSystem;
/// <summary>
/// device manufacturer
/// </summary>
public string Manufacturer;
/// <summary>
/// device model
/// </summary>
public string Model;
/// <summary>
/// product id of app
/// </summary>
public string ProductID;
/// <summary>
/// windows phone version
/// </summary>
public string WindowsPhone;
/// <summary>
/// windows version
/// </summary>
public string Windows;
}
I was able to make a post after formatting the logs as described in the documentation for the crashes/upload endpoint(http://support.hockeyapp.net/kb/api/api-crashes#-u-post-api-2-apps-app_id-crashes-upload-u-).
Although I ended up hitting "crashes/", which from my understanding is different from crashes/upload(Therefore this is a solution that is hitting an undocumented endpoint).
private static readonly string HOCKEYUPLOADURL = #"https://rink.hockeyapp.net/api/2/apps/{0}/crashes/";
private static async Task SendDataAsync(String log, String userID, String contact, String description) {
string rawData = "";
rawData += "raw=" + Uri.EscapeDataString(log);
if (userID != null) {
rawData += "&userID=" + Uri.EscapeDataString(userID);
}
if (contact != null) {
rawData += "&contact=" + Uri.EscapeDataString(contact);
}
if (description != null) {
rawData += "&description=" + Uri.EscapeDataString(description);
}
WebRequest request = WebRequest.Create(new Uri(String.Format(HOCKEYUPLOADURL, HOCKEYAPPID)));
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (Stream stream = await request.GetRequestStreamAsync()) {
byte[] byteArray = Encoding.UTF8.GetBytes(rawData);
stream.Write(byteArray, 0, rawData.Length);
stream.Flush();
}
try {
using (WebResponse response = await request.GetResponseAsync()) { }
}
catch (WebException e) {
WriteLocalLog(e, "HockeyApp SendDataAsync failed");
}
}
Related
I have started using nLog to log errors on my application recently. However, I am here to see if there is any way to add some information about the currently logged in user on the application to the log layout if a user is currently logged in on the application at the time the exception occurs.
Currently I have the following layout:
<target xsi:type="File" name="errorlogs" fileName="./logs/error-logs/${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
However, I wish to add the email of the currently logged in user if the exception happens when there is a logged in user on the application.
My application is running on ASP.Net Core 3.1.
How can I add that to the layout.
Thank you
Maybe not a 100% fit as your question relies on the User logging into your application, and your application running under their user context, but the following works for me in 'normal' application programming...
Create a new public static class. I call mine "Logging".
If your assembly doesn't have it already, add a reference to NLog.
The output format is that used by the freely available Microsoft CMTrace utility. Available here: CMTrace Download link
Add the following:
public static class Logging
{
#region Fields
private static bool _IsSetup = false;
private static Logger _Log = LogManager.GetCurrentClassLogger();
#endregion
#region Private Methods
[MethodImpl(MethodImplOptions.NoInlining)]
private static string GetCurrentMethod()
{
StackTrace st = new StackTrace();
int FrameNumber = 1;
StackFrame sf = st.GetFrame(FrameNumber); // Get the previous stack frame
string MethodName = sf.GetMethod().Name;
string ClassName = sf.GetMethod().ReflectedType.FullName;
while (MethodName == "Log" || MethodName.StartsWith("Write")) // If it's the "Log" or "Write" method calling this, get the method before that one.
{
FrameNumber++;
if (FrameNumber < 6)
{
try
{
MethodName = st.GetFrame(FrameNumber).GetMethod().Name;
ClassName = st.GetFrame(FrameNumber).GetMethod().ReflectedType.FullName;
}
catch
{
}
}
else // Prevent an infinite loop
{
MethodName = "Unknown Method";
ClassName = "Unknown Class";
}
}
return ClassName + "." + MethodName;
}
#endregion
#region Public Methods
/// <summary>
/// Append the specified text to the given TextBox
/// </summary>
/// <param name="Message">The message to append</param>
/// <param name="Control">The TextBox to target</param>
public static void LogToTextbox(string Message, TextBox Control)
{
if (Message.Length > 0)
{
Control.AppendText(Message + Environment.NewLine);
Control.Refresh();
Application.DoEvents();
}
}
/// <summary>
/// Setup Logging
/// </summary>
/// <param name="Overwrite">If set to true, any existing file will be over-written</param>
public static void Setup(bool Overwrite = false)
{
LoggingConfiguration Config = new LoggingConfiguration();
FileTarget File = new FileTarget();
Config.AddTarget("File", File);
File.Layout = "${message}";
File.FileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName, System.Diagnostics.Process.GetCurrentProcess().ProcessName) + ".log";
File.AutoFlush = true;
File.KeepFileOpen = false;
File.ArchiveFileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName, System.Diagnostics.Process.GetCurrentProcess().ProcessName) + "_{#}.log";
File.ArchiveNumbering = ArchiveNumberingMode.Rolling;
File.ArchiveEvery = FileArchivePeriod.Day;
File.MaxArchiveDays = 31;
if (Overwrite)
{
File.DeleteOldFileOnStartup = true;
}
// Create rules
LoggingRule Rule1 = new LoggingRule("*", LogLevel.Trace, File);
// Apply rules
Config.LoggingRules.Add(Rule1);
// Activate logging
LogManager.Configuration = Config;
// Cleanup
_IsSetup = true;
}
/// <summary>
/// Write the specified message type and string to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
/// </summary>
/// <param name="Level">The level of message to write</param>
/// <param name="Message">The message to write</param>
public static void Write(LogLevel Level, string Message)
{
string Severity;
string OutputMessage;
DateTime UtcNow = DateTime.UtcNow;
DateTime Now = DateTime.Now;
string UtcDate = UtcNow.ToString("MM-dd-yyyy");
string UtcTime = UtcNow.ToString("HH:mm:ss.") + UtcNow.Millisecond;
string Date = Now.ToString("dd-MM-yyyy");
string Time = Now.ToString("HH:mm:ss.") + UtcNow.Millisecond;
string TZOffset = TimeZoneInfo.Local.GetUtcOffset(Now).TotalHours.ToString("+000");
if (!_IsSetup)
{
Setup();
}
Trace.WriteLine(Message);
Message = $"{Date} {Time}: {Message}";
switch (Level.Name)
{
default:
Severity = "0";
break;
case "Info":
Severity = "1";
break;
case "Warn":
Severity = "2";
break;
case "Error":
Severity = "3";
break;
}
// https://adamtheautomator.com/building-logs-for-cmtrace-powershell/
OutputMessage = $"<![LOG[{Message}]LOG]!><time=\"{UtcTime}{TZOffset}\" date=\"{UtcDate}\" component=\"{GetCurrentMethod()}\" context=\"{Environment.UserName}\" type=\"{Severity}\" thread=\"{Thread.CurrentThread.ManagedThreadId}\" file=\"{System.Diagnostics.Process.GetCurrentProcess().ProcessName}\">";
// The following can be used as a catch-all
//try
//{
_Log.Log(Level, OutputMessage);
//}
//catch (Exception e)
//{
// // If we cannot write to the log file, write to the EventLog instead.
// using (EventLog eventLog = new EventLog("Application"))
// {
// string OutputFolderName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName);
// string ExecutableName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
// eventLog.Source = "Application";
// eventLog.WriteEntry($"Failed to write to application logfile (in {OutputFolderName}) for {ExecutableName}. The error was: {e.Message}", EventLogEntryType.Error, 101, 1);
// }
//}
}
/// <summary>
/// Write a error message to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
/// </summary>
/// <param name="Message">The message to write</param>
public static void WriteError(string Message)
{
Write(LogLevel.Error, Message);
}
/// <summary>
/// Write an informational message to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
/// </summary>
/// <param name="Message">The message to write</param>
public static void WriteInfo(string Message)
{
Write(LogLevel.Info, Message);
}
/// <summary>
/// Write a warning message to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
/// </summary>
/// <param name="Message">The message to write</param>
public static void WriteWarning(string Message)
{
Write(LogLevel.Warn, Message);
}
#endregion
}
Usage:
Logging.Setup();
Logging.WriteInfo("Application startup");
Logging.WriteError($"{DestinationFilename}: Cannot overwrite file. User advised to delete file manually. The error was: {ex.Message}");
Example output:
<![LOG[20-11-2020 13:22:48.626: Application startup]LOG]!><time="05:22:48.626+008" date="11-20-2020" component="Bitberry.Elda.GetLatest.frmMain..ctor" context="DaveR" type="1" thread="1" file="GetLatest">
Specifically, the 'context="USERNAME"' portion is what you have asked for.
I am implementing passing a web socket back from our api according to this url here;
http://blogs.msdn.com/b/youssefm/archive/2012/07/17/building-real-time-web-apps-with-asp-net-webapi-and-websockets.aspx
Now the idea is that the user will register for a web socket. This is done and works using the following code;
[HttpGet]
[Route("getsocket")]
public HttpResponseMessage GetWebSocket()
{
HttpContext.Current.AcceptWebSocketRequest(new TestWebSocketHandler());
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
Then they make a call to the api to begin doing some specific functions, which will then report with message back down the same websocket.
private static WebSocketWrapper _socket;
[HttpGet]
[Route("begin")]
public async Task<IHttpActionResult> StartRunning(string itemid)
{
try
{
if (_socket == null ||
_socket.State() == WebSocketState.Aborted ||
_socket.State() == WebSocketState.Closed ||
_socket.State() == WebSocketState.None)
{
_socket = WebSocketWrapper.Create("wss://localhost:44301/api/v1/testcontroller/getsocket");
_socket.OnConnect(OnConnect);
_socket.OnDisconnect(OnDisconnect);
_socket.OnMessage(OnMessage);
await _socket.ConnectAsync();
}
//builds first message to be sent and sends it
_socket.QueueMessage(firstTest);
}
catch(Exception ex)
{
//logs error
}
return Ok();
}
So effectively the client cretes a new websocket connected to the server. They then call the second message to trigger the server to start a number of tests on the device passed. The server start the tests and broadcast the messages back down the socket (the json message model contains the deviceid, so the client can filter for relevent messages).
When they receive a message the client will then acknowledge it and the next test is done etc.
Now it works the first time I run it (after compilation). However, I want to be able to have multiple client connect to the websocket list (the solution is tabulated and the tests it will run may take a while, so its possible multiple tests will be ran at any one time). So I think it has something to do with the static WebSocketWrapper instance.
However, they have asked that a single websocket be used on the server, with a list of the devices being listened for. So in effect all messages are sent to all clients from the one server connection. The clients then filter out the messages they want to listen to based on the deviceid they pass.
When I try re-running, ore running a second test, Which is basically calling the getwebsocket and then the begin method, the code runs without error, but the onopen method never gets called? Its as though the socket just doesnt fire up?
Unfortunately we cannot use signalr as this is not specified
For reference the socket wrapper class is
public class WebSocketWrapper
{
private const int ReceiveChunkSize = 2048;
private const int SendChunkSize = 2048;
private readonly ClientWebSocket _ws;
private readonly Uri _uri;
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private readonly CancellationToken _cancellationToken;
private Action<WebSocketWrapper> _onConnected;
private Action<TestResultModel, WebSocketWrapper> _onMessage;
private Action<WebSocketWrapper> _onDisconnected;
//private static Queue<TestResultModel> _messageQueue = new Queue<TestResultModel>();
private static BlockingCollection<TestResultModel> _messageQueue = new BlockingCollection<TestResultModel>();
protected WebSocketWrapper(string uri)
{
_ws = new ClientWebSocket();
_ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(30);
_uri = new Uri(uri);
_cancellationToken = _cancellationTokenSource.Token;
}
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="uri">The URI of the WebSocket server.</param>
/// <returns></returns>
public static WebSocketWrapper Create(string uri)
{
return new WebSocketWrapper(uri);
}
/// <summary>
/// Get the current state of the socket
/// </summary>
/// <returns>WebSocketState of the current _ws</returns>
public WebSocketState State()
{
return _ws.State;
}
/// <summary>
/// Disconnects from the websocket
/// </summary>
public async Task DisconnectAsync()
{
try
{
await _ws.CloseOutputAsync(
WebSocketCloseStatus.NormalClosure,
"Server has been closed by the disconnect method",
_cancellationToken);
CallOnDisconnected();
}
catch(Exception ex)
{
throw ex;
}
finally
{
_ws.Dispose();
}
}
/// <summary>
/// Connects to the WebSocket server.
/// </summary>
/// <returns></returns>
public async Task<WebSocketWrapper> ConnectAsync()
{
try
{
await _ws.ConnectAsync(_uri, _cancellationToken);
}
catch(Exception ex)
{
}
CallOnConnected();
RunInTask(() => ProcessQueueAsync());
RunInTask(() => StartListen());
return this;
}
/// <summary>
/// Set the Action to call when the connection has been established.
/// </summary>
/// <param name="onConnect">The Action to call.</param>
/// <returns></returns>
public WebSocketWrapper OnConnect(Action<WebSocketWrapper> onConnect)
{
_onConnected = onConnect;
return this;
}
/// <summary>
/// Set the Action to call when the connection has been terminated.
/// </summary>
/// <param name="onDisconnect">The Action to call</param>
/// <returns></returns>
public WebSocketWrapper OnDisconnect(Action<WebSocketWrapper> onDisconnect)
{
_onDisconnected = onDisconnect;
return this;
}
/// <summary>
/// Adds a message to the queu for sending
/// </summary>
/// <param name="message"></param>
public void QueueMessage(TestResultModel message)
{
//_messageQueue.Enqueue(message);
_messageQueue.Add(message);
}
/// <summary>
/// returns the size of the current message queue.
/// Usefult for detemning whether or not an messages are left in queue before closing
/// </summary>
/// <returns></returns>
public int QueueCount()
{
return _messageQueue.Count;
}
/// <summary>
/// Processes the message queue in order
/// </summary>
public async Task ProcessQueueAsync()
{
try
{
foreach(var current in _messageQueue.GetConsumingEnumerable())
{
await SendMessageAsync(current);
}
}
catch(Exception ex)
{
//TODO
}
}
/// <summary>
/// Set the Action to call when a messages has been received.
/// </summary>
/// <param name="onMessage">The Action to call.</param>
/// <returns></returns>
public WebSocketWrapper OnMessage(Action<TestResultModel, WebSocketWrapper> onMessage)
{
_onMessage = onMessage;
return this;
}
/// <summary>
/// Send a message to the WebSocket server.
/// </summary>
/// <param name="message">The message to send</param>
public async Task SendMessageAsync(TestResultModel result)
{
if (_ws.State != WebSocketState.Open)
{
throw new Exception("Connection is not open.");
}
var message = JsonConvert.SerializeObject(result);
var messageBuffer = Encoding.UTF8.GetBytes(message);
var messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / SendChunkSize);
for (var i = 0; i < messagesCount; i++)
{
var offset = (SendChunkSize * i);
var count = SendChunkSize;
var lastMessage = ((i + 1) == messagesCount);
if ((count * (i + 1)) > messageBuffer.Length)
{
count = messageBuffer.Length - offset;
}
await _ws.SendAsync(new ArraySegment<byte>(messageBuffer, offset, count), WebSocketMessageType.Text, lastMessage, _cancellationToken);
}
}
private async Task StartListen()
{
var buffer = new byte[ReceiveChunkSize];
//part of a big hack, temporary solution
string prevResult = "";
try
{
while (_ws.State == WebSocketState.Open)
{
var stringResult = new StringBuilder();
WebSocketReceiveResult result;
do
{
result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), _cancellationToken);
if (result.MessageType == WebSocketMessageType.Close)
{
await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
CallOnDisconnected();
}
else
{
var str = Encoding.UTF8.GetString(buffer, 0, result.Count);
stringResult.Append(str);
}
} while (!result.EndOfMessage);
if (!prevResult.Equals(stringResult.ToString()))
{
prevResult = stringResult.ToString();
CallOnMessage(stringResult);
}
}
}
catch (Exception ex)
{
CallOnDisconnected();
}
finally
{
_ws.Dispose();
}
}
private void CallOnMessage(StringBuilder stringResult)
{
if (_onMessage != null)
{
try
{
var message = JsonConvert.DeserializeObject<TestResultModel>(stringResult.ToString());
RunInTask(() => _onMessage(message, this));
}
catch (Exception ex)
{
//Ignore any other messages not TestResultModel
}
}
}
private void CallOnDisconnected()
{
if (_onDisconnected != null)
RunInTask(() => _onDisconnected(this));
}
private void CallOnConnected()
{
if (_onConnected != null)
RunInTask(() => _onConnected(this));
}
private static Task RunInTask(Action action)
{
return Task.Factory.StartNew(action);
}
}
As an update please see the debug screen taken when trying to call the websocket for the second time. As you can see it appears to be in the aborted state? (on first run obviousl its null). Any ideas?
A lot of the functionality you are looking is "easy" to implement using SignalR. As where you create a "Hub" where you can broadcast to all clients or simply choose a single client.
I take it that you have previous experience with websockets, so I think you should take a look! It is a bit magical and can be a hell to troubleshoot in bigger applications and services.
A simple chat tutorial can be found here
You can also take a look here for a person that has had problems sending notications to single clients using SignalR
This question already has answers here:
Passing arguments one by one one in console exe by c# code
(3 answers)
Closed 9 years ago.
I am running a console application exe (developed in c#) by my another windows application in c# .
Process p = Process.Start("strExePath");
The console application which i ran expects some user inputs and I want to provide those user inputs by the c# code of windows application which runs the console exe file. How can I achieve this?
You can redirect the Standard Input for a Console application which you start via Process.Start().
To do so, you need to use ProcessStartInfo.RedirectStandardInput.
There's a complete example in the MSDN documentation that I linked.
Also, here's a wrapper class that you could probably adapt to your needs. (The code is somewhat old, so it's not using the latest C# features such as Tasks, but hopefully it will still demonstrate what you need to do.)
Note that to make the I/O redirection work without deadlock when you are redirecting both the input and the output, you must use a separate thread to process the I/O:
/// <summary>
/// Encapsulates an executable program.
/// This class makes it easy to run a console app and have that app's output appear
/// in the parent console's window, and to redirect input and output from/to files.
/// </summary>
/// <remarks>
/// To use this class:
/// (1) Create an instance.
/// (2) Set the ProgramFileName property if a filename wasn't specified in the constructor.
/// (3) Set other properties if required.
/// (4) Call Run().
/// </remarks>
public class Executable
{
#region Constructor
/// <summary>Constructor.</summary>
/// <param name="programFileName">Name of the program file to run.</param>
public Executable(string programFileName)
{
ProgramFileName = programFileName;
_processStartInfo.ErrorDialog = false;
_processStartInfo.CreateNoWindow = false;
_processStartInfo.UseShellExecute = false;
_processStartInfo.RedirectStandardOutput = false;
_processStartInfo.RedirectStandardError = false;
_processStartInfo.RedirectStandardInput = false;
_processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_processStartInfo.Arguments = "";
}
/// <summary>Construct with an empty executable name.</summary>
public Executable(): this(string.Empty)
{
}
#endregion // Constructor
#region Public Properties
/// <summary>The filename (full pathname) of the executable.</summary>
public string ProgramFileName
{
get
{
return _processStartInfo.FileName;
}
set
{
_processStartInfo.FileName = value;
}
}
/// <summary> command-line arguments passed to the executable when run.</summary>
public string Arguments
{
get
{
return _processStartInfo.Arguments;
}
set
{
_processStartInfo.Arguments = value;
}
}
/// <summary> The working directory set for the executable when run. </summary>
public string WorkingDirectory
{
get
{
return _processStartInfo.WorkingDirectory;
}
set
{
_processStartInfo.WorkingDirectory = value;
}
}
/// <summary>
/// The file to be used if standard input is redirected,
/// or null or string.Empty to not redirect standard input.
/// </summary>
public string StandardInputFileName
{
set
{
_standardInputFileName = value;
_processStartInfo.RedirectStandardInput = !string.IsNullOrEmpty(value);
}
get
{
return _standardInputFileName;
}
}
/// <summary>
/// The file to be used if standard output is redirected,
/// or null or string.Empty to not redirect standard output.
/// </summary>
public string StandardOutputFileName
{
set
{
_standardOutputFileName = value;
_processStartInfo.RedirectStandardOutput = !string.IsNullOrEmpty(value);
}
get
{
return _standardOutputFileName;
}
}
/// <summary>
/// The file to be used if standard error is redirected,
/// or null or string.Empty to not redirect standard error.
/// </summary>
public string StandardErrorFileName
{
set
{
_standardErrorFileName = value;
_processStartInfo.RedirectStandardError = !string.IsNullOrEmpty(value);
}
get
{
return _standardErrorFileName;
}
}
#endregion // Public Properties
#region Public Methods
/// <summary>Run the executable and wait until the it has terminated.</summary>
/// <returns>The exit code returned from the executable.</returns>
public int Run()
{
Thread standardInputThread = null;
Thread standardOutputThread = null;
Thread standardErrorThread = null;
_standardInput = null;
_standardError = null;
_standardOutput = null;
int exitCode = -1;
try
{
using (Process process = new Process())
{
process.StartInfo = _processStartInfo;
process.Start();
if (process.StartInfo.RedirectStandardInput)
{
_standardInput = process.StandardInput;
standardInputThread = startThread(new ThreadStart(supplyStandardInput), "StandardInput");
}
if (process.StartInfo.RedirectStandardError)
{
_standardError = process.StandardError;
standardErrorThread = startThread(new ThreadStart(writeStandardError), "StandardError");
}
if (process.StartInfo.RedirectStandardOutput)
{
_standardOutput = process.StandardOutput;
standardOutputThread = startThread(new ThreadStart(writeStandardOutput), "StandardOutput");
}
process.WaitForExit();
exitCode = process.ExitCode;
}
}
finally // Ensure that the threads do not persist beyond the process being run
{
if (standardInputThread != null)
standardInputThread.Join();
if (standardOutputThread != null)
standardOutputThread.Join();
if (standardErrorThread != null)
standardErrorThread.Join();
}
return exitCode;
}
#endregion // Public Methods
#region Private Methods
private static Thread startThread(ThreadStart startInfo, string name)
{
Thread t = new Thread(startInfo);
t.IsBackground = true ;
t.Name = name;
t.Start();
return t;
}
/// <summary>Thread which supplies standard input from the appropriate file to the running executable.</summary>
private void supplyStandardInput()
{
// feed text from the file a line at a time into the standard input stream
// NOTE: THERE MAY BE ISSUES RELATED TO CHARACTER ENCODING HERE -- REQUIRES INVESTIGATION
using (StreamReader reader = File.OpenText(_standardInputFileName))
using (StreamWriter writer = _standardInput)
{
writer.AutoFlush = true;
for (;;)
{
string textLine = reader.ReadLine();
if (textLine == null)
break;
writer.WriteLine(textLine);
}
}
}
/// <summary>Thread which outputs standard output from the running executable to the appropriate file.</summary>
private void writeStandardOutput()
{
// NOTE: THERE MAY BE ISSUES RELATED TO CHARACTER ENCODING HERE -- REQUIRES INVESTIGATION
using (StreamWriter writer = File.CreateText(_standardOutputFileName))
using (StreamReader reader = _standardOutput)
{
writer.AutoFlush = true;
for (;;)
{
string textLine = reader.ReadLine();
if (textLine == null)
break;
writer.WriteLine(textLine);
}
}
if (File.Exists(_standardOutputFileName))
{
FileInfo info = new FileInfo(_standardOutputFileName);
// if the error info is empty or just contains eof etc.
if (info.Length < 4)
info.Delete();
}
}
/// <summary>Thread which outputs standard error output from the running executable to the appropriate file.</summary>
private void writeStandardError()
{
// NOTE: THERE MAY BE ISSUES RELATED TO CHARACTER ENCODING HERE -- REQUIRES INVESTIGATION
using (StreamWriter writer = File.CreateText(_standardErrorFileName))
using (StreamReader reader = _standardError)
{
writer.AutoFlush = true;
for (;;)
{
string textLine = reader.ReadLine();
if (textLine == null)
break;
writer.WriteLine(textLine);
}
}
if (File.Exists(_standardErrorFileName))
{
FileInfo info = new FileInfo(_standardErrorFileName);
// if the error info is empty or just contains eof etc.
if (info.Length < 4)
info.Delete();
}
}
#endregion // Private Methods
#region Private Fields
private StreamReader _standardError ;
private StreamReader _standardOutput ;
private StreamWriter _standardInput ;
private string _standardInputFileName;
private string _standardOutputFileName;
private string _standardErrorFileName;
ProcessStartInfo _processStartInfo = new ProcessStartInfo();
#endregion // Private Fields
}
I'm making a simple app for connecting to a webservice, and I'm having problems managing async requests.
The problem is with the function ProcessRequest, which basically makes an async HttpWebRequest, and returns the HttpWebResponse. As the request is async, I'm having problems with the return value, and with the functions calling the ProcessRequest method and waiting for the HttpWebResponse object.
By the way, the request in itself works perfectly, already tested inside functions so I don't need to return an HttpWebResponse.
I hope I'm making myself clear, as I said, this is my first time ever touching c#, .NET, and Windows Phone development. ( And WebRequests for that matter )
The errors that Visual Studio is throwing are:
1: Since 'System.AsyncCallback' returns void, a return keyword must not be followed by an object expression
2: Cannot convert lambda expression to delegate type 'System.AsyncCallback' because some of the return types in the block are not implicitly convertible to the delegate return type
And this is the code:
namespace SimpleNoteConcept
{
public sealed class SimpleNote
{
private static readonly SimpleNote _instance = new SimpleNote();
private static string _authToken = string.Empty;
private static string _email = string.Empty;
private static string _authQsParams
{
get
{
if (string.IsNullOrEmpty(_authToken)) throw new SimpleNoteConceptAuthorisationException();
return string.Format("auth={0}&email={1}", _authToken, _email);
}
}
private SimpleNote() { }
public static SimpleNote Instance
{
get { return _instance; }
}
public bool Connect(string email, string password)
{
try
{
StringParamCheck("email", email);
StringParamCheck("password", password);
var data = string.Format("email={0}&password={1}", email, password);
var bytes = Encoding.GetEncoding("utf-8").GetBytes(data);
data = Convert.ToBase64String(bytes);
using (var resp = ProcessRequest( loginPath, "POST", content: data))
{
if (resp != null)
{
_authToken = resp.Cookies["auth"].Value;
_email = email;
System.Diagnostics.Debug.WriteLine("Connection established! -> " + _authToken);
return true;
}
return false;
}
}
catch (Exception)
{
throw;
}
}
public void GetIndex(int length = 100, string mark = null, DateTimeOffset? since = null)
{
try
{
string sinceString = null;
if (since.HasValue)
sinceString = Json.DateTimeEpochConverter.DateToSeconds(since.Value);
var queryParams = string.Format("{0}&length={1}&mark={2}&since={3}", _authQsParams, length, mark, sinceString);
using (var resp = ProcessRequest( indexPath, "GET", queryParams))
{
var respContent = ReadResponseContent(resp);
System.Diagnostics.Debug.WriteLine("GetIndex: " + respContent.ToString());
//var notes = JsonConvert.DeserializeObject<Objects.NoteEnumerable<T>>(respContent);
//return notes;
}
}
catch (WebException ex)
{
var resp = (HttpWebResponse)ex.Response;
switch (resp.StatusCode)
{
//401
case HttpStatusCode.Unauthorized:
throw new SimpleNoteConceptAuthorisationException(ex);
default:
throw;
}
}
catch (Exception) { throw; }
}
/// <summary>
/// Generic method to process a request to Simplenote.
/// All publicly expose methods which interact with the store are processed though this.
/// </summary>
/// <param name="requestPath">The path to the request to be processed</param>
/// <param name="method">The HTTP method for the request</param>
/// <param name="content">The content to send in the request</param>
/// <param name="queryParams">Queryparameters for the request</param>
/// <returns>An HttpWebResponse continaing details returned from Simplenote</returns>
private static HttpWebResponse ProcessRequest(string requestPath, string method,
string queryParams = null, string content = null)
{
try
{
var url = string.Format("{0}{1}{2}", "https://", domainPath, requestPath);
if (!string.IsNullOrEmpty(queryParams)) url += "?" + queryParams;
var request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = new CookieContainer();
request.Method = method;
request.BeginGetRequestStream((e) =>
{
using (Stream stream = request.EndGetRequestStream(e))
{
// Write data to the request stream
var bytesBody = Encoding.GetEncoding("utf-8").GetBytes(content);
stream.Write(bytesBody, 0, bytesBody.Length);
stream.Close();
stream.Dispose();
}
request.BeginGetResponse((callback) =>
{
try
{
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callback);
return response;
}
catch (WebException ex)
{
using (WebResponse Exresponse = ex.Response)
{
HttpWebResponse httpResponse = (HttpWebResponse)Exresponse;
System.Diagnostics.Debug.WriteLine("Error code: {0}", httpResponse.StatusCode);
using (Stream str = Exresponse.GetResponseStream())
{
string text = new StreamReader(str).ReadToEnd();
System.Diagnostics.Debug.WriteLine(text);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Message: " + ex.Message);
}
}, request);
}, request);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Reads the content from the response object
/// </summary>
/// <param name="resp">The response to be processed</param>
/// <returns>A string of the response content</returns>
private static string ReadResponseContent(HttpWebResponse resp)
{
if (resp == null) throw new ArgumentNullException("resp");
using (var sr = new StreamReader(resp.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
/// <summary>
/// String parameter helper method.
/// Checks for null or empty, throws ArgumentNullException if true
/// </summary>
/// <param name="paramName">The name of the paramter being checked</param>
/// <param name="value">The value to check</param>
private void StringParamCheck(string paramName, string value)
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(paramName, "Value must not be null or string.Empty");
}
} // Class End
} // Namespace End
Thanks in advance!
You cannot do async programming the same as normal. Here .Net is running the async parts in different thread, how can they be communicated? So what you can do is passing a delegate with your ProcessRequest method. which will take a parameter of HttpWebResponse.
Call your method like this:
Action<HttpWebResponse> actionDelegate = DoAfterGettingResponse;
ProcessRequest(indexPath, "GET", actionDelegate, queryParams);
Function that handle the response
public static void DoAfterGettingResponse(HttpWebResponse resp)
{
if (resp != null)
{
_authToken = resp.Cookies["auth"].Value;
_email = email;
System.Diagnostics.Debug.WriteLine("Connection established! -> " + _authToken);
}
//Do anything else with the response
}
ProcessRequest will have the new signature.
private static HttpWebResponse ProcessRequest(
string requestPath, string method, Action<HttpWebResponse> reponseDelegate,
string queryParams = null, string content = null)
{
//Do what you are already doing
}
And the place where you are returning response, just do
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callback);
responseDelegate(response);
You can do this way or use events, fire an event passing HttpWebResponse as parameter and handle the response in the listener.
I have a very simple WCF program where I have a simple self-host and a client running on the same computer. There is a single method which returns a System.IO.Stream, which is actually a serialized form of a simple string. (This could be any number of data types, but for the time being let's take it as a string).
Following is the code I use if you want to take a look at. SerializeData() and DeserializeData() are methods used to do just that, and works fine.
Host Service:
namespace HelloWCF1
{
[ServiceContract(Namespace = "http://My.WCF.Samples")]
public interface IService1
{
[OperationContract]
Stream GetString();
}
public class Service1 : IService1
{
//Simple String
public Stream GetString()
{
string str = "ABC";
Stream ms = new MemoryStream();
SerializeData<string>(str, ms);
return ms;
}
/// <summary>
/// Serialize an object of the type T to a Stream
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectToSerialize"></param>
/// <param name="str"></param>
public void SerializeData<T>(T objectToSerialize, Stream str)
{
BinaryFormatter bf = new BinaryFormatter();
try
{
bf.Serialize(str, objectToSerialize);
str.Position = 0;
}
catch (Exception)
{
}
}
/// <summary>
/// Deserialize a Stream
/// </summary>
/// <param name="dataToDeserialize"></param>
/// <returns></returns>
public object DeserializeData(Stream dataToDeserialize)
{
BinaryFormatter bf = new BinaryFormatter();
object ret = null;
try
{
ret = bf.Deserialize(dataToDeserialize);
}
catch (Exception)
{
}
return ret;
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddr = new Uri("http://localhost:8000/WCFSampleService");
//ServiceHost is created by defining Service Type and Base Address
using (ServiceHost svcHost = new ServiceHost(typeof(Service1), baseAddr))
{
//Trace message for service start
Console.WriteLine("Service Starting...");
//Adding an end point
svcHost.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "HelloWCF");
//Open service host
svcHost.Open();
Console.WriteLine("Press [Enter] to terminate.");
Console.ReadLine();
//Close service host
svcHost.Close();
}
}
}
}
Client Program:
namespace Client
{
[ServiceContract(Namespace = "http://My.WCF.Samples")]
public interface IService1
{
[OperationContract]
Stream GetString();
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private IService1 proxy = null;
private Stream memStr = new MemoryStream();
private void button1_Click(object sender, EventArgs e)
{
//Create end point
EndpointAddress epAddr = new EndpointAddress("http://localhost:8000/WCFSampleService/HelloWCF");
//Create proxy
proxy = ChannelFactory<IService1>.CreateChannel(new BasicHttpBinding(), epAddr);
//WCF Service Method is called to aquire the stream
try
{
memStr = proxy.GetString();
string str = (string)DeserializeData(memStr);
MessageBox.Show(str);
}
catch (CommunicationException commEx)
{
MessageBox.Show("Service Call Failed:" + commEx.Message);
}
}
/// <summary>
/// Serialize an object of the type T to a Stream
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectToSerialize"></param>
/// <param name="str"></param>
public static void SerializeData<T>(T objectToSerialize, Stream str)
{
BinaryFormatter bf = new BinaryFormatter();
try
{
bf.Serialize(str, objectToSerialize);
str.Position = 0;
}
catch (Exception)
{
}
}
/// <summary>
/// Deserialize a Stream
/// </summary>
/// <param name="dataToDeserialize"></param>
/// <returns></returns>
public static object DeserializeData(Stream dataToDeserialize)
{
BinaryFormatter bf = new BinaryFormatter();
object ret = null;
try
{
ret = bf.Deserialize(dataToDeserialize);
}
catch (Exception)
{
}
return ret;
}
}
}
Now, this works fine. It serializes a string into a Stream and sends using HTTP quite fine. However, I have a need to convert of cast this Stream into an object before sending. Simply put, I need the GetString() method to look like this:
public object GetString()
{
string str = "ABC";
Stream ms = new MemoryStream();
SerializeData<string>(str, ms);
return (object)ms;
}
However, when I do this, inside the Button1_Click event, at the line ***memStr = proxy.GetString();*** I get a CommunicationException. I work in a Japanese OS, so the exception message I get is in Japanese so I'll try to translate into English as best as I can. Forgive me if it is not very clear.
***Error occured in the reception of HTTP response concerning [url]http://localhost:8000/WCFSampleService/HelloWCF[/url]. Cause of the error could be Service End Point binding is not using an HTTP protocol. Or as a different cause, it is also possible that HTTP request context is suspended according to the server (as in the case of server being shut down). Please refer the server log for more details.***
What exactly is the problem and how can I get the program to work the way I want? It says to look at the log files but where can I find them?
Thanks in advance!
Would you not be better off just returning the string? Surely the string returned as a stream over an Http binding will be being Base64 encoded, which normally doubles the size of the data being returned!
Maybe you could consider TcpBinding as an alternative.