I'm new to both C# and Perl, but I've been programming in other languages for a few years now. But anyways, I've been trying to write a simple program that passes a value from a C# program to a Perl script via its STDIN. The C# program opens the Perl script just fine, but I cant seem to figure out a way to pass a '1' to it. what would the best way of doing this be? I've searched far and wide for solutions, with no luck...
The C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace OpenPerl
{
class Program
{
static void Main(string[] args)
{
string path ="Z:\\folder\\test.pl";
Process p = new Process();
Process.Start(path, #"1");
}
}
}
The Perl program
#!/usr/bin/perl
use strict;
use warnings;
print "Enter 1: ";
my $number=<STDIN>;
if($number==1)
{
print "You entered 1\n\n";
}
Try this:
my ($number)=#ARGV;
instead of:
my $number=<STDIN>;
From perldoc: "The array #ARGV contains the command-line arguments intended for the script."
You are passing a command line argument to the perl script , not a user input via Process.Start(string,string).
Try printing the #ARGV received by the perl script and you should be able to see 1.
If you want the perl script to receive its input via STDIN, the C# side would look like this:
Process p = new Process();
p.StartInfo.FileName = path;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.Start();
p.StandardInput.WriteLine("1");
Setting UseShellExecute is required for RedirectStandardInput, but it may prevent the perl script from starting properly. In that case, set FileName="<path to perl.exe>" and Arguments="<path to script.pl>".
Related
I'm creating a simple C# program used to check various settings on windows 10 builds we're testing. I need to capture the output from a CMD or Powershell invoke and display the result as a string (human-readable). Specifically, I'm trying to capture the output from get-bitlockervolume to check if the drives are encrypted. The program should be able to run without using admin creds.
Get Powershell command's output when invoked through code
unfortunately doesn't quite seem to work, so I thought I'd attempt to capture the output to a txt file and read it from there, but for some reason, the txt ends up being empty. For my latest attempts, I've ditched PowerShell and attempted to get it done using simple CMD.
Process bl = new Process();
bl.StartInfo.WindowStyle = ProcessWindowStyle.Hidden ;
bl.StartInfo.FileName = "cmd.exe";
bl.StartInfo.Arguments = #"/c manage-bde -status > C:\windows\temp\bitlockerstatus.txt";
bl.StartInfo.RedirectStandardOutput = true;
bl.StartInfo.UseShellExecute = false;
bl.Start();
This seems to create the output file I was looking for in the right location, but it always turns up empty. When running the command directly from cmd, there doesn't seem to be an issue recording the output.
I'm still quite new to C#, self-learning as I go along, so any suggestions for an alternate method/way of doing this would be appreciated.
Very late edit:
I've figured out a way by using a Collection.
code:
StringBuilder str = new Stringbuilder();
using (Powershell ps = Powershell.create())
{
ps.addscript ("manage-bde -status");
Collection<PSObject> psoutp = ps.Invoke();
foreach(PSObject outp in psoutp)
{
if (outp != null)
{
str.Append(outp);
}
else str.Append ("Error");
}
return str.ToString();
}
This for a Method that returns the output of manage-bde - status from powershell back to main. If there's no output at all (not even an errormessage) , it'll simply return "error" back to main.
Here's hoping this helps someone else one day.
I think the answer is here:
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
// do something with line
}
I am using this function below to create powershell script
public static void joinDomain()
{
string path = #"C:\Windows\Temp\Test.ps1";
if(!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
sw.WriteLine("add-computer –domainname ad.contoso.com -Credential AD\adminuser -restart –force");
}
}
}
After successfully script creation I run that script using this below code
Classes.Functions.joinDomain();
string strCmdText = #"C:\Windows\Temp\Test.ps1";
System.Diagnostics.Process.Start("C:\\windows\\system32\\windowspowershell\\v1.0\\powershell.exe ", strCmdText);
If i run script from Powershell ISE it prompts for password so the script works.
Even calling it works but i just got blue powershell commandline and then it disseaper it wont ask for password and i dont know why.
Any ideas would be appreciated?
Found solution:
It was cause by windows restricted policy for unsigned scripts.
Solution here:
https://github.com/eapowertools/ReactivateUsers/wiki/Changing-Execution-Signing-Policy-in-Powershell
I have a Python backend running machine learning algorithms. I want to use the same backend for both an Excel plugin (C#) and a website. I want both interfaces to send my training data (thousands of lines of numbers in arrays) to the same Python application and retrieve the results in the form of another array up to a few thousand lines.
The website would fetch data from a SQL database and send that data to Python, while the Excel plugin would take the data that is in the current worksheet and send that data to Python. I need to be able to create numpy arrays in Python before continuing to process the data. Note that the website would be running on the same machine where the Python application resides. I still haven't decided what I will use to code the website, but I was leaning towards Node.js.
I have done some research and found a few options:
1- Named pipes
2- Sockets
3- RPC server such as gRPC or XML-RPC.
4- Writing the data to a file and reading it back in Python
5- Web Service
Note: I would need the Python "server" to be stateful and keep the session running between calls. So I would need to have a kind of daemon running, waiting for calls.
Which one would you experts recommend and why? I need flexibility to handle several parameters and also large arrays of numbers. Using IronPython is not an option because I am running Keras on Python, which apparently does not support IronPython.
I had the same problem recently.
I used a named pipe to transport data from python to my c# server, hope it helps you.
Python:
import win32pipe, win32file
class PipeServer():
def __init__(self, pipeName):
self.pipe = win32pipe.CreateNamedPipe(
r'\\.\pipe\\'+pipeName,
win32pipe.PIPE_ACCESS_OUTBOUND,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
1, 65536, 65536,
0,
None)
#Carefull, this blocks until a connection is established
def connect(self):
win32pipe.ConnectNamedPipe(self.pipe, None)
#Message without tailing '\n'
def write(self, message):
win32file.WriteFile(self.pipe, message.encode()+b'\n')
def close(self):
win32file.CloseHandle(self.pipe)
t = PipeServer("CSServer")
t.connect()
t.write("Hello from Python :)")
t.write("Closing now...")
t.close()
For this code to work you need to install pywin32 (best choice is from binarys): https://github.com/mhammond/pywin32
C#-Server:
using System;
using System.IO;
using System.IO.Pipes;
class PipeClient
{
static void Main(string[] args)
{
using (NamedPipeClientStream pipeClient =
new NamedPipeClientStream(".", "CSServer", PipeDirection.In))
{
// Connect to the pipe or wait until the pipe is available.
Console.Write("Attempting to connect to pipe...");
pipeClient.Connect();
Console.WriteLine("Connected to pipe.");
Console.WriteLine("There are currently {0} pipe server instances open.",
pipeClient.NumberOfServerInstances);
using (StreamReader sr = new StreamReader(pipeClient))
{
// Display the read text to the console
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
}
}
Console.Write("Press Enter to continue...");
Console.ReadLine();
}
}
You can use Python for .NET (Python.NET). It may require some changes to your code, but then it should work very well, once everything is in good shape.
Python.NET allows two-way communication between CPython and CLR.
Let me give you a neat and quick recipe, in the form of example code.
There are basically two ways to tie python in the backend of C# (or a C# winform app or gui or something similar).
Method1: Iron Python. In this method you install a .net package in your visual studio called IronPython. I would not prefer this, because assuming your machine learning model uses keras or a lot of other libraries. It would be another quest to get you installations ready and working in IronPython. And most importantly, it is not as good as your common virtual env or conda environment.
Method2: (The Good Method): Create a Custom Process in your C# that takes arguments from your GUI, knows the path to your script and your python env. Using all these things, it calls your python code exactly the way you would call it in your terminal and pass arguments to it.
Now the tasty example code (I have used this simple trick and it always helps make my black screen python stuff look good with the cover of C# apps).
Python Part
import sys
a = sys.argv[1]
b = sys.argv[2]
print("The Sum = ", float(a)+float(b))
The C# Part
So here is the python process/function that you need to call on the click event of your sum button in the application
static void PythonProcess()
{
//1) Create Process Info
var psi = new ProcessStartInfo();
//Conda Env Path
psi.FileName = #"C:\Users\jd\.conda\pkgs\py\python.exe";
//2) Provide Script and the Arguments
var script = #"C:\Users\jd\Desktop\script.py";
var a = "15";
var b = "18";
psi.Arguments = $"\"{script}\" \"{a}\" \"{b}\"";
//3) Process Configuration
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
//4) Execute Process and get Output.
var errors = "";
var results = "";
using(var process = Process.Start(psi))
{
errors = process.StandardError.ReadToEnd();
results = process.StandardOutput.ReadToEnd();
}
//5) Display Output
Console.WriteLine("ERRORS: ");
Console.WriteLine(errors);
Console.WriteLine();
Console.WriteLine("RESULTS: ");
Console.WriteLine(results);
}
Calling Python from C# is easily possible via Pyrolite where your Python code is running as a Pyro4 server. It should be fast enough to handle "large arrays of numbers" however you didn't specify any performance constraints.
I had the same issue and seem to end up with named pipes. Here is a nice example of how to set it up to talk C# => Python, assuming C# is the server.
It can use the same way to talk back or just Python.net to call directly through CLR as shown here. I use the latter.
just downloaded ActivePerl. I want to embed the perl interpreter in a C# application (or at least call the perl interpreter from C#). I need to be able to send send out data to Perl from C#, then receive the output back into C#.
I just installed ActivePerl, and added MS Script Control 1.0 as a reference. I found this code on the internet, but am having trouble getting it to work.
MSScriptControl.ScriptControlClass Interpreter = new MSScriptControl.ScriptControlClass();
Interpreter.Language = #"ActivePerl";
string Program = #"reverse 'abcde'";
string Results = (string)Interpreter.Eval(Program);
return Results;
Originally, it had 'PerlScript' instead of 'ActivePerl', but neither work for me. I'm not entirely sure what Interpreter.Language expects. Does it require the path to the interpreter?
Solved... I'm not sure how, but when I changed it back to PerlScript it works now. Still, I would like to know if MSScript Control is using ActivePerl or another interpreter.
You can run an external program as Maxwell suggests, in which case the external program can be Perl or anything else. It might be easier to use temp files to send the input data and get the output, but that depends on how the external program expects to get its data.
The alternative, which is what I think you're looking for, is to use the PerlNET compiler that comes with ActiveState's Perl Dev Kit. It lets you add a class wrapper around the Perl code so you can expose it to C# just like any C# class. It's fairly simple to use; you add POD comments to your Perl code to specify the method names and signatures to expose, including type information, then you compile your Perl module into a DLL .NET assembly. Once that's done you can reference the assembly from any .NET program, construct an object from your Perl class, and call its methods.
I am not sure about the script control but I have done a similar thing where I had to 'embed' spamassasin (which is a Perl program). I basically used the Process to do the job. Something along the lines of:
var proc = new Process
{
StartInfo =
{
FileName = "perl",
WorkingDirectory = HttpRuntime.AppDomainAppPath,
Arguments = " myscript.pl arg1 arg2",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false
}
};
proc.Start();
proc.StandardInput.BaseStream.Write... // feed STDIN
proc.StandardOutput.Read... // Read program output
var procStdErr = proc.StandardError.ReadToEnd(); // errors
proc.StandardError.Close();
proc.StandardOutput.Close();
proc.WaitForExit(3000);
int exitCode = proc.ExitCode;
proc.Close();
This obviously not just Perl specific and it has the process creation overhead, so if you are running your script too often probably you need to think of a different solution.
I am having a problem which seems really daft. I must be missing something silly. We have a PGP keyring that is on one of our production servers. The user account it belongs to is not allowed to be logged on as interactively for security. Our problem is we sometimes need to add new keys and can not do this easily. So we thought we could create a quick console app that would be run as its ID and would call the PGP commands via the command line.
The command gets called but it asks for input to confirm what we are doing. Our problem is the "y" we send to standardinput is never displayed and the key is not verified.
here is the code:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.DirectoryServices;
using System.Threading;
namespace TestConsoleApp
{
class RegExValidator
{
private System.Diagnostics.Process myProcess;
public RegExValidator()
{
}
public static void Main(string[] args)
{
RegExValidator myValidator = new RegExValidator();
myValidator.InstallKeys("C:\\Test\\batch.asc", "batch.asc");
}
private void InstallKeys(string keyPath, string keyName)
{
myProcess = new System.Diagnostics.Process();
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.StartInfo.CreateNoWindow = false;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.FileName = "pgp";
myProcess.StartInfo.Arguments = "-ka " + keyPath + "";
myProcess.Start();
StreamWriter myInput = myProcess.StandardInput;
myInput.AutoFlush = true;
Thread.Sleep(3000);
myInput.WriteLine("y");
myInput.WriteLine(Environment.NewLine);
}
}
}
This is the output we get on the command line.
C:\Test>TestConsoleApp.exe
Pretty Good Privacy(tm) Version 6.5.2
(c) 1999 Network Associates Inc.
Uses the BSafe(tm) Toolkit, which is copyright RSA Data Security, Inc.
Export of this software may be restricted by the U.S. government.
WARNING: Environmental variable TZ is not defined, so GMT timestamps
may be wrong. See the PGP User's Guide to properly define TZ
Looking for new keys...
DSS 2048/1024 0xDE053A3D 2007/05/29 Batch Interface <batch#netgiro.com>
sig? 0xDE053A3D (Unknown signator, can't be checked)
keyfile contains 1 new keys. Add these keys to keyring ? (Y/n)
C:\Test>
Can anyone help?
Thanks
EDIT
We tried this process but instead of PGP we just moved a file and we got the Y/N box and that worked. It would seem that you may not be able to do it with PGP. No idea why though.
The message
keyfile contains 1 new keys. Add these keys to keyring ? (Y/n)
suggests replying with an Uppercase Y. try changing your call to:
myInput.WriteLine("Y");
(I have no PGP installed for checking, but have encountered other command line interfaces that insisted on case.)
Another thing to try is flushing stream buffers, which clears all buffers for the stream and causes any buffered data to be written to the underlying device:
myInput.WriteLine("Y");
myInput.Flush();