Run bat file in c# throws not recognized error - c#

I wan to add some DNS records in c# program via a bat file so I have written these lines in bat file:
set servername=%1
set siteaddress=%2
"C:\Windows\System32\dnscmd.exe" %servername% /zoneadd %siteaddress% /primary /file %siteaddress%.dns
and I have written these lines in C#:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.WorkingDirectory = Application.StartupPath;
p.StartInfo.FileName = General.DnsBatPath;
p.StartInfo.Arguments = string.Format("{0} {1}", General.DnsServerName, txtSiteAddress.Text);
p.Start();
p.WaitForExit();
I get this error "dnscmd.exe is not recognized as internal or external command..." but when I run bat file manually (outside of C#) all things are OK.
I changed my C# code to check what happened
Process.Start(#"C:\Windows\System32\dnscmd.exe");
I still get "not recognized ..." error.but I can see dnscmd.exe in "C:\Windows\System32".
I changed my C# code again to check another thing:
Process.Start(#"C:\Windows\System32\cmd.exe");
and after that CMD windows will be opened???
any idea?

In answer to your second question, you can always check the environmental variable PROCESSOR_ARCHITECTURE to see if it contains the number 64.
set servername=%1
set siteaddress=%2
if "%PROCESSOR_ARCHITECTURE%" equ "%PROCESSOR_ARCHITECTURE:64=%" (
REM 32bit
"C:\Windows\System32\dnscmd.exe" %servername% /zoneadd %siteaddress% /primary /file %siteaddress%.dns
) else (
REM 64bit
"%windir%\Sysnative\dnscmd.exe" %servername% /zoneadd %siteaddress% /primary /file %siteaddress%.dns
)
Possibly a more reliable method is to get it from the registry:
set servername=%1
set siteaddress=%2
for /f "tokens=3" %%x in ('reg Query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier') do (
set arch=%%x
)
if %!arch:~-2!%==64 (
set dnsPath=%windir%\Sysnative
) else (
SET dnsPath=C:\Windows\System32
)
"%dnsPath%\dnscmd.exe" %servername% /zoneadd %siteaddress% /primary /file %siteaddress%.dns

In my case I created a console application to run a batch file with some java command but was getting 'java' not recognized as an internal command error.
Took me few hours but the solution is straight forward. My server had JVM running on 64 bit so I changed the platform target to 64 bit.
x86 is 32 bit and x64 is 64 bit. Project properties are below:

Try this code:
ProcessInfo psi= new ProcessStartInfo("cmd.exe", string.Format("/c {0} {1} {2}", General.DnsBatPath, General.DnsServerName, txtSiteAddress.Text);
psi.UseShellExecute = false;
process = Process.Start(psi);
process.WaitForExit();
The /c parameter says that the given command should run and then cmd.exe should exit. For details, run cmd /? in windows console.
You can also try what happens when you set ShellExecute to true. Then the process should start the same way like a file is double-clicked in explorer. The disadvantage of shell execution is that if the user changed the .BAT file default application to (for example ) notepad, the file will not be executed but opened in notepad.
If you also want to redirect the console output, look here: Executing Batch File in C#

Related

File copy started from aspnet webforms and implemented using batch, pstools and robocopy doesn't work

I have 4 independent servers (not in domain):
IIS, SQL1, SQL2, SQL3
I want to copy a database backup from SQL1 to SQL2 or SQL3 (depending on parameters) by button click on webpage hosted on IIS
I wrote a button click method for that, which is calling batch file located in inetpub folder on IIS
Batch is using pstools to run robocopy on SQL1 which should copy required file to destination server (SQL2 or SQL3)
This solution works if I execute batch directly on IIS (cmd as Administrator) or when I debug it on my local machine, but it doesn't if it is called from the running site.
It even doesn't spend any time between the following lines:
batchProcess.Start();
batchProcess.WaitForExit();
Here is my copy method:
private bool ProcessCopy(string file, string destinationIp)
{
SecureString password = ConvertToSecureString("myPassword");
try
{
string batchPath = Server.MapPath(".") + "\\CopyFile.bat";
string cmd = #"c:\Windows\System32\cmd.exe";
ProcessStartInfo processInfo = new ProcessStartInfo
{
FileName = cmd,
UseShellExecute = false
};
Process batchProcess = new Process {StartInfo = processInfo};
batchProcess.StartInfo.Arguments = $"/C {batchPath} {file} {destinationIp}";
batchProcess.StartInfo.Domain = "";
batchProcess.StartInfo.UserName = "Administrator";
batchProcess.StartInfo.Password = password;
batchProcess.StartInfo.RedirectStandardOutput = true;
batchProcess.StartInfo.RedirectStandardError = true;
batchProcess.StartInfo.CreateNoWindow = true;
batchProcess.Start();
batchProcess.WaitForExit();
string response = batchProcess.StandardOutput.ReadToEnd();
response += batchProcess.StandardError.ReadToEnd();
statusStringAppend($"response: {response}");
return true;
}
catch (Exception ex)
{
statusStringAppend($"Failed: {ex.Message}. {ex.StackTrace}");
}
return false;
}
Batch body is:
#echo off
c:\qa\tools\pstools\psexec64.exe -accepteula -u Administrator -p myPassword \\SourceIP robocopy \\SourceIP\qa\db_backup\ \\%2\qa\db_backup\ %1 /is
My questions are:
1. Why the file was not copied?
2. Is there any better way to get it copied?
CODE UPDATED ACCORDING TO SUGGESTIONS BELOW
My guess is that you never executed pstools as the user that your IIS service is running as before, so the EULA dialog is blocking your execution.
If you remember, you always got a window and needed to press the accept button when running any sysinternals tool like pstools the first time.
I guess this should work for you:
c:\qa\tools\pstools\psexec64.exe -accepteula -u Administrator -p myPassword \\SourceIP robocopy \\SourceIP\qa\db_backup\ \\%2\qa\db_backup\ %1 /is
[EDIT]
You would most likely have hit this problem later on, anyway it did not work for you, so i have to list what else could be wrong with your code:
starting a .bat file needs cmd.exe as mother process, you cannot just start a .bat file as process directly. Instead you can for example use another method than ProcessStartInfo that spawns the system default script interpreter automatically: Executing Batch File in C#
the process for executing batch files is "cmd.exe", first parameter "/C", second parameter the batch file you are executing
when executing typical commandline tools, you might consider reading the SDTOUT (standard output) of the process you are executing, like this: Capturing console output from a .NET application (C#)

Check if process indirectly resulted in an error

I have the following code that I'm using to open cmd and run the SQL Server setup.exe with a configuration file.
ProcessStartInfo pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = "cmd.exe";
pStartInfo.Arguments = "/c /q setup.exe /c /q /configurationFile=../configurationFile.ini";
pStartInfo.WorkingDirectory = installDir + #"\SQL Server Unpacked";
pStartInfo.CreateNoWindow = true;
pStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pStartInfo.UseShellExecute = false;
pStartInfo.RedirectStandardInput = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.RedirectStandardError = true;
Process p = new Process();
p.StartInfo = pStartInfo;
lb_SQLServerReadout.Text = "Please wait while the setup runs";
p.Start();
This is working alright. It'll attempt to install with the details of the configuration file, and if the configuration file contains invalid details then the setup.exe will give an error and not install.
I want to be able to get that error code and write it to a label on my form, so the user has some way of knowing that an error occurred and why it occurred. I considered using Process.StandardError.ReadToEnd, but it will always return null. I believe this is because the Process itself is cmd, not setup.exe (where the error actually occurs).
I've read that it's possible to get the last error code in cmd using echo %errorlabel%, and that does work when I run it manually, so I tried implementing it in my code:
p.StandardInput.WriteLine("echo %errorlevel%");
string s = p.StandardOutput.ReadLine();
p.WaitForExit();
I was hoping that s would be set to the output of the echo %errorlevel% input, but it is just set to null.
How can I get the error code of SQL Server setup, through my cmd Process?
Just as a side note, I've considered running setup.exe as the Process, but it requires elevation, which means enabling UseShellExecute, which in turn means disabling RedirectStandardError, which would prevent me getting the error code anyway.
That is all not necessary, the exit code of the "executable" invoked with cmd.exe /c will be the exit code of cmd.exe itself.
You can try this on the command line as well:
c:\> cmd.exe /c app.exe make-it-fail
c:\> echo %errorlevel%
The second line will print the exit code of "app.exe make-it-fail".
So, just read the value of the Process.ExitCode property, after Process.WaitForExit().
What particular values setup.exe (of SQL Server) sets in case it fails, I don't know. Should be something different than 0 though (by convention).

Why can't i use this code to make a minecraft launcher in c#?

I have searched everywhere to find out how to make a custom minecraft launcher. I managed to create this code, which should work, but sadly it does not. I login but it never starts, however for a second I get the loading ring next to my mouse. This is my code:
ProcessStartInfo start = new ProcessStartInfo();
// Enter in the command line arguments, everything you would enter after the executable name itself
start.Arguments = #"-Xmx1G -Djava.library.path=%APPDATA%\.minecraft\versions\1.6.2\1.6.2-natives -cp %APPDATA%\.minecraft\libraries\net\sf\jopt-simple\jopt-simple\4.5\jopt-simple-4.5.jar;%APPDATA%\.minecraft\libraries\com\paulscode\codecjorbis\20101023\codecjorbis-20101023.jar;%APPDATA%\.minecraft\libraries\com\paulscode\codecwav\20101023\codecwav-20101023.jar;%APPDATA%\.minecraft\libraries\com\paulscode\libraryjavasound\20101123\libraryjavasound-20101123.jar;%APPDATA%\.minecraft\libraries\com\paulscode\librarylwjglopenal\20100824\librarylwjglopenal-20100824.jar;%APPDATA%\.minecraft\libraries\com\paulscode\soundsystem\20120107\soundsystem-20120107.jar;%APPDATA%\.minecraft\libraries\argo\argo\2.25_fixed\argo-2.25_fixed.jar;%APPDATA%\.minecraft\libraries\org\bouncycastle\bcprov-jdk15on\1.47\bcprov-jdk15on-1.47.jar;%APPDATA%\.minecraft\libraries\com\google\guava\guava\14.0\guava-14.0.jar;%APPDATA%\.minecraft\libraries\org\apache\commons\commons-lang3\3.1\commons-lang3-3.1.jar;%APPDATA%\.minecraft\libraries\commons-io\commons-io\2.4\commons-io-2.4.jar;%APPDATA%\.minecraft\libraries\net\java\jinput\jinput\2.0.5\jinput-2.0.5.jar;%APPDATA%\.minecraft\libraries\net\java\jutils\jutils\1.0.0\jutils-1.0.0.jar;%APPDATA%\.minecraft\libraries\com\google\code\gson\gson\2.2.2\gson-2.2.2.jar;%APPDATA%\.minecraft\libraries\org\lwjgl\lwjgl\lwjgl\2.9.0\lwjgl-2.9.0.jar;%APPDATA%\.minecraft\libraries\org\lwjgl\lwjgl\lwjgl_util\2.9.0\lwjgl_util-2.9.0.jar;%APPDATA%\.minecraft\versions\1.6.2\1.6.2.jar net.minecraft.client.main.Main --username playername --session token:"+ words[3] + #":" + words[4]+ #" --version 1.6.2 --gameDir %APPDATA%\.minecraft --assetsDir %APPDATA%\.minecraft\assets";
start.FileName = #"c:\Program Files (x86)\java\jre7\bin\javaw.exe";
// Do you want to show a console window?
start.CreateNoWindow = true;
System.Diagnostics.Process.Start(start);
This just does the loading ring by my mouse for a second, then nothing opens. No logs, crashes, errors, nothing wrong. This is Visual c# compiled on Visual Studio 2012.
The arguments you are giving have an environment variable in them - %APPDATA%.
The command line will expand this by default, but the .net library won't.
See How do I ensure c# Process.Start will expand environment variables?
As Pete Kirkham mentioned you need to set up environment variable.
You can set it before starting the Process like:
var appDataPath = "your path";
start.EnvironmentVariables.Add("APPDATA", appDataPath);

Redirect standard output of exe to my Console application

There is an executable called jlink.exe that I will like to make of use on my console application. Because jlink.exe is not a .net application it is not possible for me to reference it and call it's methods. As a result I will like to use it reading it's output
When I start that exe from windows explorer this is how it looks:
It then waits for a command. If I type mem 88 2 then I get back:
Now I will like to do the same thing with my .net console application reading it's standard output but for some reason I cannot read the output. This is what I have:
// change working directory
Directory.SetCurrentDirectory(#"C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.4 Evaluation\arm\bin");
Process p = new Process( )
{
StartInfo = new ProcessStartInfo( #"jlink.exe" )
{
UseShellExecute = false ,
RedirectStandardOutput = true ,
RedirectStandardInput = true
}
};
p.Start( );
StreamWriter standardInput = p.StandardInput;
StreamReader standardOutput = p.StandardOutput;
var line = string.Empty;
while ( ( line = standardOutput.ReadLine( ) ) != null )
{
Console.WriteLine( line );
}
why when I run that code I just get a black window... I do not get the same output as when running that executable. When I run that code against other executable it works great.
If I then type mem 88 2 and press ENTER nothing happens. If I press enter one more time then I finally get what I want with extra content. Why am I getting this behavior with that executable?
Edit
I have looked at similar question such as this one:
process.standardoutput.ReadToEnd() always empty?
I don't think I am doing something wrong. I guess there is something weird with that executable. To use that executable you may download it at: http://supp.iar.com/Download/SW/?item=EWARM-EVAL or at http://www.iar.com/en/Products/IAR-Embedded-Workbench/ARM/ . Then go to C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.4 Evaluation\arm\bin and execute jlink.exe (If you do not have a board connected via usb you will get an error but it does not matter. the error message does not show up either).

How to view Process.Start() Arguments as they are passed to executable

I'm trying to run a fortran executable with Process.Start and it is not working.
Process proc = new Process();
string args = "<C:\\file.in> C:\\file.out";
proc.StartInfo = new ProcessStartInfo(AppName, args);
proc.Start();
if I paste those arguments into a command window the application runs as expected. proc.Start() does not run as expected.
Any ideas how I can view what Start is actually passing as arguments? My gut feeling is that this is a quotes issue.
The executable launches and hangs, so I'm confident the AppName is getting passed in correctly, it looks like an argument problem.
I tried setting the WorkingDirectory to that of the input and output files as suggested in this question: process.start() arguments but that did not work.
Redirection with the < and > command line operators is a feature that's implemented by the command line processor. Which is cmd.exe. Use its /c argument to execute just a single command:
string args = "/c " + AppName + " < C:\\file.in > C:\\file.out";
proc.StartInfo = new ProcessStartInfo("cmd.exe", args);
proc.Start();
Your args string is exactly what is being passed as arguments to the executable. You can double check it reading your Process ProcessStartInfo.Arguments Property.
Something similar happened to me once, i.e., calling the executable from the command line worked and from code didn't, and it turned out that when called from the command line the executable was running on my PC's [C:] drive, and when called from code it was running on my PC's [E:] drive, which was full!
To check which directory your application is using to run the executable use the Directory.GetCurrentDirectory Method.

Categories

Resources