C# Topshelf fails to execute a powershell file - c#

After installing the service (.exe) and starting it, the powershell file in the code will be executed. There are also no error messages.
My Steps:
cd C:\Users\USER_XY\source\repos\ServiceApp\ServiceApp\bin\Debug\net6.0
.\ScriptAsService.exe install -servicename "MyService123" -displayname "My Service123" -description "This is my service123."
start the service (right click, start)
the start.txt and stop.txt will be generated but the powershell script will not be executed.
Here the c# code:
using Topshelf;
public class App
{
static void Main(string[] args)
{
var exitCode = HostFactory.Run(x =>
{
x.Service<ScriptAsService>(s =>
{
s.ConstructUsing(service => new ScriptAsService());
s.WhenStarted(service => service.Start());
s.WhenStopped(service => service.Stop());
});
x.RunAsLocalSystem();
});
}
}
public class ScriptAsService
{
private String _scriptfile = #"C:\Users\USER_XY\source\repos\ServiceApp\ServiceApp\bin\Debug\net6.0\myscript.ps1";
public void Start()
{
File.WriteAllText("start.txt", "service started --> start scriptfile=" + _scriptfile);
ProcessStartInfo process_start_info = new ProcessStartInfo();
process_start_info.FileName = "powershell.exe";
process_start_info.Arguments = this._scriptfile;
Process process = new Process();
process.StartInfo = process_start_info;
process.Start();
process.WaitForExit();
}
public void Stop()
{
File.WriteAllText("stop.txt", "service stopped");
}
}
And here the powershell code (myscript.ps1):
$counter = 0
while (1)
{
Add-Content info22.txt -Value "content = $counter"
$counter++
Start-Sleep -s 15
}
I except the info22.txt file. This file will be created by the powershell script.

Related

How to create a command line to run c# exe file and give some parameters to a function in form1?

I have c# program that I want to run but from the cmd using a command line so here is my question I don't know how to create the command and I don't know how to send the parameter in my command to some function there.
You can use the Process class to execute files
var fileName = "some.exe";
var arguments = "";
var info = new System.Diagnostics.ProcessStartInfo(fileName, arguments);
info.UseShellExecute = false;
info.CreateNoWindow = true;
// if you want read output
info.RedirectStandardOutput = true;
var process = new System.Diagnostics.Process { StartInfo = info };
process.Start();
var output = process.StandardOutput.ReadToEnd();
var error = process.StandardError?.ReadToEnd();
You can create a console application and after generating (by building) .exe file, you can call with the arguments in command line.
Sample Example:
class Program
{
static void Main(string[] args)
{
var a = Convert.ToInt32(args[0]);
var b = Convert.ToInt32(args[1]);
Console.WriteLine(a+b);
}
}
OUTPUT

How to execute a shell command across platforms in .NET Core?

In my .NET Core Console app, I receive multiple commands in form of an array of string, and would like to execute them as console command (and showing their output in my own app if possible but not hard requirement).
At first, I tried to parse each command to separate their name and arguments and put them in ProcessStartInfo. However, some command does not work (even simple commands like echo "Hello").
Now I switched to call Powershell instead like this:
static IEnumerable<ProcessStartInfo> ParseCommands(string[] args)
{
return args
.Skip(1)
.Select(q => new ProcessStartInfo()
{
FileName = "powershell",
Arguments = q,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
}).ToList();
}
static void RunCommand(ProcessStartInfo processInfo)
{
Console.WriteLine($"{processInfo.Arguments}");
var process = new Process()
{
StartInfo = processInfo,
};
process.Start();
while (!process.StandardOutput.EndOfStream)
{
Console.WriteLine(process.StandardOutput.ReadLine());
}
process.WaitForExit();
}
The problem is I don't think this one can run on Linux or MacOS. Is there any "standard" way to tell my app to "run this as if it's a console command"?
This is my current code by using the Platform to determine the console command, feel free to tell me if there is a better way:
static IEnumerable<ProcessStartInfo> ParseCommands(string[] args)
{
var argsPrepend = "";
var shellName = "/bin/bash";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
shellName = "cmd";
argsPrepend = "/c ";
}
return args
.Skip(1)
.Select(q => new ProcessStartInfo()
{
FileName = shellName,
Arguments = argsPrepend + q,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
}).ToList();
}
static void RunCommand(ProcessStartInfo processInfo)
{
Console.WriteLine($"{processInfo.Arguments.Substring(processInfo.FileName == "cmd" ? 3 : 0)}");
var process = new Process()
{
StartInfo = processInfo,
};
process.Start();
while (!process.StandardOutput.EndOfStream)
{
Console.WriteLine(process.StandardOutput.ReadLine());
}
process.WaitForExit();
}

Process (CMD) is not closed hence no new process can be started

I created a CMDHandler class to handle all my CMD commands i'm having in my project.
For example:
CMDHandler.Run("/c \"" + DWClientSetupChainerV1 + "\"");
CMDHandler.Run("taskkill /F /IM \"DWClientSetupChainerV1 .exe\"");
Though it seems like the process of my CMDHandler is never closed and therefore it's not able to start a second one. Does someone have an idea what i'm doing wrong?
FYI: The Exitcode is not always been used.
namespace Automated_Tests
{
class CMDHandler
{
private static readonly ILogger logger = LoggerFactory.Create(typeof(Program));
public int Run(string command)
{
var proc = new Process();
try
{
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "CMD",
Arguments = command,
UseShellExecute = false
};
proc = Process.Start(startInfo);
proc.WaitForExit();
return proc.ExitCode;
}
catch (Exception ex)
{
logger.Log(LogLevel.Error, ex.Message);
}
finally
{
proc.Close();
}
return 0;
}
}
}
I found a workaround by creating separate tasks for my CMDHandler.Run method:
Task setupTask = new Task(() => CMDHandler.Run("/c \"" + DWClientSetupChainerV1 + "\""));
setupTask.Start();
Thread.Sleep(10000);
Task killSetupTask = new Task(() => CMDHandler.Run("/c taskkill /F /IM \"DWClientSetupChainerV1.exe\"", true));
killSetupTask.Start();
Task.WaitAll(setupTask, killSetupTask);

is there a way to execute a python program using c#?

I want to call my python program and get it executed automatically as it gets called, using c#. I have done uptill opening the program but how to run it and the get the output. It is my final year project kindly help me out.Here is my code:
Process p = new Process();
ProcessStartInfo pi = new ProcessStartInfo();
pi.UseShellExecute = true;
pi.FileName = #"python.exe";
p.StartInfo = pi;
try
{
p.StandardOutput.ReadToEnd();
}
catch (Exception Ex)
{
}
The following code execute python script that call modules and return result
class Program
{
static void Main(string[] args)
{
RunPython();
Console.ReadKey();
}
static void RunPython()
{
var args = "test.py"; //main python script
ProcessStartInfo start = new ProcessStartInfo();
//path to Python program
start.FileName = #"F:\Python\Python35-32\python.exe";
start.Arguments = string.Format("{0} ", args);
//very important to use modules and other scripts called by main script
start.WorkingDirectory = #"f:\labs";
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
}
test scripts:
test.py
import fibo
print ( "Hello, world!")
fibo.fib(1000)
module: fibo.py
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while b < n:
print (b),
a, b = b, a+b

Reading stdout/sdterr of subprocesses in a thread-safe manner

I have the following class to call subprocesses from my application and read their stdout and stderr. Since there are no instance variables for this class, I would have thought it to be thread-safe, but without the lock (_lockObject) I can reproduce an error where the stdout of a subprocess is incorrectly reported as empty.
public class SubprocessRepository
{
private readonly static object _lockObject = new Object();
public ProcessReturnInfo Call(string fileName, params string[] args)
{
lock (_lockObject)
{
var joinedArguments = String.Join(" ", args);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = String.Format(#"""{0}""", fileName),
Arguments = joinedArguments,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
}
};
// read the stdout and stderr streams asynchronously. if they are read synchronously and the streams fill up,
// the entire process waits for the stream to be consumed which will never happen and the subprocess will be
// never complete its operation
var sortSdtOut = new StringBuilder();
var sortSdtErr = new StringBuilder();
process.OutputDataReceived += (sendingProcess, outLine) =>
{
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
// Add the text to the collected output.
sortSdtOut.Append(Environment.NewLine + outLine.Data);
}
};
process.ErrorDataReceived += (sendingProcess, outLine) =>
{
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
// Add the text to the collected output.
sortSdtErr.Append(Environment.NewLine + outLine.Data);
}
};
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit(15 * 60 * 1000); // N minutes * 60 seconds * 1000 milliseconds
if (!process.HasExited)
{
process.Kill(); // executes asynchronously
process.WaitForExit(10000);
}
return new ProcessReturnInfo
{
ExitCode = process.ExitCode,
StdOut = sortSdtOut.ToString().Trim('\r', '\n'),
StdErr = sortSdtErr.ToString().Trim('\r', '\n')
};
}
}
}
I have done some reading on the topic but everything seems to suggest putting locks around the class' instance variables. I suspect it's something to do with the event-handling delegates, but am a bit unsure.
What is the cause of the thread-unsafety in this code when the lock is absent?
FYI, one of the processes that is being called (and incorrectly returning an empty string) is the following Powershell script:
Param(
[string]$server_name,
[string]$service_name
)
$path = (Get-WmiObject -ComputerName $server_name -query "SELECT PathName FROM Win32_Service WHERE DisplayName = '$service_name'").PathName
if ($path -eq $null) {
Write-Error "The $service_name Windows service does not exist on the target server."
exit 1
} else {
Write-Host $path
}

Categories

Resources