I have a Windows Service that is going to process results from plink.exe (Putty/SSH thing). I'm currently successfully capturing the standard output and sending commands via the standard input to the process.
My problem is, the OutputDataReceived event doesn't seem to get raised until I receive a line break from the console application process. The process prompts for a password and there isn't a line break until after I input a password.
So my question is, is there a way to process the Standard Output character-by-character, instead of line-by-line from the System.Diagnostics.Process class?
Here is my code:
_processInfoTest = new ProcessStartInfo();
_processInfoTest.FileName = serviceSettings.PlinkExecutable;
_processInfoTest.Arguments = GetPlinkArguments(serviceSettings);
_processInfoTest.RedirectStandardOutput = true;
_processInfoTest.RedirectStandardError = true;
_processInfoTest.RedirectStandardInput = true;
_processInfoTest.UseShellExecute = false;
_processInfoTest.CreateNoWindow = true;
_processTest = new Process();
_processTest.StartInfo = _processInfoTest;
_processTest.OutputDataReceived += processTest_OutputDataReceived;
_processTest.ErrorDataReceived += processTest_OutputDataReceived;
_processTest.Start();
_processTest.BeginOutputReadLine();
_processTest.BeginErrorReadLine();
And the event handler that handles the incoming lines of text:
private static void processTest_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string line = e.Data;
if (line != null)
{
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(line, DateTime.Now));
if (line.Contains("If you do not trust this host, press Return to abandon the"))
{
_processTest.StandardInput.Write("y");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
// This code never gets called because the event doesn't get raised until a line-break occurs
if (line.Contains("'s password:"))
{
_processTest.StandardInput.Write("mypassword");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
if (line.Contains("Access granted"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.Success));
}
else if (line.Contains("Access denied") || line.Contains("Password authentication failed"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidUserOrPass));
}
else if (line.Contains("Host does not exist"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidHostname));
}
else if (line.Contains("Connection timed out"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.TimedOut));
}
}
}
Thank you!
the event you are using is line buffered ... you will have to implement your own reader that calls Read() / ReadAsync() on the streamreader to get every char ...
I was looking for an answer forever, and right after asking the question here I found my solution.
Btw, thanks DarkSquirrel, you hit the nail on the head.
Here is my solution:
_processInfoTest = new ProcessStartInfo();
_processInfoTest.FileName = serviceSettings.PlinkExecutable;
_processInfoTest.Arguments = GetPlinkArguments(serviceSettings);
_processInfoTest.RedirectStandardOutput = true;
_processInfoTest.RedirectStandardError = true;
_processInfoTest.RedirectStandardInput = true;
_processInfoTest.UseShellExecute = false;
_processInfoTest.CreateNoWindow = true;
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(_processInfoTest.Arguments, DateTime.Now));
_processTest = new Process();
_processTest.StartInfo = _processInfoTest;
_processTest.Start();
Task.Factory.StartNew(() =>
{
ProcessOutputCharacters(_processTest.StandardError);
});
Task.Factory.StartNew(() =>
{
ProcessOutputCharacters(_processTest.StandardOutput);
});
And my methods:
private static void ProcessOutputCharacters(StreamReader streamReader)
{
int outputCharInt;
char outputChar;
string line = string.Empty;
while (-1 != (outputCharInt = streamReader.Read()))
{
outputChar = (char)outputCharInt;
if (outputChar == '\n' || outputChar == '\r')
{
if (line != string.Empty)
{
ProcessLine("Output: " + line);
}
line = string.Empty;
}
else
{
line += outputChar;
if (line.Contains("login as:"))
{
_processTest.StandardInput.Write("myusername");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
if (line.Contains("'s password:"))
{
_processTest.StandardInput.Write("mypassword");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
}
}
}
private static void ProcessLine(string line)
{
if (line != null)
{
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(line, DateTime.Now));
if (line.Contains("If you do not trust this host, press Return to abandon the"))
{
_processTest.StandardInput.Write("y");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
if (line.Contains("Access granted"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.Success));
}
else if (line.Contains("Access denied") || line.Contains("Password authentication failed"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidUserOrPass));
}
else if (line.Contains("Host does not exist"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidHostname));
}
else if (line.Contains("Connection timed out"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.TimedOut));
}
}
}
My only problem now is when I send the username or password (through StandardInput), it's only sending the first 5 characters. I'll do some research on the issue and post that as a separate question if I need to.
Thanks!
Related
Relevant code:
private static Thread m_thread = null;
private static Boolean m_stop = false;
public static Boolean Start(SearcherParams pars)
{
Boolean success = false;
if (m_thread == null)
{
// Perform a reset of all variables,
// to ensure that the state of the searcher is the same on every new start:
ResetVariables();
// Remember the parameters:
m_pars = pars;
// Start searching for FileSystemInfos that match the parameters:
m_thread = new Thread(new ThreadStart(SearchThread));
m_thread.Start();
success = true;
}
return success;
}
private static void SearchThread()
{
Boolean success = true;
String errorMsg = "";
// Search for FileSystemInfos that match the parameters:
if ((m_pars.SearchDir.Length >= 3) && (Directory.Exists(m_pars.SearchDir)))
{
if (m_pars.FileNames.Count > 0)
{
// Convert the string to search for into bytes if necessary:
if (m_pars.ContainingChecked)
{
if (m_pars.ContainingText != "")
{
try
{
m_containingBytes =
m_pars.Encoding.GetBytes(m_pars.ContainingText);
}
catch (Exception)
{
success = false;
errorMsg = "The string\r\n" + m_pars.ContainingText +
"\r\ncannot be converted into bytes.";
}
}
else
{
success = false;
errorMsg = "The string to search for must not be empty.";
}
}
if (success)
{
// Get the directory info for the search directory:
DirectoryInfo dirInfo = null;
try
{
dirInfo = new DirectoryInfo(m_pars.SearchDir);
}
catch (Exception ex)
{
success = false;
errorMsg = ex.Message;
}
if (success)
{
// Search the directory (maybe recursively),
// and raise events if something was found:
SearchDirectory(dirInfo);
}
}
}
else
{
success = false;
errorMsg = "Please enter one or more filenames to search for.";
}
}
else
{
success = false;
errorMsg = "The directory\r\n" + m_pars.SearchDir + "\r\ndoes not exist.";
}
// Remember the thread has ended:
m_thread = null;
// Raise an event:
if (ThreadEnded != null)
{
ThreadEnded(new ThreadEndedEventArgs(success, errorMsg));
}
}
private static void SearchDirectory(DirectoryInfo dirInfo)
{
if (!m_stop)
{
try
{
foreach (String fileName in m_pars.FileNames)
{
FileSystemInfo[] infos = dirInfo.GetFileSystemInfos(fileName);
foreach (FileSystemInfo info in infos)
{
if (m_stop)
{
break;
}
if (MatchesRestrictions(info))
{
// We have found a matching FileSystemInfo,
// so let's raise an event:
if (FoundInfo != null)
{
FoundInfo(new FoundInfoEventArgs(info));
}
}
}
}
if (m_pars.IncludeSubDirsChecked)
{
DirectoryInfo[] subDirInfos = dirInfo.GetDirectories();
foreach (DirectoryInfo subDirInfo in subDirInfos)
{
if (m_stop)
{
break;
}
// Recursion:
SearchDirectory(subDirInfo);
}
}
}
catch (Exception)
{
}
}
}
The stop is working fine I wanted to add also a pause button to pause and resume the thread.
I added a button click event but how do I make the pause/resume actions and where?
This is a link for the complete code : https://pastebin.com/fYYnHBB6
You can use the ManualResetEvent object.
Here is the simplified version.
In your class, create the object:
public static ManualResetEvent _mrsevent = new ManualResetEvent(false);
In your thread function, as part of the loops that search for files/directories:
private static void SearchThread()
{
foreach (String fileName in m_pars.FileNames)
{
_mrsevent.WaitOne();
}
}
From outside the thread you can call:
a) _mrsevent.Set(); // resume the thread.
b) _mrsevent.Reset(); // pause
I am using multi threading to make tasks goes faster and smooth, when results increase into richtextbox the UI start hanging don't know why, I creating a webbrowser in thread and doing some other stuff in a single thread kind !
Using Thread as STA ( Single thread kind )
here is a snippet of the code !
foreach (string line in URLLMemoRichTxt.Lines)
{
string href = line;
if (href.Trim() != string.Empty)
{
//XtraMessageBox.Show(href);
if (StopGettingInnerLink == true)
{
AddLog("Getting links has been stopped successfully!");
StopGettingInnerLink = true;
break;
}
else if (StopGettingInnerLink == false)
{
AddLog("Getting links from " + href);
runBrowserThread( new Uri(href));
await Task.Delay(5000);
AddLog("Giving the tool some rest for 5 seconds ! ");
}
}
}
private void runBrowserThread(Uri url)
{
browserth = new Thread(() => {
var br = new WebBrowser();
br.ScriptErrorsSuppressed = true;
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
browserth.SetApartmentState(ApartmentState.STA);
browserth.Start();
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br = sender as WebBrowser;
string currentURL = br.Url.ToString();
if (br.Url == e.Url)
{
HtmlElementCollection acollection = br.Document.GetElementsByTagName("a");
foreach (HtmlElement a in acollection)
{
string href = a.GetAttribute("href");
if (URLLMemoRichTxt.InvokeRequired)
{
URLLMemoRichTxt.Invoke((MethodInvoker)delegate ()
{
if (!URLList.Contains(href) && href.Trim() != string.Empty && !href.Contains(".jpg") && !href.Contains(".png") && !href.Contains(".gif") && !href.Contains(".jpeg"))
{
URLList.Add(href);
// URLListView.Items.Add(href);
// adding new link ino listview !
// URLListCountLBL.Text = URLListView.Items.Count.ToString();
URLLMemoRichTxt.Text += href + "\n";
URLListCountLBL.Text = URLLMemoRichTxt.Lines.Length.ToString();
// runbrowserinthread(href);
}
});
}
else
{
if (!URLList.Contains(href) && href.Trim() != string.Empty && !href.Contains(".jpg") && !href.Contains(".png") && !href.Contains(".gif") && !href.Contains(".jpeg"))
{
URLList.Add(href);
// URLListView.Items.Add(href);
URLLMemoRichTxt.Text += href + "\n";
URLListCountLBL.Text = URLLMemoRichTxt.Lines.Length.ToString();
// GetInnerLink(href);
}
}
}
AddLog("All links has been scrapped successfully for \r\n" + currentURL);
Application.ExitThread(); // Stops the thread
}
}
Already found a solution myself :
replaced :
URLLMemoRichTxt.Text += href + "\n";
With :
URLLMemoRichTxt.AppendText(Environment.NewLine + href);
I've been working on a project called Borealis Server Manager (https://github.com/cyberstrawberry101/Borealis-Server-Manager) since May of this year. I need help from anyone skilled enough in C# to do the following:
Allow the user to start multiple gameserver processes with arguments given to them, in upwards of dozens or hundreds of them as background processes, and still be able to switch the redirected output and input to those processes on-the-fly to the console window within Borealis.
This is what the console page looks like in Borealis:
You can see the screenshot here.
This is the code that currently launches the programs, merged with some code that was suggested by another Facepunch user months ago, but their new code seems to freeze Borealis while even one server is running, which is actually a step backwards, and I can't even get it to properly redirect console output into my console window in Borealis. I am completely okay with a rewrite of the code should someone step up to help Borealis enter beta stages. I've had the code functional enough to launch the servers externally, but internally, I can only get one server's output at a time, without killing all of the other servers. It's a pretty complex problem.
using MetroFramework;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Borealis
{
public partial class TAB_CONTROL : Form
{
#region pInvoke
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH
delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
public static void StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(Process proc)
{
//This does not require the console window to be visible.
if (AttachConsole((uint)proc.Id))
{
//Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
//Must wait here. If we don't and re-enable Ctrl-C handling below too fast, we might terminate ourselves.
var wasAborted = false;
var procTask = Task.Run(() =>
{
//This is necessary because when we kill the process, it obviously exits. At that point, there is no proc object to wait for any longer
if (!wasAborted)
proc.WaitForExit();
});
if (!procTask.Wait(10000))
{
wasAborted = true;
proc.Kill();
}
FreeConsole();
//Re-enable Ctrl-C handling or any subsequently started programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
}
#endregion
private CancellationTokenSource cancelToken;
public TAB_CONTROL()
{
InitializeComponent();
}
//===================================================================================//
// UI HANDLING CODE //
//===================================================================================//
private void ServerControl_Load(object sender, EventArgs e)
{
//Pull all gameserver data from gameservers.json, split all json strings into a list, iterate through that list for specific data.
if (GameServer_Management.server_collection != null)
{
foreach (GameServer_Object gameserver in GameServer_Management.server_collection)
{
comboboxGameserverList.Items.Add(gameserver.SERVER_name_friendly);
}
}
}
private void comboboxGameserverList_SelectedValueChanged(object sender, EventArgs e)
{
foreach (GameServer_Object gameserver in GameServer_Management.server_collection)
{
if (gameserver.SERVER_name_friendly == comboboxGameserverList.Text)
{
//Decide what data to pull from the object at this point in time of development.
GameServer_Object Controlled_GameServer = new GameServer_Object();
Controlled_GameServer.DIR_install_location = Controlled_GameServer.DIR_install_location;
Controlled_GameServer.SERVER_executable = Controlled_GameServer.SERVER_executable;
Controlled_GameServer.SERVER_launch_arguments = Controlled_GameServer.SERVER_launch_arguments;
Controlled_GameServer.SERVER_running_status = Controlled_GameServer.SERVER_running_status;
}
}
btnStartServer.Visible = true;
chkAutoRestart.Visible = true;
lblAutoRestart.Visible = true;
chkStandaloneMode.Visible = true;
lblStandaloneMode.Visible = true;
consolePanel.Visible = true;
}
private void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
consoleOutputList.Items.Add(e.Data);
}
}
private void RefreshData()
{
comboboxGameserverList.Items.Clear();
if (GameServer_Management.server_collection != null)
{
foreach (GameServer_Object gameserver in GameServer_Management.server_collection)
{
comboboxGameserverList.Items.Add(gameserver.SERVER_name_friendly);
}
}
}
private void ServerControl_Activated(object sender, EventArgs e)
{
RefreshData();
}
private void chkStandaloneMode_OnValueChange(object sender, EventArgs e)
{
if (chkStandaloneMode.Value == true)
{
consolePanel.Visible = false;
}
else
{
consolePanel.Visible = true;
}
}
//===================================================================================//
// SERVER CONTROL: //
//===================================================================================//
private void LaunchServer(string SERVER_executable, string SERVER_launch_arguments, Action<string> redirectedOutputCallback = null, TextReader input = null, string argWorkingDirectory = null, EventHandler onExitedCallback = null, CancellationToken cancelToken = default(CancellationToken))
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.Arguments = SERVER_launch_arguments;
startInfo.FileName = SERVER_executable;
if (redirectedOutputCallback != null) //Redirect Output to somewhere else.
{
startInfo.UseShellExecute = false; //Redirect the programs.
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.ErrorDialog = false;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (var process = Process.Start(startInfo))
{
if (onExitedCallback != null)
{
process.EnableRaisingEvents = true;
process.Exited += onExitedCallback;
}
if (cancelToken != null)
{
cancelToken.Register(() =>
{
StopProgramByAttachingToItsConsoleAndIssuingCtrlCEvent(process);
redirectedOutputCallback?.Invoke("Shutting down the server...");
});
}
process.OutputDataReceived += (sender, args) =>
{
if (!string.IsNullOrEmpty(args?.Data))
redirectedOutputCallback(args.Data);
};
process.BeginOutputReadLine();
process.ErrorDataReceived += (sender, args) =>
{
if (!string.IsNullOrEmpty(args?.Data))
redirectedOutputCallback(args.Data);
};
process.BeginErrorReadLine();
//For whenever input is needed
string line;
while (input != null && (line = input.ReadLine()) != null)
process.StandardInput.WriteLine(line);
process.WaitForExit();
}
}
catch
{
StringBuilder errorDialog = new StringBuilder();
errorDialog.Append("There was an error launching the following server:\n")
.Append(startInfo.FileName)
.Append("\n\n")
.Append("[Retry]: Attempt to start the same server again.\n")
.Append("[Cancel]: Cancel attempting to start server.");
}
}
else //No not redirect output somewhere else
{
startInfo.UseShellExecute = true; //Execute the programs.
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
StringBuilder errorDialog = new StringBuilder();
errorDialog.Append("There was an error launching the following server:\n")
.Append(startInfo.FileName)
.Append("\n\n")
.Append("[Retry]: Attempt to start the same server again.\n")
.Append("[Cancel]: Cancel attempting to start server.");
}
}
}
private void btnStartServer_Click(object sender, EventArgs e)
{
if (GameServer_Management.server_collection != null)
{
foreach (GameServer_Object gameserver in GameServer_Management.server_collection)
{
if (gameserver.SERVER_name_friendly == comboboxGameserverList.Text)
{
//SOURCE ENGINE HANDLER
if (gameserver.ENGINE_type == "SOURCE")
{
//Check to see if the gameserver needs to be run with a visible console, or directly controlled by Borealis.
if (chkStandaloneMode.Value == true) //To be hopefully depreciated soon. Only needed right now as a fallback option to server operators.
{
LaunchServer(gameserver.DIR_install_location + #"\steamapps\common" + gameserver.DIR_root + gameserver.SERVER_executable,
string.Format("{0} +port {1} +map {2} +maxplayers {3}",
gameserver.SERVER_launch_arguments,
gameserver.SERVER_port,
gameserver.GAME_map,
gameserver.GAME_maxplayers));
}
else
{
LaunchServer(gameserver.DIR_install_location + #"\steamapps\common" + gameserver.DIR_root + gameserver.SERVER_executable,
string.Format("{0} +port {1} +map {2} +maxplayers {3}",
gameserver.SERVER_launch_arguments,
gameserver.SERVER_port,
gameserver.GAME_map,
gameserver.GAME_maxplayers));
}
}
//SOURCE ENGINE HANDLER
if (gameserver.ENGINE_type == "UNREAL")
{
//Check to see if the gameserver needs to be run with a visible console, or directly controlled by Borealis.
if (chkStandaloneMode.Value == true)
{
/*LaunchServer(gameserver.DIR_install_location + #"\steamapps\common" + gameserver.DIR_root + gameserver.SERVER_executable,
string.Format("{0}?{1}?Port={2}?MaxPlayers={3}",
gameserver.GAME_map,
gameserver.SERVER_launch_arguments,
gameserver.SERVER_port,
gameserver.GAME_maxplayers));
*/
}
else
{
MetroMessageBox.Show(BorealisServerManager.ActiveForm, "Unfortunately Borealis cannot directly control console output at this time; instead, please launch the server in 'standalone mode'.", "Unable to launch server within Borealis.", MessageBoxButtons.OK, MessageBoxIcon.Error);
/*
chkAutoRestart.Visible = false;
lblAutoRestart.Visible = false;
chkStandaloneMode.Visible = false;
lblStandaloneMode.Visible = false;
btnStartServer.Enabled = false;
btnStopServer.Visible = true;
consolePanel.Visible = true;
txtboxIssueCommand.Visible = true;
txtboxIssueCommand.Text = " > Enter a Command";
txtboxIssueCommand.Enabled = true;
Execute(Environment.CurrentDirectory + gameserver.SERVER_executable, gameserver.SERVER_launch_arguments, true);
*/
}
}
}
}
}
}
private void btnStopServer_Click(object sender, EventArgs e)
{
btnStopServer.Visible = false;
btnStartServer.Enabled = true;
chkAutoRestart.Visible = true;
lblAutoRestart.Visible = true;
txtboxIssueCommand.Visible = false;
consoleOutputList.Items.Clear();
txtboxIssueCommand.Text = " > Server is Not Running";
txtboxIssueCommand.Enabled = false;
cancelToken.Cancel();
backgroundWorker01.RunWorkerCompleted += (sender2, e2) =>
{
btnStopServer.Enabled = false;
btnStartServer.Enabled = true;
txtboxIssueCommand.Enabled = false;
txtboxIssueCommand.Text = "> Server is not running";
consoleOutputList.Items.Add("Server stopped...");
};
}
private void txtboxIssueCommand_Enter(object sender, EventArgs e)
{
txtboxIssueCommand.Text = "";
}
private void txtboxIssueCommand_MouseClick(object sender, MouseEventArgs e)
{
txtboxIssueCommand.Text = "";
}
private void backgroundWorker01_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
bool stopped = false;
//It's an assumption that these 3 elements MUST exist
var asyncCallback = comboboxGameserverList.BeginInvoke((Func<string[]>)delegate ()
{
return new string[] {
//GameServerXMLData( comboboxGameserverList.SelectedItem as string, "installation_folder" ),
//GameServerXMLData( comboboxGameserverList.SelectedItem as string, "default_launchscript" ),
//GameServerXMLData( comboboxGameserverList.SelectedItem as string, "binaries" )
};
});
asyncCallback.AsyncWaitHandle.WaitOne();
var serverParams = comboboxGameserverList.EndInvoke(asyncCallback) as string[];
Action<string> textAddCallback = (args) =>
{
consoleOutputList.BeginInvoke((Action)delegate ()
{
consoleOutputList.Items.Add(args);
});
};
EventHandler exitedHandler = (sender2, e2) =>
{
if (!cancelToken.IsCancellationRequested)
{
//Wait a little until we restart the server
consoleOutputList.BeginInvoke((Action)delegate ()
{
consoleOutputList.Items.Add(Environment.NewLine);
consoleOutputList.Items.Add(Environment.NewLine);
consoleOutputList.Items.Add(Environment.NewLine);
consoleOutputList.Items.Add("An error occured and the process has crashed. Auto-restarting in 5 seconds...");
//Scroll to the bottom
consoleOutputList.TopIndex = consoleOutputList.Items.Count - 1;
consoleOutputList.Items.Add(Environment.NewLine);
consoleOutputList.Items.Add(Environment.NewLine);
consoleOutputList.Items.Add(Environment.NewLine);
});
Thread.Sleep(5000);
}
else
stopped = true;
};
while (chkAutoRestart.Value && !stopped)
LaunchServer(serverParams[0] + serverParams[2], serverParams[1], textAddCallback, null, serverParams[0], chkAutoRestart.Value ? exitedHandler : null, chkAutoRestart.Value ? cancelToken.Token : default(System.Threading.CancellationToken));
}
}
}
void device_DeviceArrived(ProximityDevice sender)
{
//Compatible Device enters area
if (stance == WriteStage.PREP)
{
System.Diagnostics.Debug.WriteLine("Writestages won");
//Perhaps here
updateStatusRectangle(Colors.Yellow);
stance = WriteStage.WRITING;
updateStatusText("Writing...");
writeToTag(msg);
}
else
{
updateReceivedText("Device connected!");
}
}
private void MessageReceivedHandler(ProximityDevice sender, ProximityMessage message)
{
System.Diagnostics.Debug.WriteLine("Handler ran");
var rawMsg = message.Data.ToArray();
var ndefMessage = NdefMessage.FromByteArray(rawMsg);
foreach (NdefRecord record in ndefMessage)
{
System.Diagnostics.Debug.WriteLine("Record type: " + Encoding.UTF8.GetString(record.Type, 0, record.Type.Length));
var specType = record.CheckSpecializedType(false);
if (specType == typeof(NdefTextRecord))
{
var textrec = new NdefTextRecord(record);
updateReceivedText(textrec.Text);
}
}
}
The above event and handler are executed when the phone comes into contact with an NFC device. For intents and purposes in this app, I need to ensure that before writing to a card, if it already has content, it will prompt the user to verify overwriting the data. I commented where I think it should go, but as far as checking for the Message, I'm not sure how to go about it. I can't call the handler without the ProximityMessage, and I don't know of another way to view the message.
The Question: Is it possible to call the MessageReceivedHandler (or check the message at all), from device_DeviceArrived?
(Note: Debug.Writelines are for test purposes, and this is just a quick NFC writer I'm throwing together).
UPDATE: In attempting to find a work around, I ran into a different problem.
public bool promptUserForOverwrite()
{
bool response = false;
Dispatcher.BeginInvoke(() =>
{
MessageBoxResult cc = MessageBox.Show("You are about to overwrite data. Proceed?", "Overwrite?", MessageBoxButton.OKCancel);
if (cc == MessageBoxResult.OK)
{
System.Diagnostics.Debug.WriteLine("MessageBox OK result");
response = true;
}
});
return response;
}
private void MessageReceivedHandler(ProximityDevice sender, ProximityMessage message)
{
System.Diagnostics.Debug.WriteLine("Handler ran");
var rawMsg = message.Data.ToArray();
var ndefMessage = NdefMessage.FromByteArray(rawMsg);
foreach (NdefRecord record in ndefMessage)
{
System.Diagnostics.Debug.WriteLine("Record type: " + Encoding.UTF8.GetString(record.Type, 0, record.Type.Length));
var specType = record.CheckSpecializedType(false);
if (specType == typeof(NdefTextRecord))
{
var textrec = new NdefTextRecord(record);
updateReceivedText(textrec.Text);
}
}
bool pow = promptUserForOverwrite();
if (!pow)
{
System.Diagnostics.Debug.WriteLine("Prompt returned");
//This always hits - pow is always false.
}
if (stance == WriteStage.WRITING && pow)
{
//writeToTag(msg);
}
}
This would work as a work around; the problem is the beginInvoke method. I need it for cross thread access, but used like this seems to make it run at a later time (when the thread is free?). The bool pow is always false, even after I click ok on the messagebox (debugged, and it does get the result, but after I can no longer use it). Is there an alternative that I can use for the Dispatcher?
Ugly, but I have this working. You need to get a TaskScheduler from the UI thread, so declare a
private TaskScheduler sched;
and then on the OnLoaded event for the page
sched = TaskScheduler.FromCurrentSynchronizationContext();
Then your methods
public async Task<bool> promptUserForOverwrite()
{
return false;
}
private async void MessageReceivedHandler(ProximityDevice sender, ProximityMessage message)
{
System.Diagnostics.Debug.WriteLine("Handler ran");
var rawMsg = message.Data.ToArray();
var ndefMessage = NdefMessage.FromByteArray(rawMsg);
foreach (NdefRecord record in ndefMessage)
{
System.Diagnostics.Debug.WriteLine("Record type: " + Encoding.UTF8.GetString(record.Type, 0, record.Type.Length));
var specType = record.CheckSpecializedType(false);
if (specType == typeof(NdefTextRecord))
{
var textrec = new NdefTextRecord(record);
updateReceivedText(textrec.Text);
}
}
var task = promptUserForOverwrite();
var pow = await task.ContinueWith(t =>
{
MessageBoxResult cc = MessageBox.Show("You are about to overwrite data. Proceed?", "Overwrite?", MessageBoxButton.OKCancel);
if (cc == MessageBoxResult.OK)
{
System.Diagnostics.Debug.WriteLine("MessageBox OK result");
return true;
}
else
{
return false;
}
}, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, sched);
if (!pow)
{
System.Diagnostics.Debug.WriteLine("Prompt returned");
//This always hits - pow is always false.
}
if (stance == WriteStage.WRITING && pow)
{
//writeToTag(msg);
}
}
On my windows phone 7 Mango app I use the Microsoft.Live and Microsoft.Live.Controls references to download and upload on Skydrive. Logging in and uploading files works fine but whenever I call the "client.GetAsync("/me/skydrive/files");" on the LiveConnectClient the Callback result is empty just containing the error: "An error occurred while retrieving the resource. Try again later."
I try to retrieve the list of files with this method.
This error suddenly occured without any change on the source code of the app (I think..) which worked perfectly fine for quite a while until recently. At least I didn't change the code section for up- or downloading.
I tried out the "two-step verification" of Skydrive, still the same: can log in but not download.
Also updating the Live refercences to 5.5 didn't change anything.
Here is the code I use. The error occurs in the "getDir_Callback" in the "e" variable.
Scopes (wl.skydrive wl.skydrive_update) and clientId are specified in the SignInButton which triggers "btnSignin_SessionChanged".
private void btnSignin_SessionChanged(object sender, LiveConnectSessionChangedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
processing = true;
client = new LiveConnectClient(e.Session);
client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(UploadCompleted);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompleted);
client.DownloadProgressChanged += new EventHandler<LiveDownloadProgressChangedEventArgs>(client_DownloadProgressChanged);
infoTextBlock.Text = "Signed in. Retrieving file IDs...";
client.GetAsync("me");
}
else
{
connected = false;
infoTextBlock.Text = "Not signed into Skydrive.";
client = null;
}
}
private void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
if (e.Error == null)
{
client.GetCompleted -= new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.GetCompleted += getDir_Callback;
client.GetAsync("/me/skydrive/files");
client_id = (string)e.Result["id"];
if (e.Result.ContainsKey("first_name") && e.Result.ContainsKey("last_name"))
{
if (e.Result["first_name"] != null && e.Result["last_name"] != null)
{
infoTextBlock.Text = "Hello, " +
e.Result["first_name"].ToString() + " " +
e.Result["last_name"].ToString() + "!";
}
}
else
{
infoTextBlock.Text = "Hello, signed-in user!";
}
}
else
{
infoTextBlock.Text = "Error calling API: " +
e.Error.ToString();
}
processing = false;
}
public void getDir_Callback(object s, LiveOperationCompletedEventArgs e)
{
client.GetCompleted -= getDir_Callback;
//filling Dictionary with IDs of every file on SkyDrive
if (e.Result != null)
ParseDir(e);
if (!String.IsNullOrEmpty(e.Error.Message))
infoTextBlock.Text = e.Error.Message;
else
infoTextBlock.Text = "File-IDs loaded";
}
Yo shall use client.GetAsync("me/SkyDrive/files", null) in place of client.GetAsync("me");
This is my code and it works fine.
private void btnSignIn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
try
{
if (e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
{
client = new LiveConnectClient(e.Session);
client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_GetCompleted);
client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_UploadCompleted);
client.GetAsync("me/SkyDrive/files", null);
client.DownloadAsync("me/SkyDrive", null);
canUpload = true;
ls = e.Session;
}
else
{
if (client != null)
{
MessageBox.Show("Signed out successfully!");
client.GetCompleted -= client_GetCompleted;
client.UploadCompleted -= client_UploadCompleted;
canUpload = false;
}
else
{
canUpload = false;
}
client = null;
canUpload = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void client_GetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
try
{
List<System.Object> listItems = new List<object>();
if (e.Result != null)
{
listItems = e.Result["data"] as List<object>;
for (int x = 0; x < listItems.Count(); x++)
{
Dictionary<string, object> file1 = listItems[x] as Dictionary<string, object>;
string fileName = file1["name"].ToString();
if (fileName == lstmeasu[0].Title)
{
folderID = file1["id"].ToString();
folderAlredyPresent = true;
}
}
}
}
catch (Exception)
{
MessageBox.Show("An error occured in getting skydrive folders, please try again later.");
}
}
It miraculously works again without me changing any code. It seems to be resolved on the other end. I have no clue what was wrong.