For some reason, I can't seem to launch and run a .cmd file with c#. An example of a line of the cmd file is:
"C:\Windows\system32\ffmpeg64.exe" -v verbose -y -i "S:\TEMP\A.ts" -c:v copy -c:a copy -ss 00:00:00.000 -t 2 "S:\TEMP\A_SHORT.ts"
I've tried several different ways to launch this file from within C#, such as (where curDirectory is for example "S:\TEMP")
Process p = Process.Start(curDirectory + "\\ffmpeg.cmd");
I've also tried
string path = curDirectory + "\\ffmpeg.cmd";
Process p = Process.Start("cmd.exe", #"/c " + path); //I've also tried /k
But what happens is the cmd prompt will show up and say "C:\Windows\System32\ffmpeg64.exe" is not recognized ..." even though the file is there. What am I doing wrong?
If your system is running the Windows 6.1 kernel or later, System32 is actually comprised of other directories based on the application you're running (depending on whether it is a 32-bit or 64-bit application).
I assume that ffmpeg64.exe is a 64-bit application, and when you execute the .cmd file manually, it should default to a 64-bit command prompt - Also ensure that your application is targeting "x64" or "Any CPU". Alternatively, you could place a 32-bit version of ffmpeg in the WoW64 directory.
Also, I know you've stated in comments that you don't want to read the .cmd file and modify it, but you could compose your ProcessStartInfo with Environment.SystemDirectory instead of the hard-coded path.
As a last option, you could place the ffmpeg exe somewhere static (as you stated in the comments, in c:\ works), or just in your application's working directory.
Related
I am trying to silently read and write to a .cmd file from a C# console application. The .cmd file looks like this :
ECHO OFF
set Input=""
set /p IsCustom="Do you want to create a custom deploy package ? (Y/N)"
set /p Input="Enter product name (press enter for none):"
ECHO ON
cd .\DeployScript
IF /I "%IsCustom%" == "Y" (
nant -buildfile:Deploy.build -D:environment=Disk Deploy.LocalRelease -D:productname=%Input%
cd ..
GOTO END
)
nant -buildfile:Deploy.build -D:environment=Disk Deploy.NewLocalRelease -D:productname=%Input%
cd ..
:END
This is where I want to insert the values :
set /p IsCustom="Do you want to create a custom deploy package ? (Y/N)"
set /p Input="Enter product name (press enter for none):"
Here is what I tried in C# console application, but I am not able to read the above two questions and write to them:
private static void ExecuteCmdFile()
{
Process process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived);
process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived);
process.Exited += new System.EventHandler(process_Exited);
process.StartInfo.FileName = Path + #"\createPackage.cmd";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
static void process_Exited(object sender, EventArgs e)
{
Console.WriteLine(string.Format("process exited with code {0}\n", ""));
}
static void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
//Console.WriteLine(e.Data + "\n");
}
I am unable to read the questions at process_OutputDataReceived() and have no clues on inserting values. Just wanted to know if I am reading/writing to the .cmd file from C# application correctly ? Am I missing something here or is there any other approach ?
The Windows Command Processor cmd.exe processing a batch file is not designed for communication with other processes. It is designed for executing commands and executables one after the other and supports simple IF conditions and GOTO to control the sequence of command/program executions and FOR for doing something repeated in a loop. That´s it.
I suggest modifying the batch file to this code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "IsCustom=%~1"
if defined IsCustom goto CheckInput
%SystemRoot%\System32\choice.exe /C NY /N /M "Do you want to create a custom deploy package (Y/N)?"
if errorlevel 2 (set "IsCustom=Y") else set "IsCustom=N"
:CheckInput
set "Input=%~2"
if not defined Input goto InputPrompt
if /I "%Input%" == "/N" set "Input="
goto ProcessData
:InputPrompt
set /P "Input=Enter product name (press enter for none): "
if not defined Input goto ProcessData
set "Input=%Input:"=%"
:ProcessData
cd /D "%~dp0DeployScript"
if /I "%IsCustom%" == "Y" (
nant -buildfile:Deploy.build -D:environment=Disk Deploy.LocalRelease -D:productname="%Input%"
goto END
)
nant -buildfile:Deploy.build -D:environment=Disk Deploy.NewLocalRelease -D:productname="%Input%"
:END
endlocal
Now the batch file can be executed without any argument in which case the user is prompted twice with evaluation of the input in a safe and secure manner.
But it is also possible to run the batch file with one or two arguments from another executable like a program coded in C# or from within a command prompt window or called by another batch file.
The first argument is assigned to the environment variable IsCustom which is explicitly undefined on batch file executed without any argument or with "" as first argument. The IF condition used later with referencing the string value of the environment variable IsCustom just checks if the string value is Y or y to do the custom action. Any other argument string passed as first argument to the batch file results in running the standard action.
The second argument is assigned to the environment variable Input which is also explicitly undefined on batch file executed without any argument or with "" as second argument. If the second argument is case-insensitive equal /N, then the batch file interprets this as explicit request to use no product name.
The Yes/No prompt is done using command CHOICE which is highly recommended for such choice prompts if the batch file is called without any argument or with "" as first argument.
The second prompt is done using set /P if the batch file is called without a second argument or with just "" as second argument. Double quotes are removed from input string if the user inputs a string at all for safe and secure processing of the input string by the remaining lines.
The directory DeployScript is most likely a subdirectory of the directory containing the batch file and for that reason the command CD is used with option /D to change if needed also the current drive and makes explicitly the subdirectory DeployScript the current directory independent on which directory is the current directory on starting the execution of the batch file.
nant should be referenced with file extension and if possible with full path if the full path is well known because of being relative to batch file path or is fixed making it possible to use the fully qualified file name in the batch file as it is done for external command CHOICE.
The command ENDLOCAL results in making the initial current directory again the current directory. SETLOCAL pushes the current directory path on stack. ENDLOCAL pops that path from stack and makes this directory again the current directory (if not deleted in the meantime which is not possible here). For that reason the execution of cd .. is not needed at all.
Now the C# coded application can run cmd.exe with the options /D and /C and the batch file name with its full path with the two arguments Y or N and product name or /N. There is no need anymore to communicate with cmd.exe processing the batch file or passing the arguments via standard input stream to the internal command SET of cmd.exe.
Note:
I do not really understand why the C# Process class is used being a C# wrapper class for the Windows kernel function CreateProcess called with using the STARTUPINFO structure to run cmd.exe to process a batch file. It would be also possible to use the Process class to run nant directly by the C# coded program and of course also any other executable which the batch file perhaps runs additionally. A C# coded application has direct access to all Windows library functions used by cmd.exe on processing a batch file. So there is really no need in my opinion to use a batch file at all for the task according to the provided information about the task.
For understanding the used commands in the batch file and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains batch file argument referencing
choice /?
cmd /?
echo /?
endlocal /?
goto /?
if /?
set /?
setlocal /?
I am running a VBS file that needs to be executed on the 64-bit version of cscript. On the command line, when I call cscript, it opens the 64-bit version located at C:\Windows\System32\cscript.exe and the VBS file works fine.
However, I'd like to call this VBS file through C# as a Process. Starting the process with the FileName as cscript does open cscript, but only opens the 32-bit version, located at C:\Windows\SysWoW64\cscript.exe.
Even when I set the FileName to specifically point to the 64-bit version of cscript, it only loads the 32-bit version.
How can I force the process to open the 64-bit version of cscript?
Here is my code, including the 64-bit version file path explained above:
string location = #"C:\location";
Process process = new Process();
process.StartInfo.FileName = #"C:\Windows\System32\cscript.exe";
process.StartInfo.WorkingDirectory = location+#"\VBS\";
process.StartInfo.Arguments = "scriptName.vbs";
process.Start();
Depending on your requirements there is another solution.
Some background: When you run a 64-Bit application and try to start cscript.exe then you call C:\Windows\System32\cscript.exe (or more general %windir%\System32\cscript.exe)
However, when you run a 32-Bit application then each call to %windir%\System32\ is automatically redirected to %windir%\SysWOW64\. In this directory you will find all your 32-Bit DLL's. This redirection is done by Windows internally and your application does not recognize any difference.
In order to access %windir%\System32\ from a 32-Bit application you can use %windir%\Sysnative\, i.e. process.StartInfo.FileName = #"C:\Windows\Sysnative\cscript.exe"; should work even if you compile your application as 32-Bit.
Note, folder %windir%\Sysnative\ does not exist in 64-Bit environment, thus you may check your run-environment by Environment.Is64BitProcess, so
if Environment.Is64BitProcess {
process.StartInfo.FileName = #"C:\Windows\System32\cscript.exe";
} else {
process.StartInfo.FileName = #"C:\Windows\Sysnative\cscript.exe";
}
See also File System Redirector
Note, a similar mechanism exists also for the Registry, see Registry Redirector
A simple answer has arose:
In my C# application I am creating, in Visual Studio (2017) had selected my Platform target to Prefer 32-bit, which should perhaps better be called as "force 32-bit".
By unselecting this option, my process ran as 64-bit, both by specifying the 64-bit path as the code above, but also by just running the cscript by name.
process.StartInfo.FileName = "cscript";
Toggle this option inside Properties > Build > Prefer 32-bit
I am trying to call tpmvscmgr.exe located in C:\Windows\System32
from my c# application.
I found this code:
string strCmdText;
strCmdText = #"/C Robocopy C:\Users\Johannes\test1 C:\Users\Johannes\test2";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
here Run Command Prompt Commands
and it works good.
However when I try to change it to
strCmdText = "/C Tpmvscmgr.exe create /name tpmvsc /pin default /adminkey random /generate ";
This does not work.
In fact when I debug and look around in the command prompt opened by the code I can not find the tpmvscmgr.exe in windows/system32.
Im guessing it is opened as a different user or with other priviledges or something but can this be fixed?
I really would need to run a tpmvscmgr.exe command from code.
OK I figured something out.
In my visual studio c# project I had "Any CPU" marked for the project. Apparently this means the console opens as win32.
When I changed the c# project to x86 I could run tpmvscmgr.exe
Pretty tricky stuff.
C# cmd prompt cannot see telnet.exe
I am trying to run xxx.exe file using command prompt with silent mode. i saw this link in Google: http://www.powerware.com/Software/lansafe_help/LSHelp424.htm.
when i run this command : C:>"D:\xxx.exe" -r -f1"D:\Test.iss"
am getting error : "xxx.exe" is not recognized as an internal or external command operable program or batch file.
Can any body give the idea where i am doing mistake.
As others said, make sure your path to your exe file is correct. You can change directory where exe is before execution or write out the full path.
By silent mode if you mean to run exe without any output on screen, then simply redirect the output to a file.
E.g. if your exe is in D:\myprog\myprog.exe, then following command will make your program run in "silent" mode:
c:>"D:\myprog\myprog.exe" > "D:\myprog\output.txt"
Above example will dump output into output.txt file.
you have to run your command in which location EXE file is located. can you check whether its residing in d:?
You may switch over to the D:\ drive before running your command, but it shouldn't matter. Double check it actually exists in that location
I'm launching the path C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk via Process.Start, but it fails with The system cannot find the file specified.
The link shows up on a dir, so it exists.
Can it be permissions?
Notes:
The path is auto-discovered by iterating over the Start Menu directory.
I can launch it via explorer and command line.
Clarifications:
Code is as follows:
public void Execute() { Process.Start(_shortcut.FullName);}
_shortcut is of type FileInfo
_shortcut.Exists is true, so the file can be found
replacing _shortcut.FullName with the explicit path #"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk" has the same effect.
This is a WPF app using Caliburn and MEF.
Running as Administrator has the same effect.
This here on the other hand seems to work:
[Fact]
public void TestIisManager()
{
var path = new FileInfo(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk");
Process.Start(path.FullName);
}
It does seem to be a bit "environment" based.
Second clarification:
It seems to work in a Windows 7 x86 but not in a Windows 7 x64.
I ran into this recently. Windows Forms based solution, VS2013, x64 machine. Process.Start() could not launch applications via .lnk file. Using process explorer, it seemed that the target specified in the .lnk file was resolving incorrectly to c:\program files (x86)... instead of c:\program files... I followed Bruno's excellent advice, but then again my Target was already marked as "AnyCPU".
After some head scratching, it turned out there's a new compiler flag in VS11+ called "Prefer 32-bit" that was checked by default. This was forcing the EXE output to be 32-bit even though my OS was 64-bit and platform was set to AnyCPU. After I unchecked and recompiled, the problem was fixed.
More reading at: http://blogs.microsoft.co.il/sasha/2012/04/04/what-anycpu-really-means-as-of-net-45-and-visual-studio-11/
Found the issue.
The WPF application was compiled as x86 (all other dlls were compiled as AnyCPU), and when launching some executables or links in a 64 bit machine it failed.
Changing the "Platform Target" to AnyCPU fixes this.
This may not actually relate to your situation, but you can launch the IIS Manager by using
Process.Start("inetmgr.exe")
If you want to continue to use the shortcut, it will probably work if you start the process using a ProcessStartInfo and set ProcessStartInfo.UseShellExecute to true
Can you make sure that you are trying this from an STA thread? You can see whether the apartment state is a problem if the following sample succeeds:
using System;
using System.Diagnostics;
public class Program
{
// make sure to call Process.Start from an STA thread
[STAThread]
static void Main(string[] args)
{
Process.Start(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk");
}
}
Process.Start calls ShellExecute under the hood to run the file passed. As described by Raymond Chen, shell functions require an STA thread:
One possible reason why ShellExecute returns SE_ERR_ACCESSDENIED and ShellExecuteEx returns ERROR_ACCESS_DENIED
As stated already, you will see the "The system cannot find the file specified" error because Windows is looking for inetmgr.exe in ...\SysWOW64\intsrv\ (caused by file system redirection), but it only exists in ...\System32\intsrv\.
This is caused by your 32-bit executable attempting to launch a 64-bit executable. As suggested, not using a 32-bit executable will solve this, but for anyone who must build for 32-bit (a WiX installer bundle in my case), try the following.
Using the start menu LNK/shortcut to inetmgr.exe instead of the executable is a good start, but an extra level of distance is required. This can be provided by using explorer.exe, which can be launched from a 32-bit executable:
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "explorer.exe";
startInfo.Arguments = "/seperate /root,\"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Administrative Tools\\IIS Manager.lnk\"";
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo = startInfo;
process.Start();
It's a bit of a hack, but try launching it like this:
string path = #"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk";
Process.Start("cmd.exe", String.Format("/k \"\"{0}\"\"",path));
Note the double quotes needed to save the spaces in the path.
That way, you might see a more precise error message and/or walk around in the command environment afterwards to see what is wrong with the path.