I'm working with a 3rd party, command-line tool called "sam-ba" v3.5 (available here for free). It's a C++ / QML command line tool that interfaces with a hardware module to read/write data. Output from commands, in most cases, is sent to Standard Error.
I have a C# / .NET application that creates a Process object to execute the sam-ba tool and run commands. Executing the commands works as expected. What doesn't always work is the redirect of the Standard Error output. In some commands, part or all of the output is not received by the C# application. For example, here is the execution of a command using the sam-ba tool directly at the Windows 10 command line:
C:\Temp\Stuff\sam-ba_3.5>sam-ba -p serial:COM5 -d sama5d3 -m version
Error: Cannot open invalid port 'COM5'
Cannot open invalid port 'COM5'
Here is some simple code from a C# application to create a Process object to execute the sam-ba tool with the same command:
Process p = new Process
{
StartInfo = new ProcessStartInfo("sam-ba.exe", "-p serial:COM5 -d sama5d3 -m version")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
p.Start();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Console.WriteLine("Standard Out: " + output);
Console.WriteLine("Standard Error: " + error);
The Output of the C# application:
Standard Out:
Standard Error: Cannot open invalid port 'COM5'
In this simple example, only 1 of the output lines is redirected to Standard Error while the other is not. I've tried many different commands and results are mixed. Sometimes I get everything, sometimes partial output, sometimes no output.
Now ... here's the real issue. The following is a python script (v3.8) that does exactly what the C# application is doing:
import subprocess
import sys
result = subprocess.run("sam-ba.exe -p serial:COM5 -d sama5d3 -m version", capture_output=True, text=True)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
This script always returns the correct output to standard error. BUT ... when I run this script from the C# app to create a chain of C# -> python -> sam-ba, I get the same issue of output missing from the stream.
This has led me to 2 conclusions:
Something in that sam-ba tool is different about the way it is outputting its text. Format, content, ... something. There's an inconsistency going on somewhere in that code
Something is different about the environment created by the C# Process object when executing external applications that doesn't happen when the external application is run directly. Otherwise, why would the python script get all the output when run directly, but not when run through the C# Process object?
It is #2 that has brought me here. I'm looking for any insight on how to diagnose this. Something I'm doing wrong, settings I can try within the Process object, thoughts on how data can go into a stream and not come out on redirect, or if anyone has ever seen something like this before and how they resolved it.
UPDATE
Got a hold of the sam-ba tool's source code. The output the C# app is not capturing is coming from the QML files. They are using this 'print()' method that I can't really find any details on. The output the C# app can capture is being delivered back to the C++ side via signals and then sent to standard error. This feeds back into my conclusion #1 where they have inconsistencies in their code.
Still, this potentially means there is a conflict between C# and QT/QML, which would explain why the Python script gets the QML output, but the C# app does not.
The following uses ShellExecute instead of CreateProcess when running
process. When using ShellExecute one can't re-direct StandardOutput and/or StandardError for Process. To work around this, both StandardOutput and StandardError are re-directed to a temp file, and then the data is read from the temp file--which seems to result in the same output that one sees when running from a cmd window.
Note: In the following code it's necessary to use %windir%\system32\cmd.exe (ex: C:\Windows\system32\cmd.exe) with the /c option. See the usage section below.
Add using statement: using System.Diagnostics;
Then try the following:
public string RunProcess(string fqExePath, string arguments, bool runAsAdministrator = false)
{
string result = string.Empty;
string tempFilename = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "tempSam-ba.txt");
string tempArguments = arguments;
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
return "Error: fqExePath not specified";
}
//redirect both StandardOutput and StandardError to a temp file
if (!arguments.Contains("2>&1"))
{
tempArguments += String.Format(" {0} {1} {2}", #"1>", tempFilename, #"2>&1");
}
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(fqExePath, tempArguments);
if (runAsAdministrator)
{
startInfo.Verb = "runas"; //elevates permissions
}//if
//set environment variables
//pStartInfo.EnvironmentVariables["SomeVar"] = "someValue";
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = true; //use ShellExecute instead of CreateProcess
startInfo.CreateNoWindow = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fqExePath);
using (Process p = Process.Start(startInfo))
{
//start
p.Start();
//waits until the process is finished before continuing
p.WaitForExit();
}
//read output from temp file
//file may still be in use, so try to read it.
//if it is still in use, sleep and try again
if (System.IO.File.Exists(tempFilename))
{
string errMsg = string.Empty;
int count = 0;
do
{
//re-initialize
errMsg = string.Empty;
try
{
result = System.IO.File.ReadAllText(tempFilename);
Debug.WriteLine(result);
}
catch(System.IO.IOException ex)
{
errMsg = ex.Message;
}
catch (Exception ex)
{
errMsg = ex.Message;
}
System.Threading.Thread.Sleep(125);
count += 1; //increment
} while (!String.IsNullOrEmpty(errMsg) && count < 10);
//delete temp file
System.IO.File.Delete(tempFilename);
}
return result;
}
Usage:
RunProcess(#"C:\Windows\system32\cmd.exe", #"/c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version");
Note: /c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version is the value of the process "Argument" property.
Update:
Option 2:
Here's a solution that uses named pipes. Process is used to redirect the output to a named pipe instead of a file. One creates a named pipe "server" which listens for a connection from a client.Then System.Diagnostics.Process is used to run the desired command and redirect the output to the named pipe server. The "server" reads the output, and then raises event "DataReceived" which will return the data to any subscribers.
The named pipe server code is from here, however I've modified it. I've added numerous events--which can be subscribed to. I've also added the ability for the server to shut itself down after it's finished reading the data by setting "ShutdownWhenOperationComplete" to "true".
Create a class named: HelperNamedPipeServer.cs
HelperNamedPipeServer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Pipes;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Security.Principal;
namespace ProcessTest
{
public class HelperNamedPipeServer : IDisposable
{
//delegates
public delegate void EventHandlerClientConnected(object sender, bool e);
public delegate void EventHandlerDataReceived(object sender, string data);
public delegate void EventHandlerOperationCompleted(object sender, bool e);
public delegate void EventHandlerMessageComplete(object sender, bool e);
public delegate void EventHandlerReadComplete(object sender, bool e);
public delegate void EventHandlerServerShutdown(object sender, bool e);
public delegate void EventHandlerServerStarted(object sender, bool e);
//event that subscribers can subscribe to
public event EventHandlerClientConnected ClientConnected;
public event EventHandlerDataReceived DataReceived;
public event EventHandlerMessageComplete MessageReadComplete;
public event EventHandlerOperationCompleted OperationCompleted;
public event EventHandlerReadComplete ReadComplete;
public event EventHandlerServerShutdown ServerShutdown;
public event EventHandlerServerStarted ServerStarted;
public bool IsClientConnected
{
get
{
if (_pipeServer == null)
{
return false;
}
else
{
return _pipeServer.IsConnected;
}
}
}
public string PipeName { get; set; } = string.Empty;
public bool ShutdownWhenOperationComplete { get; set; } = false;
//private int _bufferSize = 4096;
private int _bufferSize = 65535;
//private volatile NamedPipeServerStream _pipeServer = null;
private NamedPipeServerStream _pipeServer = null;
public HelperNamedPipeServer()
{
PipeName = "sam-ba-pipe";
}
public HelperNamedPipeServer(string pipeName)
{
PipeName = pipeName;
}
private NamedPipeServerStream CreateNamedPipeServerStream(string pipeName)
{
//named pipe with security
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null); //member of Administrators group
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); //everyone
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); //member of Users group
//PipeAccessRule rule = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
//PipeSecurity pSec = new PipeSecurity();
//pSec.AddAccessRule(rule);
//named pipe - with specified security
//return new NamedPipeServerStream(PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, _bufferSize, _bufferSize, pSec);
//named pipe - access for everyone
//return new System.IO.Pipes.NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
return new System.IO.Pipes.NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
}
public void Dispose()
{
Shutdown();
}
private void OnClientConnected()
{
LogMsg("OnClientConnected");
//raise event
if (ClientConnected != null)
ClientConnected(this, true);
}
private void OnDataReceived(string data)
{
LogMsg("OnClientConnected");
//raise event
if (DataReceived != null && !String.IsNullOrEmpty(data))
{
if (DataReceived != null)
DataReceived(this, data);
}
}
private void OnMessageReadComplete()
{
LogMsg("OnMessageReadComplete");
//raise event
if (MessageReadComplete != null)
MessageReadComplete(this, true);
}
private void OnOperationCompleted()
{
LogMsg("OnOperationCompleted");
//raise event
if (OperationCompleted != null)
OperationCompleted(this, true);
}
private void OnReadComplete()
{
LogMsg("OnReadComplete");
//raise event
if (ReadComplete != null)
ReadComplete(this, true);
}
private void OnServerShutdown()
{
LogMsg("OnServerShutdown");
//raise event
if (ServerShutdown != null)
ServerShutdown(this, true);
}
private void OnServerStarted()
{
LogMsg("OnServerStarted");
//raise event
if (ServerStarted != null)
ServerStarted(this, true);
}
private async void DoConnectionLoop(IAsyncResult result)
{ //wait for connection, then process the data
if (!result.IsCompleted) return;
if (_pipeServer == null) return;
//IOException = pipe is broken
//ObjectDisposedException = cannot access closed pipe
//OperationCanceledException - read was canceled
//accept client connection
try
{
//client connected - stop waiting for connection
_pipeServer.EndWaitForConnection(result);
OnClientConnected(); //raise event
}
catch (IOException) { RebuildNamedPipe(); return; }
catch (ObjectDisposedException) { RebuildNamedPipe(); return; }
catch (OperationCanceledException) { RebuildNamedPipe(); return; }
while (IsClientConnected)
{
if (_pipeServer == null) break;
try
{
// read from client
string clientMessage = await ReadClientMessageAsync(_pipeServer);
OnDataReceived(clientMessage); //raise event
}
catch (IOException) { RebuildNamedPipe(); return; }
catch (ObjectDisposedException) { RebuildNamedPipe(); return; }
catch (OperationCanceledException) { RebuildNamedPipe(); return; }
}
//raise event
OnOperationCompleted();
if (!ShutdownWhenOperationComplete)
{
//client disconnected. start listening for clients again
if (_pipeServer != null)
RebuildNamedPipe();
}
else
{
Shutdown();
}
}
private void LogMsg(string msg)
{
//ToDo: log message
string output = String.Format("{0} - {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), msg);
//ToDo: uncomment this line, if desired
//Debug.WriteLine(output);
}
private void RebuildNamedPipe()
{
Shutdown();
_pipeServer = CreateNamedPipeServerStream(PipeName);
_pipeServer.BeginWaitForConnection(DoConnectionLoop, null);
}
private async Task<string> ReadClientMessageAsync(NamedPipeServerStream stream)
{
byte[] buffer = null;
string clientMsg = string.Empty;
StringBuilder sb = new StringBuilder();
int msgIndex = 0;
int read = 0;
LogMsg("Reading message...");
if (stream.ReadMode == PipeTransmissionMode.Byte)
{
LogMsg("PipeTransmissionMode.Byte");
//byte mode ignores message boundaries
do
{
//create instance
buffer = new byte[_bufferSize];
read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read > 0)
{
clientMsg = Encoding.UTF8.GetString(buffer, 0, read);
//string clientMsg = Encoding.Default.GetString(buffer, 0, read);
//remove newline
//clientMsg = System.Text.RegularExpressions.Regex.Replace(clientString, #"\r\n|\t|\n|\r|", "");
//LogMsg("clientMsg [" + msgIndex + "]: " + clientMsg);
sb.Append(clientMsg);
msgIndex += 1; //increment
}
} while (read > 0);
//raise event
OnReadComplete();
OnMessageReadComplete();
}
else if (stream.ReadMode == PipeTransmissionMode.Message)
{
LogMsg("PipeTransmissionMode.Message");
do
{
do
{
//create instance
buffer = new byte[_bufferSize];
read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read > 0)
{
clientMsg = Encoding.UTF8.GetString(buffer, 0, read);
//string clientMsg = Encoding.Default.GetString(buffer, 0, read);
//remove newline
//clientMsg = System.Text.RegularExpressions.Regex.Replace(clientString, #"\r\n|\t|\n|\r|", "");
//LogMsg("clientMsg [" + msgIndex + "]: " + clientMsg);
sb.Append(clientMsg);
msgIndex += 1; //increment
}
} while (!stream.IsMessageComplete);
//raise event
OnMessageReadComplete();
} while (read > 0);
//raise event
OnReadComplete();
LogMsg("message completed");
}
return sb.ToString();
}
private void Shutdown()
{
LogMsg("Shutting down named pipe server");
if (_pipeServer != null)
{
try { _pipeServer.Close(); } catch { }
try { _pipeServer.Dispose(); } catch { }
_pipeServer = null;
}
}
public void StartServer(object obj = null)
{
LogMsg("Info: Starting named pipe server...");
_pipeServer = CreateNamedPipeServerStream(PipeName);
_pipeServer.BeginWaitForConnection(DoConnectionLoop, null);
}
public void StopServer()
{
Shutdown();
OnServerShutdown(); //raise event
LogMsg("Info: Server shutdown.");
}
}
}
Next, I've created a "Helper" class that contains the code to start the named pipe server, run the command using Process, and return the data. There are three ways to get the data. It's returned by the method, one can subscribe to the "DataReceived" event, or once the method completes, the data will be in property "Data".
Helper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO.Pipes;
using System.IO;
using System.Threading;
namespace ProcessTest
{
public class Helper : IDisposable
{
public delegate void EventHandlerDataReceived(object sender, string data);
//event that subscribers can subscribe to
public event EventHandlerDataReceived DataReceived;
private StringBuilder _sbData = new StringBuilder();
private HelperNamedPipeServer _helperNamedPipeServer = null;
private bool _namedPipeServerOperationComplete = false;
public string Data { get; private set; } = string.Empty;
public Helper()
{
}
private void OnDataReceived(string data)
{
if (!String.IsNullOrEmpty(data) && DataReceived != null)
{
DataReceived(this, data);
//Debug.Write("Data: " + data);
}
}
public void Dispose()
{
ShutdownNamedPipeServer();
}
public async Task<string> RunSambaNamedPipesAsync(string fqExePath, string arguments, string pipeName = "sam-ba-pipe", string serverName = ".", bool runAsAdministrator = false)
{
string result = string.Empty;
string tempArguments = arguments;
//re-initialize
_namedPipeServerOperationComplete = false;
_sbData = new StringBuilder();
Data = string.Empty;
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
return "fqExePath not specified";
}
//create new instance
_helperNamedPipeServer = new HelperNamedPipeServer(pipeName);
_helperNamedPipeServer.ShutdownWhenOperationComplete = true;
//subscribe to events
_helperNamedPipeServer.DataReceived += HelperNamedPipeServer_DataReceived;
_helperNamedPipeServer.OperationCompleted += HelperNamedPipeServer_OperationCompleted;
//start named pipe server on it's own thread
Thread t = new Thread(_helperNamedPipeServer.StartServer);
t.Start();
//get pipe name to use with Process
//this is where output from the process
//will be redirected to
string fqNamedPipe = string.Empty;
if (String.IsNullOrEmpty(serverName))
{
fqNamedPipe = String.Format(#"\\{0}\pipe\{1}", serverName, pipeName);
}
else
{
fqNamedPipe = String.Format(#"\\{0}\pipe\{1}", ".", pipeName);
}
//redirect both StandardOutput and StandardError to named pipe
if (!arguments.Contains("2>&1"))
{
tempArguments += String.Format(" {0} {1} {2}", #"1>", fqNamedPipe, #"2>&1");
}
//run Process
RunProcess(fqExePath, tempArguments, runAsAdministrator);
while (!_namedPipeServerOperationComplete)
{
await Task.Delay(125);
}
//set value
Data = _sbData.ToString();
return Data;
}
public void RunProcess(string fqExePath, string arguments, bool runAsAdministrator = false)
{
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
throw new Exception( "Error: fqExePath not specified");
}
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(fqExePath, arguments);
if (runAsAdministrator)
{
startInfo.Verb = "runas"; //elevates permissions
}//if
//set environment variables
//pStartInfo.EnvironmentVariables["SomeVar"] = "someValue";
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = true; //use ShellExecute instead of CreateProcess
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fqExePath);
using (Process p = Process.Start(startInfo))
{
//start
p.Start();
//waits until the process is finished before continuing
p.WaitForExit();
}
}
private void HelperNamedPipeServer_OperationCompleted(object sender, bool e)
{
//Debug.WriteLine("Info: Named pipe server - Operation completed.");
//set value
Data = _sbData.ToString();
//set value
_namedPipeServerOperationComplete = true;
}
private void HelperNamedPipeServer_DataReceived(object sender, string data)
{
Debug.WriteLine("Info: Data received from named pipe server.");
if (!String.IsNullOrEmpty(data))
{
//append
_sbData.Append(data.TrimEnd('\0'));
//send data to subscribers
OnDataReceived(data);
}
}
private void ShutdownNamedPipeServer()
{
Debug.WriteLine("Info: ShutdownNamedPipeServer");
try
{
if (_helperNamedPipeServer != null)
{
//unsubscribe from events
_helperNamedPipeServer.DataReceived -= HelperNamedPipeServer_DataReceived;
_helperNamedPipeServer.OperationCompleted -= HelperNamedPipeServer_OperationCompleted;
_helperNamedPipeServer.Dispose();
_helperNamedPipeServer = null;
}
}
catch (Exception ex)
{
}
}
}
}
Usage:
private async void btnRunUsingNamedPipes_Click(object sender, EventArgs e)
{
//Button name: btnRunUsingNamedPipes
using (Helper helper = new Helper())
{
//subscribe to event
helper.DataReceived += Helper_DataReceived;
var result = await helper.RunSambaNamedPipesAsync(#"C:\Windows\system32\cmd.exe", #"/c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version");
Debug.WriteLine("Result: " + result);
//unsubscribe from event
helper.DataReceived -= Helper_DataReceived;
}
}
private void Helper_DataReceived(object sender, string data)
{
//System.Diagnostics.Debug.WriteLine(data);
//RichTextBox name: richTextBoxOutput
if (richTextBoxOutput.InvokeRequired)
{
richTextBoxOutput.Invoke((MethodInvoker)delegate
{
richTextBoxOutput.Text = data;
richTextBoxOutput.Refresh();
});
}
}
Resources:
When do we need to set ProcessStartInfo.UseShellExecute to True?
Redirecting error messages from Command Prompt: STDERR/STDOUT
PipeTransmissionMode.Message: How do .NET named pipes distinguish between messages?
WellKnownSidType Enum
I`m trying to see what is wrong with my code for a tcp connection to a server in xamarin forms using the mvvm architecture. The client is connecting but when using streamReader.readLine() i get the following error:
System.IO.IOException: Unable to read data from the transport connection: Operation on non-blocking socket would block.
I´m using xamarin forms and System.Net.Sockets for the TcpClient instance. I´m trying synchronous communication for testing with the server.
I Have a TcpServer program proven to work with other devices. Below is some code to do the communication
The ViewModel of the connection properties page has the following code:
public ICommand TestConnectionCommand { get { return new
RelayCommand(sendMsg); } }
public ICommand ConnectCommand { get; set; }
public void sendMsg()
{
this.Response = new Response();
this.EthernetConn.Port = 9091;
this.EthernetConn.Ip = SelectedDevice.Ip;
if (String.IsNullOrEmpty(EthernetConn.Ip))
{
Application.Current.MainPage.DisplayAlert(Languages.Error, Languages.IpValidation, Languages.Accept);
return;
}
if (String.IsNullOrEmpty(EthernetConn.Timeout))
{
Application.Current.MainPage.DisplayAlert(Languages.Error, Languages.TimeoutValidation, Languages.Accept);
return;
}
this.EthernetConn.Message = "TESTING\n";
tcpService.Initialize(this.ethernetConn);
this.Response.Message = tcpService.sendMessage(this.EthernetConn);
if (this.Response.Message.Contains("FormatException"))
{
this.Response.IsSuccess = false;
Application.Current.MainPage.DisplayAlert(Languages.Error, Languages.FormatValidation, Languages.Accept);
this.IsRunning = false;
this.IsEnabled = true;
return;
}
else if (this.Response.Message.Contains("ArgumentNullException"))
{
this.Response.IsSuccess = false;
Application.Current.MainPage.DisplayAlert(Languages.Error, this.Response.Message, Languages.Accept);
this.IsRunning = false;
this.IsEnabled = true;
return;
}
else if (this.Response.Message.Contains("SocketException"))
{
this.Response.IsSuccess = false;
Application.Current.MainPage.DisplayAlert(Languages.Error, this.Response.Message, Languages.Accept);
this.IsRunning = false;
this.IsEnabled = true;
return;
}
else if (this.Response.Message.Contains("OK"))
{
this.Response.IsSuccess = true;
}
if (this.Response.IsSuccess)
{
Application.Current.MainPage.DisplayAlert(Languages.Success, Languages.ConnEstablished, Languages.Accept);
this.IsRunning = false;
this.IsEnabled = true;
}
else
{
Application.Current.MainPage.DisplayAlert(Languages.Error, Languages.ConnFailed, Languages.Accept);
this.IsRunning = false;
this.IsEnabled = true;
}
}
tcpService is an instance of TCPService, which has the Initialize function to establish connection and the sendMessage function where the error appears to be:
public void Initialize(EthernetConnection eth)
{
client.SendTimeout = Convert.ToInt16(eth.Timeout);
client.ReceiveTimeout = Convert.ToInt16(eth.Timeout);
client.Connect(eth.Ip, eth.Port);
}
public string sendMessage(EthernetConnection eth)
{
string response = String.Empty;
try
{
if (!client.Connected)
{
return null;
}
var writer = new StreamWriter(client.GetStream());
writer.WriteLine(eth.Message);
//Here is where the problem seems to be:
var reader = new StreamReader(client.GetStream());
response = reader.ReadLine();
return response;
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
return "ArgumentNullException: " + e;
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
return "SocketException:: " + e;
}
catch (FormatException e)
{
Console.WriteLine("SocketException: {0}", e);
return "FormatException: " + e;
}
}
I would be expecting to read the data in the ReadLine function. Also, I had some printing on the console on the server side if it recieves data and i'm seeing it is not.
Could somebody help me on where could I be wrong? A proven solution is also fine!
Thank you so much!
Whenever you use StreamWriter you need to Flush() the contents of the stream. I'll quote MSDN as the reason becomes quite clear:
Clears all buffers for the current writer and causes any buffered data
to be written to the underlying stream.
You can use it like this:
var writer = new StreamWriter(client.GetStream());
writer.AutoFlush = true;
writer.WriteLine(eth.Message);
writer.Flush();
And there is a simple demo you can check:
https://thuru.net/2012/01/07/simple-clientserver-in-c/
I a trying to find a hostheader is already under usage. So I start a HTTP listener with an url. I expected an exception, as the url having host header is already registered. The code is below. The URL abc:81 is already up and running, however the listener starts and stops without exception. can anyone help
bool notUnderUse;
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://abc:81/") ;
try
{
listener.Start();
listener.Stop();
notUnderUse = true;
}
catch (Exception ex)
{
notUnderUse = false;
}
I coded the below function with various references and it worked fine.
public static bool IsHostHeaderUnderUse(string ipAddress,string port, string hostname)
{
bool alreadyUnderUse = false;
var header = string.Format("{0}:{1}:{2}", ipAddress, port, hostname);
int? iisVersion = IISUtility.GetIISVersion();
if (iisVersion.HasValue && iisVersion < 7)
{
DirectoryEntry iis = new DirectoryEntry("IIS://localhost/W3SVC");
foreach (DirectoryEntry directoryEntry in iis.Children)
{
var bindings = directoryEntry.Properties["ServerBindings"];
if (bindings.Contains(header))
{
alreadyUnderUse = true;
break;
}
}
}
else
{
var serverManager = new ServerManager();
foreach (Site site in serverManager.Sites)
{
foreach (var binding in site.Bindings)
{
if (binding.BindingInformation.Contains(header))
{
alreadyUnderUse = true;
break;
}
}
if (alreadyUnderUse) { break; }
}
}
return alreadyUnderUse;
}
I'm making a simple program in C# that allows a client to connect to a server and run MS DOS commands on that server.
The program worked well, later on I decided I wanted to cmd.exe process to run whilst the client was connected to the server, this is where I'm stuck at. I wanted this so that I could
run commands like CD and change the working directory, previously this would've had no effect because the cmd.exe process was closed after the command was run.
It appears that I can only read from the StandardOutput and StandardError streams if the process has exited, is there any workaround for this?
Here's some of my code used in the program :
*Creates and returns cmd.exe process: *
private Process createDOS()
{
try
{
// Create a ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo("cmd","");
// Set up the values
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardInput = true;
startInfo.CreateNoWindow = false;
// Create a cmd.exe process
Process process = new Process();
// Apply the start info and run
process.StartInfo = startInfo;
process.Start();
return process;
}
catch (Exception)
{
return null;
}
}
Method that formats the response into a string :
private string findResponse(StreamReader err,StreamReader outp)
{
string output = outp.ReadToEnd();
string error = err.ReadToEnd();
string ret = "";
// Add the output to the return field if the process actually output data
if (output.Length > 0)
{
ret = "OUTPUT : " + output;
}
// Then attempt to add data from the error stream
if (error.Length > 0)
{
// If output was read, add a newline character separating the two fields
if (ret.Length > 0) ret = ret + Environment.NewLine;
ret = ret + "ERROR : " + error;
}
// If there's no output, that means there's no error, which means the execution was silently succesful
if (ret.Length <= 0)
{
ret = "Command execution succesful!";
}
return ret;
}
Server listener block :
private void run()
{
while (true)
{
Stream stream = null;
StreamReader sr = null;
StreamWriter sw = null;
Socket socket = null;
Process proc = null;
try
{
socket = server.AcceptSocket();
stream = new NetworkStream(socket);
sr = new StreamReader(stream);
sw = new StreamWriter(stream);
String mode = sr.ReadLine();
sw.WriteLine("Serverside link connected");
sw.Flush();
// Create cmd process in WINPROCESS mode
if (mode == "WINPROCESS")
{
proc = createDOS();
}
String line;
while ((line = sr.ReadLine()) != null)
{
if (mode == "WINPROCESS")
{
proc.StandardInput.WriteLine(line);
proc.StandardInput.Flush();
// Response
sw.WriteLine(findResponse(proc.StandardError, proc.StandardOutput));
sw.Flush();
}
else
{
sw.WriteLine(exeDOS(line));
sw.Flush();
}
}
}
catch (Exception ex)
{
// Silence
}
finally
{
if (socket != null) socket.Close();
if (stream != null) stream.Close();
if (sw != null) sw.Close();
if (sr != null) sr.Close();
if (proc != null) proc.Close();
}
}
}
Any help would be appreciated, thanks!
You need to add a handler to the OutputDataReceived and ErrorDataReceived events:
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived +=
new DataReceivedEventHandler(process_OutputDataReceived);
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived +=
new DataReceivedEventHandler(process_ErrorDataReceived);
if (process.Start())
{
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
}
void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
I want to loop through the available ports:
System.IO.Ports.SerialPort.GetPortNames()
to find if a port is used by a gsm modem.
Any idea please.
What I did in my application for one similar task:
To check that a modem is connected to particular port you can send AT command into this port.
This function below returns true if we found a modem on the current COM port:
private bool CheckExistingModemOnComPort(SerialPort serialPort)
{
if ((serialPort == null) || !serialPort.IsOpen)
return false;
// Commands for modem checking
string[] modemCommands = new string[] { "AT", // Check connected modem. After 'AT' command some modems autobaud their speed.
"ATQ0" }; // Switch on confirmations
serialPort.DtrEnable = true; // Set Data Terminal Ready (DTR) signal
serialPort.RtsEnable = true; // Set Request to Send (RTS) signal
string answer = "";
bool retOk = false;
for (int rtsInd = 0; rtsInd < 2; rtsInd++)
{
foreach (string command in modemCommands)
{
serialPort.Write(command + serialPort.NewLine);
retOk = false;
answer = "";
int timeout = (command == "AT") ? 10 : 20;
// Waiting for response 1-2 sec
for (int i = 0; i < timeout; i++)
{
Thread.Sleep(100);
answer += serialPort.ReadExisting();
if (answer.IndexOf("OK") >= 0)
{
retOk = true;
break;
}
}
}
// If got responses, we found a modem
if (retOk)
return true;
// Trying to execute the commands without RTS
serialPort.RtsEnable = false;
}
return false;
}
On the next stage we can collect some data from the modem.
I used the following commands:
ATQ0 - switch on confirmations (receive OK on each request)
ATE0 - switch on echo
ATI - get modem details
ATI3 - get extended modem details (not all modems supports this command)
// Check each Availble COM port
foreach (string l_sport in l_available_ports)
{
GlobalVars.g_serialport = GlobalFunc.OpenPort(l_sport, Convert.ToInt32(this.cboBaudRate.Text), Convert.ToInt32(this.cboDataBits.Text), Convert.ToInt32(this.txtReadTimeOut.Text), Convert.ToInt32(this.txtWriteTimeOut.Text));
if (GlobalVars.g_serialport.IsOpen)
{
GlobalVars.g_serialport.WriteLine("AT\r");
Thread.Sleep(500);
string l_response = GlobalVars.g_serialport.ReadExisting();
if (l_response.IndexOf("OK") >= 0)
{
GlobalVars.g_serialport.WriteLine("AT+CMGF=1\r");
Thread.Sleep(500);
string l_response1 = GlobalVars.g_serialport.ReadExisting();
if (l_response1.IndexOf("OK") >= 0)
{
GlobalVars.g_PhoneNo = txt_PhNum.Text;
MessageBox.Show("Connected Successfully", "Connection", MessageBoxButtons.OK, MessageBoxIcon.Information);
lblConnectionStatus.Text = "Phone Connected Successfully.";
btnOK.Enabled = false;
btnDisconnect.Enabled = true;
GlobalVars.g_serialport.WriteLine("AT+CGSN\r");
Thread.Sleep(500);
string l_imei = GlobalVars.g_serialport.ReadExisting();
Console.WriteLine("Modem IMEI:" + l_imei);
if (l_imei.IndexOf("OK", 1) > 0)
{
l_imei = l_imei.Replace("AT+CGSN\r\r\n", null);
l_imei = l_imei.Replace("\r\n\r\nOK\r\n", null);
lbl_ModemIMEI.Text = l_imei;
}
else
{
lblConnectionStatus.Text = "Phone Connected Successfully. Error reading IMEI.";
}
EnableSMSNotification(GlobalVars.g_serialport);
break;
}
else
{
Console.WriteLine("No AT+CMGF cmd response");
}
}
else
{
Console.WriteLine("No AT cmd response");
}
}
else
{
Console.WriteLine("No Phone At:" + l_sport);
}
}