Installing bat files as windows services with NSSM and controlling them - c#

I want to use nssm (Non-Sucking Service Manager) application to be able to install bat files as windows services and then control them. (start, stop, get status etc.). Note that I placed nssm.exe into C drive.
I tested my code and it works for a simple command like "ping localhost". But if I run nssm commands then it works weird. For example if I run:
C:\nssm.exe status WindowsServiceName
Then it gives me that output:
S\0E\0R\0V\0I\0C\0E\0_\0S\0T\0O\0P\0P\0E\0D\0\r\0\n\0
Actually the output is correct. It writes "SERVICE_STOPPED" but there are lots of weird characters and when I open it in Text Visualizer it shows just "S".
If I run this command:
C:\nssm.exe start WindowsServiceName
It gives empty output and service does not start. But if I manually open a command prompt and run these codes, everything works fine.
Here is my code:
using System.Diagnostics;
namespace NssmTestApplication
{
class Program
{
static void Main(string[] args)
{
string cmdCommand = #"C:\nssm.exe start WindowsServiceName";
string output = RunCmdCommand(cmdCommand);
}
private static string RunCmdCommand(string cmdCommand)
{
string output = "";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C " + cmdCommand;
startInfo.Verb = "runas";
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardError = true;
Process process = Process.Start(startInfo);
while (!process.HasExited)
{
output += process.StandardOutput.ReadToEnd();
}
return output;
}
}
}
How can I make this code work or is there a more efficient way for installing bat files as windows service and then starting, stopping them, getting their status etc.?

Related

Calling specific Python Script from C# process

I'm trying to call python scripts from c#, so far so good. But when I try to call this specific script it is not workin. This is what I'm doing in C#:
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "C:\\Python27\\python.exe";
startInfo.Arguments = "pyScript.py";
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
StreamReader output_sr = exeProcess.StandardOutput;
StreamReader error_sr = exeProcess.StandardError;
String output = output_sr.ReadToEnd();
String error = error_sr.ReadToEnd();
Console.WriteLine(output);
Console.WriteLine(error);
exeProcess.WaitForExit();
}
My pyScript.py file is the following:
with open ('test.txt','w') as f:
for i in range(0x34):
f.write('1')
f.close()
So basically what I spect is to create a file named 'test.txt' containing:
1111111111111111111111111111111111111111111111111111
Also I'm not getting any error, and if I run it from console it works, even if I double click my python script it works. This is really weird and I don't have a clue why when I call if from c# the file is not being generated.
Your 'test.txt' file is created in your Debug folder. Or simply where your C# executable is called from. Thus, it is working but you are looking wrong place.

Start a process with a command line parameter of another executable

I'm trying to pass an .exe to another through C# code.
Here's my code so far:
string ex1 = System.Windows.Forms.Application.StartupPath.ToString() + "\\dev\\psm.exe";
string ex2 = System.Windows.Forms.Application.StartupPath.ToString() + "\\dev\\Application\\app.exe";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = ex1;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.Arguments = ex2;
try
{
Process.Start(startInfo);
}
catch
{
}
Which argument would work as dragging a file onto the application?
Details:
When you run psm.exe normally, it prompts for file name and directory.
However, when you drag an approved app on psm.exe,
it loads the app automatically.
How can this be done with C#?
You can run another app synchronously like this:
System.Diagnostics.Process myapp = new System.Diagnostics.Process();
myapp.StartInfo.FileName = ex1;
myapp.StartInfo.Arguments = ex2;
myapp.Start();
myapp.WaitForExit();
Depending on how the app you want to launch expects the command line arguments to be passed, you may need this for the arguments:
myapp.StartInfo.Arguments = String.Format("/MyArgumentName={0}", ex2);
That would be the equivalent of:
c:\MyApplicationStartupPath\dev\psm.exe /MyArgumentName=c:\MyApplicationStartupPath\Application\App.exe
Be sure to match the way app.exe expects the parameters in your StartInfo.Arguments

Working with the command prompt in C#

I am working on a project of remotely receiving commands from a server, but I am facing a problem when working with the command prompt locally. Once I get it working locally, then I will move to remote communication.
Problem:
I have to completely hide the console, and client must not see any response when the client is working with the command line but it will show a console for a instance and then hide it.
I had to use c# to send a command to cmd.exe and receive the result back in C#. I have done it in one way by setting the StandardOutput... and input to true.
The commands are not working. For example, D: should change the directory to D and it does, but after that, if we use dir to see the directories in D, it does not show the appropriate directories.
Here is my code:
First Method
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C " + textBoxInputCommand.Text + " >> " + " system";
process.StartInfo = startInfo;
process.Start();
Second Method
ProcessStartInfo procStartInfo = new ProcessStartInfo("cmd", "/c " + textBoxInputCommand.Text);
procStartInfo.WorkingDirectory = #"c:\";
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = true;
procStartInfo.UseShellExecute = false;
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
richTextBoxCommandOutput.Text += result;
I want the program to run as administrator because the exe it generates does not run commands when it runs from the C drive.
Try not to run the commands by passing them to cmd instead write the commands passed by the client to a.bat file execute the .bat. file from your program this will probably hide your command prompt window.
You can also use process.OutputDataRecieved event handler to do anything with the output.
If you want to execute command using administrator rights you can use runascommand. It is equivalent to the sudo command in Linux. Here is a piece of code may be it will help you
var process = new Process();
var startinfo = new ProcessStartInfo(#"c:\users\Shashwat\Desktop\test.bat");
startinfo.RedirectStandardOutput = true;
startinfo.UseShellExecute = false;
process.StartInfo = startinfo;
process.OutputDataRecieved += DoSomething;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
//Event Handler
public void DoSomething(object sener, DataReceivedEventArgs args)
{
//Do something
}
Hope it helps you.
You could hide command prompt window by adding this line of code:
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
or do not create it at all
startInfo.CreateNoWindow = true;
Here can be found a few awarding solutions:
Run Command Prompt Commands

Start a process using System.Diagnostics.Process.Start

I am trying to execute a file stored under program files. The program is usually called from a command prompt, and output information while it is running. I want to capture the output.
The code below solves half of the job: The program is executed, and the console pops up showing the output in the console, but I cannot capture the output:
ProcessStartInfo start = new ProcessStartInfo();
start.WorkingDirectory = directory;
start.FileName = fileName;
start.Arguments = arguments;
//start.UseShellExecute = false;
//start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
// code
}
Now I am using the code below to capture the output:
ProcessStartInfo start = new ProcessStartInfo();
start.WorkingDirectory = directory;
start.FileName = fileName;
start.Arguments = arguments;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
The program crashes in the line where I execute the start command with the error "The system cannot find the file specified"
What I don't get is, the first part of the program works, but not the second.
I have search a lot on this topic, and there are many suggestions but have not been able to find one which solves my issue.
I am using .NET 3.5 for the program which I prefers due to compatibility with other parts of the program.

C#.Net: Why is my Process.Start() hanging?

I'm trying to run a batch file, as another user, from my web app. For some reason, the batch file hangs! I can see "cmd.exe" running in the task manager, but it just sits there forever, unable to be killed, and the batch file is not running. Here's my code:
SecureString password = new SecureString();
foreach (char c in "mypassword".ToCharArray())
password.AppendChar(c);
ProcessStartInfo psi = new ProcessStartInfo();
psi.WorkingDirectory = #"c:\build";
psi.FileName = Environment.SystemDirectory + #"\cmd.exe";
psi.Arguments = "/q /c build.cmd";
psi.UseShellExecute = false;
psi.UserName = "builder";
psi.Password = password;
Process.Start(psi);
If you didn't guess, this batch file builds my application (a different application than the one that is executing this command).
The Process.Start(psi); line returns immediately, as it should, but the batch file just seems to hang, without executing. Any ideas?
EDIT: See my answer below for the contents of the batch file.
The output.txt never gets created.
I added these lines:
psi.RedirectStandardOutput = true;
Process p = Process.Start(psi);
String outp = p.StandardOutput.ReadLine();
and stepped through them in debug mode. The code hangs on the ReadLine(). I'm stumped!
I believe I've found the answer. It seems that Microsoft, in all their infinite wisdom, has blocked batch files from being executed by IIS in Windows Server 2003. Brenden Tompkins has a work-around here:
http://codebetter.com/blogs/brendan.tompkins/archive/2004/05/13/13484.aspx
That won't work for me, because my batch file uses IF and GOTO, but it would definitely work for simple batch files.
Why not just do all the work in C# instead of using batch files?
I was bored so i wrote this real quick, it's just an outline of how I would do it since I don't know what the command line switches do or the file paths.
using System;
using System.IO;
using System.Text;
using System.Security;
using System.Diagnostics;
namespace asdf
{
class StackoverflowQuestion
{
private const string MSBUILD = #"path\to\msbuild.exe";
private const string BMAIL = #"path\to\bmail.exe";
private const string WORKING_DIR = #"path\to\working_directory";
private string stdout;
private Process p;
public void DoWork()
{
// build project
StartProcess(MSBUILD, "myproject.csproj /t:Build", true);
}
public void StartProcess(string file, string args, bool redirectStdout)
{
SecureString password = new SecureString();
foreach (char c in "mypassword".ToCharArray())
password.AppendChar(c);
ProcessStartInfo psi = new ProcessStartInfo();
p = new Process();
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.WorkingDirectory = WORKING_DIR;
psi.FileName = file;
psi.UseShellExecute = false;
psi.RedirectStandardOutput = redirectStdout;
psi.UserName = "builder";
psi.Password = password;
p.StartInfo = psi;
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(p_Exited);
p.Start();
if (redirectStdout)
{
stdout = p.StandardOutput.ReadToEnd();
}
}
void p_Exited(object sender, EventArgs e)
{
if (p.ExitCode != 0)
{
// failed
StringBuilder args = new StringBuilder();
args.Append("-s k2smtpout.secureserver.net ");
args.Append("-f build#example.com ");
args.Append("-t josh#example.com ");
args.Append("-a \"Build failed.\" ");
args.AppendFormat("-m {0} -h", stdout);
// send email
StartProcess(BMAIL, args.ToString(), false);
}
}
}
}
Without seeing the build.cmd it's hard to tell what is going on, however, you should build the path using Path.Combine(arg1, arg2); It's the correct way to build a path.
Path.Combine( Environment.SystemDirectory, "cmd.exe" );
I don't remember now but don't you have to set UseShellExecute = true ?
Another possibility to "debug" it is to use standardoutput and then read from it:
psi.RedirectStandardOutput = True;
Process proc = Process.Start(psi);
String whatever = proc.StandardOutput.ReadLine();
In order to "see" what's going on, I'd suggest you transform the process into something more interactive (turn off Echo off) and put some "prints" to see if anything is actually happening. What is in the output.txt file after you run this?
Does the bmail actually executes?
Put some prints after/before to see what's going on.
Also add "#" to the arguments, just in case:
psi.Arguments = #"/q /c build.cmd";
It has to be something very simple :)
My guess would be that the build.cmd is waiting for some sort of user-interaction/reply. If you log the output of the command with the "> logfile.txt" operator at the end, it might help you find the problem.
Here's the contents of build.cmd:
#echo off
set path=C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;%path%
msbuild myproject.csproj /t:Build > output.txt
IF NOT ERRORLEVEL 1 goto :end
:error
bmail -s k2smtpout.secureserver.net -f build#example.com -t josh#example.com -a "Build failed." -m output.txt -h
:end
del output.txt
As you can see, I'm careful not to output anything. It all goes to a file that gets emailed to me if the build happens to fail. I've actually been running this file as a scheduled task nightly for quite a while now. I'm trying to build a web app that allows me to run it on demand.
Thanks for everyone's help so far! The Path.Combine tip was particularly useful.
I think cmd.exe hangs if the parameters are incorrect.
If the batch executes correctly then I would just shell execute it like this instead.
ProcessStartInfo psi = new ProcessStartInfo();
Process p = new Process();
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.WorkingDirectory = #"c:\build";
psi.FileName = #"C:\build\build.cmd";
psi.UseShellExecute = true;
psi.UserName = "builder";
psi.Password = password;
p.StartInfo = psi;
p.Start();
Also it could be that cmd.exe just can't find build.cmd so why not give the full path to the file?
What are the endlines of you batch? If the code hangs on ReadLine, then the problem might be that it's unable to read the batch fileā€¦

Categories

Resources