I am working on a window form app (C#) that should kick an external process and display some results and/or error messages from the exe in the form app.
I cannot seem to be able to form a named pipe connection between the form app and external process (exe) made in python. The exe fires up and works fine but it does not seem to hold the named pipe connection. So, I am not able to get any messages as such from the exe.
The exe is made with pyinstaller and named pipe connection seems to work pretty fine when paired with a window console app i.e. I could get messages back from the exe to a console app.
Consolse App
The script below can get return messages from the exe on to console.
namespace pipeConsoleApp
{
class Program
{
static void Main(string[] args)
{
NamedPipeClientStream pipeClient = new NamedPipeClientStream("teropipe");
Console.Write("Attempting to connect to the pipe...");
System.Diagnostics.Process.Start(#"my\path\to\external\app\tempp.exe");
pipeClient.Connect();
Console.WriteLine("Connected to the pipe");
Console.WriteLine("There are currently {0} pipe server instances open.", pipeClient.NumberOfServerInstances);
StreamWriter writer = new StreamWriter(pipeClient);
StreamReader reader = new StreamReader(pipeClient);
string temp;
while ((temp = reader.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
Console.Write("Press Enter to continue..");
Console.ReadLine();
}
}
But when I tried a similar thing on window form app I am not able to get anything from the python server
Form App
Although I have used a similar approach for the form app somehow no messages are being returned. In fact, it looks like the named piped connection isn't being held open for the form to communicate.
namespace pipeDemoForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void okayButton_Click(object sender, EventArgs e)
{
NamedPipeClientStream pipeClient = new NamedPipeClientStream("teropipe");
//MessageBox.Show("attempting to connect");
System.Diagnostics.Process.Start(#"my\path\to\external\app\tempp.exe");
pipeClient.Connect();
string numb = pipeClient.NumberOfServerInstances.ToString();
MessageBox.Show("There are currently {0} pipe server instances open", numb);
if (pipeClient.IsConnected)
{
MessageBox.Show("There are currently {0} pipe server instances open", pipeClient.NumberOfServerInstances.ToString());
}
}
}
}
Python Script - tempp.exe
The following is the python script, which I have packaged into a onefile exe with pyinstaller.
import win32pipe
import win32file
def process():
pipe_handle = win32pipe.CreateNamedPipe(r'\\.\pipe\teropipe',
win32pipe.PIPE_ACCESS_DUPLEX,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
1,65536,65536,300,None)
win32pipe.ConnectNamedPipe(pipe_handle, None)
# business logic
..................
#write some output to the form app
win32file.WriteFile(pipe_handle,"some string return from above")
Related
I wrote a C# console program to connect from A (Windows 10, Console C# app) over SSH to B (Linux server) and from there on to C (Linux server), but I cannot connect from B to C (from A to B is ok).
When I connect from A over Windows terminal to B and from B's terminal to C, it works, so I proved that my credentials are fine.
I am using Renci.SshNet for C#
I created a class Server with a .Connect(), .Disconnect() and .Execute() extension methods and then the two class instances Broker and Destination
My code looks like:
if (Broker.Connect())
{
Broker.Execute("pwd");
if (Destination.Connect())
{
Destination.Execute("pwd");
Destination.Disconnect();
}
Broker.Disconnect();
}
The Ssh connection objets are created like var broker = new SftpClient("Ip", Port, "User", "Pass")
Then I am internally using broker.Connect() and broker.Disconnect() in Renci.Ssh.Net lib given methods
To broker.Execute("cmd") I basically do
var output = host.Ssh.RunCommand(str);
var res0 = output.ExitStatus;
var res1 = output.Result;
var res2 = output.Error;
My code works for the first part as I manage to get the output of Broker.Execute("pwd") but it does not connect on Destination.Connect() returning the message A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
My aim ist to multi-hop using an automated process from within C#: users must not interact with any console and I cannot modify nor store any files on the Linux sites
Any idea where the problem lies?
Thanks in advance,
I will like to summarize here how I ended solving this issue with the help of some valuable hints gathered over the net and from #jeb:
Open a cmd.exe console, type ssh userC#hostC -p portC-J userB#hostB - p portB (portB and portC can be ommited if they are the default port 22) and then you will be promped to enter passwordB and passwordC - in this order.
If the connection succeeded you will be then on the hostC console and will manage to do whatever you intend to do.
The code you'll need is:
static void RunSshHop(params string[] cmds)
{
try
{
using (Process p = new Process())
{
p.StartInfo = new ProcessStartInfo("cmd.exe")
{
RedirectStandardInput = true,
UseShellExecute = false,
//WorkingDirectory = #"d:\" // dir of your "cmd.exe"
};
p.OutputDataReceived += p_DataReceived;
p.ErrorDataReceived += p_DataReceived;
p.Start();
// way 1: works
foreach (var e in cmds)
p.StandardInput.Write($"{e}\n"); // cannot use 'WriteLine' because Windows is '\r' and Linux is '\n'
/* way 2: works as well
using (var sw = p.StandardInput)
{
foreach (var e in cmds)
if (sw.BaseStream.CanWrite)
sw.Write($"{e}\n"); // cannot use 'WriteLine' because Windows is '\r' and Linux is '\n'
}
//*/
p.WaitForExit();
if (p.HasExited)
{
Console.WriteLine($"ExitCode: {p.ExitCode}");
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
And you can call it like this:
static void Main(string[] args)
{
RunSshHop(
"ssh userC#hostC -p portC-J userB#hostB - p portB",
"pwd",
//"...",
"ls"
);
}
To avoid having to enter the passwords for each host, you can also create an SSH key pair like this:
open cmd.exe console
type ssh-keygen -t rsa
choose path where to save the public and private keys that are to be generated (press enter to use the default destination)
copy the destination, you will need it later to get back yxour keys :-)
to manage an automated process, you have to leave the passphrase empty
-once the keys are generated, log onto the first host over ssh like ssh youruser#firsthost -p hostport (the -p hostport part can be ignored if port is the default 22)
type ssh-copy-id youruser#firsthost -p hostport
accept
repeat the process for the second host
Title sums it up. There are plenty of examples around with a c# server and python client communicating back and forth.
I'd like to understand how I can instead create a python server and c# client for some interprocess communication.
I managed to find a solution. To begin, I'd first like to clarify some confusing terminology and obscure naming conventions used in dotnet core.
It appears that the NamedPipeServerStream and NamedPipeClientStream don't actually operate on named pipes but instead on unix domain sockets. This means that we must use sockets to communicate between processes rather than FIFO files.
Another frustration I find with dotnet core is that when creating a socket or connecting to one, the NamedPipeServerStream and NamedPipeClientStream classes will add "CoreFxPipe_" to the beginning of the socket name. See related question.
Python Server
#!/usr/bin/python3
import socket
import os
import struct
SOCK_PATH = "/tmp/CoreFxPipe_mySocket"
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
try:
os.remove(SOCK_PATH)
except OSError:
pass
sock.bind(SOCK_PATH)
sock.listen()
conn, addr = sock.accept()
with conn:
try:
while True:
amount_expected = struct.unpack('I', conn.recv(4))[0]
print("amount_expected :", amount_expected)
message = conn.recv(amount_expected)
print("Received message : ", message.decode())
# Send data
message_rev = message[::-1].decode()
print("Sent message (reversed) : ", message_rev)
conn.sendall(
struct.pack(
'I',
len(message_rev)
)
+ message_rev.encode('utf-8')
)
except (struct.error, KeyboardInterrupt) as e:
print(e)
finally:
print('closing socket')
C# Client
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
class PipeClient
{
static void Main(string[] args)
{
using (NamedPipeClientStream pipeClient =
new NamedPipeClientStream(".", "mySocket", PipeDirection.InOut))
{
// Connect to the pipe or wait until the pipe is available.
Console.WriteLine("Attempting to connect to pipe...");
pipeClient.Connect();
try
{
// Read user input and send that to the client process.
using (BinaryWriter _bw = new BinaryWriter(pipeClient))
using (BinaryReader _br = new BinaryReader(pipeClient))
{
while (true)
{
//sw.AutoFlush = true;
Console.Write("Enter text: ");
var str = Console.ReadLine();
var buf = Encoding.ASCII.GetBytes(str); // Get ASCII byte array
_bw.Write((uint)buf.Length); // Write string length
_bw.Write(buf); // Write string
Console.WriteLine("Wrote: \"{0}\"", str);
Console.WriteLine("Let's hear from the server now..");
var len = _br.ReadUInt32();
var temp = new string(_br.ReadChars((int)len));
Console.WriteLine("Received from client: {0}", temp);
}
}
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
}
Console.Write("Press Enter to continue...");
}
}
Sources:
https://abgoswam.wordpress.com/2017/07/13/named-pipes-c-python-net-core/
https://realpython.com/python-sockets/
https://unix.stackexchange.com/questions/75904/are-fifo-pipe-unix-domain-socket-the-same-thing-in-linux-kernel
I have two applications. The server send files to my clients. The clients are implemented in Unity3d with C#. Each client has one thread to receive files from server. If I send the files over the network, I write the bytes to the file with this code:
private Thread clientThread;
private object writeLock = new object();
public void StartConnection()
{
// Start connection to server.
clientThread = new Thread(GetFiles);
}
public void GetFiles()
{
string fullPath;
// Receive bytes from server
fullPath = Path.Combine(clientDirPath, fileNameFromServer);
lock(writeLock)
{
using (BinaryWriter bWrite = new BinaryWriter(File.Open(fullPath, FileMode.Create)))
{
bWrite.Write(binaryFileContent);
bWrite.Flush();
}
}
Now, if I start multple clients and send files to receive them synchronously on the client-side, I get this error message: System.IO.IOException:Sharing violation on path. Whether I use the lock-statement it is not working. Do anyone know the way to get it working?
EDIT: I added more code.
I'm currently working on a project for an introductory class on C#. The idea is to make a basic irc client that connects to a single channel on a single irc server. Most things have been easy to implement, however, I've hit a brick wall when attempting to send CTCP messages.
The format is supposed to be: PRIVMSG target : /U+0001ACTION message/U+0001
I am able to identify these messages as they come in, using the Unicode control character, but whenever I try to send my own "ACTION message" is received.
Here's the code I'm using to send the message:
private TcpClient irc; //declarations of properties of irc class
private NetworkStream stream;
private string _inputLine;
StreamReader reader;
StreamWriter writer;
public void Connect() //connects to the irc server in _server on port _port
{
irc = new TcpClient(_server, _port);
stream = irc.GetStream();
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
Send("NICK " + Nick);
Send("USER " + Nick + " 0 * :" + Nick);
Listen();
OnConnect();
}
private void button1_Click(object sender, EventArgs e) //method is from a windows form
{
if (txtMessage.Text.StartsWith("/me"))//sends contents of textbox to SendCTCP if it is an action message
{
test.SendCTCP("ACTION " + txtMessage.Text.Remove(0, 3));
OnAction(test.Nick, txtMessage.Text.Remove(0, 3)); //method OnAction is within the widows form and outputs the message in a textbox for messages sent/received to the channel
}
else
{
test.SendMessage(txtMessage.Text);
ChannelTest(test.Nick, txtMessage.Text, null);
}
txtMessage.Text = "";
}
public void SendCTCP(string message) //formats message with /U+0001 characters
{
char control = '\x01';
Send("PRIVMSG " + _channel + " : " + control.ToString() + message + control.ToString());
}
public void Send(string message) //sends message to server using StreamWriter writer
{
writer.WriteLine(message);
writer.Flush();
}
The Send method uses a StreamWriter to send the message to the server.
I've tried with and without the ToString(). I've tried the character itself within the Send block, rather than in control. I've tried every representation of the character I've been able to find, but I've been unable to make it work. Any help would be appreciated.
Sending the test message "/me tests" through the client results, when the server sends it to other clients in the same channel, in the message: IRCTest!IRCTest#mask PRIVMSG #Channel : ACTION tests
Which is interpreted by every client I can find as: : ACTION tests
edit: I've updated the code with, I hope, all the relevant variables and methods
Well, I feel like an idiot, but at least I figured it out. I've changed it to use '\u0001' as the character, and (wait for it. . .) I deleted the space between the : and the control character. It now functions perfectly.
Hi I have a very annoying problem and I can't understand the reasons.
I have a c# form application which receives some args in main and uses them to connect to a device (ip,port,user,passw).
It also prints these arguments to see if they are correct.
Now if I call the exe with arguments from windows prompt all goes right.
But if I do the same thing from java using the Runtime exec() function the application starts,prints the correct arguments exactly in the same way as before BUT DOES NOT CONNECT!!
it's like arguments were wrong....any suggestion?? maybe exec passing is not the same as prompt??
I have debugged the process with vs and the variable are identical in both case but the connect function (which reminds to a third part library from device vendor)
code
public MyForm(string[] args)
{
InitializeComponent();
if (args.Length > 0)
{
this.IP = args[0];
this.PASSWORD = args[4];
this.ID = args[3];
this.PORT = args[1];
this.logTextArea.AppendText("Connection to " +
IP +":"+PORTA+" "+ID+" "+
PASSWORD+"\n");
} else set fixed values
JAVA
Runtime.getRuntime().exec("MyForm.exe IP PORT NOT_USED ID PASSW");