I'm running a batch from my ASP.NET c# page. I'm trying to use the System.Diagnostics.ProcessStartInfo method described in the link below. When I click my button to run the batch, the page just hangs with "Waiting for ".
The original code I'm using
Here are some similar problems with solutions:
Possible solution 1
Possible solution 2
Unfortunately, I don't understand solution 1 at all and solution 2 does seem to indicate there is a problem with the paths I'm trying to use, but I'm a little unclear on this as well. Any help is greatly appreciated.
My code:
protected void RunPkg_Click(object sender, EventArgs e)
{
// Get the full file path
string strFilePath = "C:\\inetpub\\wwwroot\\DecisionSupport\\CMSBenPerfUpload\\RunPackage.bat";
// Create the ProcessInfo object
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
psi.WorkingDirectory = "C:\\inetpub\\wwwroot\\DecisionSupport\\CMSBenPerfUpload\\";
// Start the process
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi);
// Open the batch file for reading
System.IO.StreamReader strm = System.IO.File.OpenText(strFilePath);
// Attach the output for reading
System.IO.StreamReader sOut = proc.StandardOutput;
// Attach the in for writing
System.IO.StreamWriter sIn = proc.StandardInput;
// Write each line of the batch file to standard input
while(strm.Peek() != -1)
{
sIn.WriteLine(strm.ReadLine());
}
strm.Close();
// Exit CMD.EXE
string stEchoFmt = "# {0} run successfully. Exiting";
sIn.WriteLine(String.Format(stEchoFmt, strFilePath));
sIn.WriteLine("EXIT");
// Close the process
proc.Close();
// Read the sOut to a string.
string results = sOut.ReadToEnd().Trim();
// Close the io Streams;
sIn.Close();
sOut.Close();
// Write out the results.
string fmtStdOut = "<font face=courier size=0>{0}</font>";
this.Response.Write(String.Format(fmtStdOut,results.Replace(System.Environment.NewLine, "<br>")));
UPDATE: I changed the paths to C:\Temp\ just to see if it made a difference, but it didn't. I opened my security wide open on the dirs I'm using, no go.
UPDATE 2: Running this on my dev box is the same, but I actually get feedback shown in the block below. If I manually perform this in cmd, it executes no problem. If I manually run the batch itself, there's no problem.
> Microsoft Windows [Version 6.1.7601]
>Copyright (c) 2009 Microsoft Corporation. All rights reserved.
>
>C:\inetpub\wwwroot\DecisionSupport\CMSBenPerfUpload>dtexec /f "ACO-SHS-PatDB.dtsx"
>Microsoft (R) SQL Server Execute Package Utility
>Version 10.50.1600.1 for 32-bit
>Copyright (C) Microsoft Corporation 2010. All rights reserved.
>
>Option "#" is not valid.
>
>C:\inetpub\wwwroot\DecisionSupport\CMSBenPerfUpload>
Your process identity doesn't have right to the directory.
Another queston where do you plan to publish this. A sane administrator would prvent any website to run executables or batch file on the server
I'm going to put an answer in here because I hate leaving things unresolved. I never could get this to work exactly right in this form. I had a network admin check permissions just to make sure I wasn't overlooking something, and they couldn't find anything that made a difference either. All permissions seemed to be properly granted.
Somewhere between the version differences in my dev PC and the server for all parts, Windows, SQL Server, Visual Studio, somewhere in there the problem kept itself well-hidden. Kudos to Microsoft for making products that fail to work properly from one machine to the next without wasting far too many hours hunting for a resolution.
Either way, I ended up going a different route. I built very simple SSIS packages on my SQL 2012 server that could be executed from Stored Procedures built on the server. This kept permission and version issues at bay. I then used C# scripting to write out my XML results files to the directories needed. The one issue to overcome was that C# executescalar will not handle XML larger than 2033 characters so I had to use ExecuteXmlReader and loop through the results to built my string before writing it to file.
This problem, though ending differently than it began, is resolved.
Related
I've read about this deadlock condition that I'm pretty certain is affecting my code (below). What I don't understand is: this code worked perfectly well running on Windows Server 2003 (.net 2.0) for the past 12 years. Now we've been trying to move it to Windows Server 2012, where it always deadlocks.
While my DLLs are built for "anyCPU" (still targeting .net 2.0), the executable process being run is absolutely 32-bit, and the move from Server 2003 to Server 2012 goes from a 32-bit to 64-bit OS.
I think I understand what to do to resolve the issue, but does anyone know why this behavior would have changed from Server 2003 to Server 2012?
public string DoMyProcess(string filenameAndPath, string arguments)
{
string stdout="";
int exitCode = 0;
try
{
ProcessStartInfo procStartInfo = new ProcessStartInfo();
procStartInfo.FileName = filenameAndPath;
procStartInfo.CreateNoWindow = true;
procStartInfo.Arguments = arguments;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
System.Diagnostics.Process theProcess = null;
try
{
theProcess = Process.Start(procStartInfo);
theProcess.WaitForExit();
exitCode = theProcess.ExitCode;
// moving this ABOVE WaitForExit should eliminate deadlocks
// But why did it always work on Server 2003 but not on Server 2012?
stdout = theProcess.StandardOutput.ReadToEnd();
}
catch (System.Exception e)
{
string errMsg = e.Message;
log_the_error("threw an exception: " + e.Message);
}
}
return stdout;
}
UPDATE:
Mystery deadlock still exists, even after changing the above code as recommended:
try
{
theProcess = Process.Start(procStartInfo);
stdout = theProcess.StandardOutput.ReadToEnd();
}
catch (System.Exception e)
{
string errMsg = e.Message;
log_the_error("threw an exception: " + e.Message);
}
}
What other conditions could cause that deadlock? If I were to examine StandardError, would it reveal anything useful?
UPDATE #2:
FWIW, we have provisioned another Windows Server 2003 (32-bit), which runs IIS 6. That was the original machine configuration this code ran on for 12 years (with only an occasional deadlock). Our same code that deadlocks on Server 2012 IIS 8 DOES NOT DEADLOCK on this Server 2003.
We now have our own minimal and complete code that reproduces the issue. However, the .exe we've licensed that is being executed by the process has confidentiality clauses that prevent us from posting. I realize that doesn't help the experts here.
THE ONE HINT we've encountered is that when run via the Visual Studio 2013 debugger installed on the actual server, the process doesn't deadlock/hang, while invoking the process from a browser OUTSIDE the server does. And oddly -- from a browser ON THE 2012 SERVER we can't connect to that test page -- the browser just says "connecting" and eventually times out (however, other sites hosted by the same server / same IIS 8 CAN BE REACHED from a browser on the server!)
Since the same command line parameters manually run from either an admin command shell or a non-admin command shell works perfectly, it's hard to believe it's a 64-bit / WOW64 problem with this 32-bit executable or it's required DLLs. We continue to search for places our permissions may be causing problems (the process needs to write to a temp folder, which we've placed at c:\temp, for now).
Without a good Minimal, Complete, and Verifiable code example, it's impossible to answer completely.
What I can tell you is that your code was always broken, and always had the potential for deadlock. You fail to read anything from the process until the process exits, but the process may not be able to exit, if it writes so much data to stdout that the buffer fills and blocks the process.
If you haven't recompiled anything, but have found that you see deadlock now when you didn't before, then the most likely explanation is that the process you're starting writes more to stdout than it used to. I.e. all of the output used to fit in the buffer before, but now it doesn't. (I guess it's also possible the buffer size was reduced in the newer OS, but that seems unlikely to me.)
You should go ahead and move the call to ReadToEnd(). In fact, you should do away with the WaitForExit() altogether. If you are calling ReadToEnd(), that won't complete until the process has in fact exited anyway, so calling WaitForExit() afterwards would be pointless.
I am trying to figure out how to run a bash command from C# running on IIS 7/.Net 4.5.
I've been searching the web and a lot of answers presume you have certain things installed/in place.
I already have Git 1.9.4.msysgit.2 installed with Git Bash and Git Giu. I'm looking for some help as to what else I need installed to run even the simplest of bash commands. And how to run it.
I've looked at posts like bash pipes - I am trying to call script from c# but that uses cygwin. Can I do the same without it and if so, how do I go about it?
Goal
If what I'm asking above doesn't make sense or seems to ask separate questions, here my ultimate goal. I'm trying to write my own server-side git hook. When a developer pushes their commits to our GitHub repo, I want GitHub to call our callback url. I want my callback url to run a git pull command to update our staging server with what was just pushed.
I got to this question based on a previous question I asked at GitHub - setup auto deployment with remote server. based on answers there I'm trying to run a simple command, either but hard coding the command, or putting it in a script and running it, e.g.: cd $REPO_DIR && git pull origin $branch_name.
I am aware of Jenkins and other software, but I want to perform these commands myself vs. installing another software.
If further information is needed please feel free to ask.
Update 1
So based on a few answers below I've come up with the following
using System.Diagnostics;
Process process = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.FileName = #"C:\Program Files (x86)\Git\bin\bash.exe";
processStartInfo.WorkingDirectory = #"C:\myrepo\mysite";
processStartInfo.Arguments = "git status";
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
processStartInfo.UseShellExecute = false;
process.StartInfo = processStartInfo;
process.Start();
String error = process.StandardError.ReadToEnd();
String output = process.StandardOutput.ReadToEnd();
ViewBag.Error = error;
ViewBag.Ouput = output;
With the code above I am getting "C:/Program Files (x86)/Git/bin/bash.exe": git: No such file or directory. I know the exe is there. What's am I doing wrong?
Update 2
As per #SurgeonofDeath comment I followed this post http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/ and added the paths of Git to my environmental variables. However I still am getting the same issues. Any ideas?
Thanks.
Instead of calling the bash.exe, simply call git and pass the status as argument:
processStartInfo.FileName = "git";
processStartInfo.Arguments = "status";
perhaps i misunderstood your question but what about execve?
here is an excerpt of it's man page.
NAME
execve - execute program
SYNOPSIS
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);
DESCRIPTION
execve() executes the program pointed to by filename. filename must > be
either a binary executable, or a script starting with a line of > the
form:
#! interpreter [optional-arg]
Check your PATH environment variable and update it
C:/Program Files (x86)/Git/bin/bash.exe": git: No such file or directory
means that it's git which is not found by bash.
1. Check the PATH environment variable in bash (which is and should remain different from Windows one)
Adjust this
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
to make the terminal visible.
In the terminal you will create with the Process.Start()
Type:
echo ${PATH}
2. Update your path
You could update the global path of windows (which requires a restart)
You could update the user path of windows (which should require a logoff, but I'm not sure).
You just set Path to what you like with System.Environment.SetEnvironmentVariable before starting the Process
Additional note:
If you have like me several versions of bash, command interpreters, git and so on, it could be really messy if you try to concatenate all the paths and hope to find the ideal order. You could see some weird behavior of you beloved commands until you realize it's not the one you intend to run ... think of FIND.exe... And I didn't even think of the user-friendly interface of windows to edit environment variables ...
In my current project, i need to call a Unix shell script from the C# application. I also need to get the response back whether the script has been execute successfully or any error has occurred.
The C# program is running on a Windows machine. I need to connect to a Unix machine and execute the script.
Can anyone let me know how this can be done using C#?
Will this solve your problem?
sharpSsh - A Secure Shell (SSH) library for .NET
Update
Refer to the developer's site for SharpSSH for more information on how to use the tool.
Update 2
change link of developer site to archived link.
A straight forward way of preforming this using System.Diagnostics.Process
// Start the child process.
Process p = new Process();
// Redirect the error stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected error stream.
// p.WaitForExit();
// Read the error stream first and then wait.
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Even i had the same problem, i have googled for solution for around 1 month.
Finally, i have decided to use plink.exe (command line version of putty.exe) to connect to unix box and execute a script there.
You have to use plink through c# process, i have tried it and this works amazingly.
But rite now the problem i am facing is when i am running a script from c# process i am unable to pass arguments to that script. Probably it would be rite to say that i do not know how to do that.
Regards
-Aakash
In my local network ,I have more than 10 pc.I need to take care all of the pc.I want to know all pc’s hardware informations.I also want to control those pc,Suppose ,at this moment I want to restart one of my client pc.Is it possible in C#.if have any question plz ask.Thanks in advance
I use bellow syntax to execute command.
try
{
// create the ProcessStartInfo using "cmd" as the program to be run,
// and "/c " as the parameters.
// Incidentally, /c tells cmd that we want it to execute the command that follows,
// and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/c " + "shutdown /r /m \\172.16.1.3 /t 1 /");
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
// Display the command output.
Console.WriteLine(result);
}
catch (Exception objException)
{
// Log the exception
}
Using the above code I get the message "The network path was not found."
Pls check the url.
http://support.microsoft.com/kb/317371
If you want to make a program which u can able to get the remote system information. You have to use Microsoft's Remoting.Here we can able to create an object in the remote system and we can able to control it.
It is possible to get System's information by executing the System.Diagnostics.ProcessStartInfo.
It is possible to get system information using "systeminfo" .It is possible to take the output using C#
Pls chk the this.
I hope this will be useful for you.
I don't think this is a C# question, cause this can be done much more elegant with things like Group Policy Editor, System Management Server, System Center Operations Manager, etc.
To do some simple tasks on a remote machine you can take a look into the PsTools.
With those requirements my first stop would be WMI. There's for example the Win32_OperatingSystem class with its Reboot and Shutdown methods and the Win32_Processor with all kinds of information about the CPU.
This MSDN section shows you how to use it from .Net: Getting Started Accessing WMI Data
This MSDN section has quite a lot of short VBScript samples for doing various things using WMI, and even if the code is different, at least you can see which WMI classes/methods/properties you should be looking at: WMI Tasks for Scripts and Applications
Please note RB's comment though, you'll need to have the correct permissions for it to work.
Edit: Forgot that since you'll want to connect to other computers, this sample will be useful: How To: Connect to a Remote Computer
This is most likely not an easy one but here is the situation:
I have written a C# command line application which:
creates a PDF using ITextSharp
writes it to disk
uses Acrord32.exe (this is Acrobat Reader) via System.Diagnostics.Process in order to silently print the generated PDF
If I build my solution and double click the pdfGen.exe, it works as expected. The PDF is created and printed.
Now, my app has to be deployed on a internal server with Windows Vista running IIS 7. This server has some PHP webapp running. And it will be called via PHP using shell_exec() so that the resulting PDF will be printed on the printer attached to the server.
So my PHP page looks basically like this:
shell_exec('/path/to/pdfGen.exe');
But here things go wrong. What happens is according to task manager etc.:
pdfGen.exe starts
the PDF is created
Acrord32.exe starts
pdfGen.exe hangs forever (and so does the PHP script) and nothing is printed
I am pretty sure it is some permission related problem. I already gave IIS_IUSRS access to the default printer, and to the directory where Acrord32.exe is located. But still, no printing. However, if I start my pdfGen.exe manually it works.
Any idea what I am missing?
EDIT:
I am not bound to use Acrobat Reader in order to print the PDF. If there is another way in order to silently print the created PDF serverside, I would not mind at all.
In order to check what is going on, try to run
the process monitor from Sysinternals and filter the events to the adobe acrobat process. You will see the system calls of acrobat and it will allow you to know more or less what is going wrong.
I know a small improvement to your solution: SumatraPDF has a nice command-line interface that can be used to auto-close Sumatra after printing.
I used PHP "system" or "exec" functions to execute a batch file to open SumatraPDF:
sumatrapdf.exe -print-to-default -exit-on-print <path_to_PDF_file>
(you can also specify the printer name to print on)
that's an interesting program.
IIS_IUSRS
seems to have no permission to print, try adding IIS_IUSRS to Print Operators Group / grant Print permission to the user.
Shell_exec() is almost intended for shell commands (ls/dir, cp, etc.)
Have you tried to use exec() instead of shell_exec() ?
Thanks all for your comments. Unfortunately this "php start printjob" thing was part of a larger project that was cancelled today because of, well... I dont know... political reasons. Guess the project is pretty much dead.
Anyway, I tried myself a few more times in the last days and could not get it to work with IIS. My solution that I implemented and tested already: remove IIS, install a XAMPP or WAMPP package with a local apache and PHP that runs with admin access rights.
This did the trick. I used pclose(popen('...command...', 'r')); in PHP in order to start the .exe and so that PHP does not wait until the PDF is finished. It all worked great.
Here is my C# code which starts the print job using Acrobat Reader
public void Print(string pathname, string acrobatDirectory)
{
var proc = new Process
{
StartInfo =
{
Arguments = String.Format("/t \"{0}\"", pathname),
FileName = acrobatDirectory,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = false,
RedirectStandardError = false,
}
};
proc.Start();
}
The first argument is the path to the PDF that should be printed, the second parameter is the absolute path to the AcroRd32.exe.
The only problem left was that AcroRd32.exe was started, printed and never got closed again. So every printjob started a new instance of AcroRd32.exe (I am using Acrobat Reader 9.0). So if you printed 10 times, 10 acrobat reader instances were created.
What I did was starting the print job, then waiting X seconds, hoping that the printer was finished and then killing all AcroRd32.exe instances:
public void Print(string pathname, string acrobatDirectory)
{
Debug.WriteLine("Printing...");
Printer.Print(pathname, acrobatDirectory);
Thread.Sleep(30000);
try
{
Debug.WriteLine("Trying to kill runnung AcroRd32.exe's ");
FindAndKillProcess("AcroRd32");
}
catch (Exception)
{
Debug.WriteLine("AcroRd32.exe could not be killed...");
}
}
private bool FindAndKillProcess(string name)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith(name))
{
clsProcess.Kill();
return true;
}
}
return false;
}
This worked out quite well.
Note that the above (killing all AcroRd32.exe and running PHP with admin privilegs) was only doable because: The whole thing is only used by one user at a time and has a very limited area of use.
It should be used on a touchscreen application deployed at the clients POS. A salesman would use the PHP app in order to configure a product, and then PHP would call my .exe which would create and print a PDF in the background. The printed document is then handed to the client. So security etc. was not really a concern in this case.
If anyone has a solution in order to use it with IIS, I am still willing to accept it as an answer.