.net cmd Proces.Start() Can't Change Directory - c#

I think my question title is already crystal clear.
I am invoking Process.Start() method by passing, "cmd.exe" as parameter. But somehow when I execute the program the command prompt that appeared has my .../bin/debug/ in my project folder as its directory. I wanted it to change to C: instead.
Can someone advise me on this?

This is the proper way to set a specified working directory for any kind of process:
var processStartInfo = new ProcessStartInfo();
processStartInfo.WorkingDirectory = #"c:\";
processStartInfo.FileName = "cmd.exe";
// set additional properties
Process proc = Process.Start(processStartInfo);

In addition to the solutions described here, cmd.exe's arguments can accept a command that will be executed immediately after the command line is opened. Also, there's the /k switch that will keep the command line running after executing the command. You can use these two things to achieve your goal:
Process.Start("cmd.exe", #"/k ""cd /d C:\""");
More info: Cmd parameters.

Edit:
Others have posted more eloquent solutions, a la Yuriy-Guts's...
Process.Start("cmd.exe", #"/k ""cd /d C:\""");
(How it works:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cmd.mspx?mfr=true
... the '/k' is part of a post execution command, which keeps your cmd prompt open after the directory change.)
...if your only goal is to launch the command prompt, but I suggest wrapping them in error handling, e.g....
try
{
Process.Start("cmd.exe", #"/k ""cd /d C:\""");
}
catch(Exception e)
{
//Just in case...
Console.WriteLine(e.ToString());
string[] str=Directory.GetLogicalDrives();
Console.WriteLine( "Using C# Directory Class ,Available drives are:");
for(int i=0;i< str.Length;i++)
Console.WriteLine(str[i]);
//If fatal
//Environment.Exit(1)
}
Further, if you're doing other stuff in C:/ I believe the below solution is the most transparent.
Short Answer:
Your IDE is automatically dumping you in the debug directory, because that is the path it's programmed to place your executable. Your executable's point of reference to System objects is the folder it lives in. You'll have to use absolute indexing to get to the root location C: you want to go to.
Long Answer with Code, Self Help Advice
Try Google first, for basics:
https://www.google.com/search?q=change+directory+c%23
First result:
http://www.c-sharpcorner.com/UploadFile/chandrahundigam/WorkingWithDirectory07022005012852AM/WorkingWithDirectory.aspx
(It's poorly formatted, but contains good content.)
To paraphrase:
Add to your code:
using System;
using System.IO;
using System.MarshalByRefObject;
class DoStuff
{
char driveLetter;
...
void Initialize()
{
try
{
Directory.SetCurrentDirectory( string(driveLetter)+string(#":\");
}
catch(FileNotFoundException e)
{
//Just in case...
Console.WriteLine(e.ToString());
string[] str=Directory.GetLogicalDrives();
Console.WriteLine( "Using C# Directory Class ,Available drives are:");
for(int i=0;i< str.Length;i++)
Console.WriteLine(str[i]);
//If fatal
//Environment.Exit(1)
}
Process.Start("cmd.exe");
//Do whatever else you need to do in C:/ ...
}
Note I am new to C# and did not explicitly know how to do this, but it was relatively trivial to figure out. C# experts feel free to correct me if there's any flaws in my approach.

var process = Process.Start(new ProcessStartInfo
{
WorkingDirectory = "C:\\",
FileName="cmd.exe"
});

Related

Using a .R file as a resource and create a batch directly in C#

I have a number of shiny applications with the file structure global.R, ui.R, server.R and something I call batchTrigger.R. The contents of the latter is simply the following-
.libPath(*Path to my R Package Repository*)
require('shiny')
runApp(*Path to the folder with the aforementioned files*)
I created a batch file called application.cmd with the following code-
cls
#pushd ""
:::::::::::::::::::
#echo off
ECHO Loading...Please, wait. The Application will open automatically.
ECHO ---
ECHO Do not close this console window for the whole duration of your session
ECHO in the application.
ECHO ---
#echo off
"C:\Program Files\R\bin\Rscript.exe" ".../**batchTrigger.R**"
:::::::::::::::::::
#popd
cmd /k
This batch file is working just fine. Then I went one step further, and decided to create a windows form with multiple R Applications. I have two buttons in the form, each of which goes something like this-
private void application1_click(object sender, EventArgs e)
{
System.Diagnostics.Process cmd = new Process();
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.FileName = "...\\**application1.cmd**";
cmd.StartInfo.Arguments = "/K";
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.RedirectStandardInput = true;
cmd.Start();
}
So far, so good. Both the buttons work exactly as they were supposed to. I want to go one more step ahead, but since I am very new at C#, I need help. What I am hoping to get is a dynamic location for the R files and the cmd files within the thus deployed application, within the solution. In other words, I should be able to write the contents of the batch file within the C# code, and the path of the batchTrigger.R should be something which changes with the location of the windows form application (which will be a self contained deployed executable file). The idea is that the R package repository and R installation may remain static and can be pointed at by the batchTrigger.R and application.cmd respectively, but the location of batchTrigger.R itself along with other R files move with the application. I think that resource.resx can do something about this, but how exactly can I go about doing it, I don't seem to get. Any suggestion would be highly appreciated.
Make a general method:
private void StartSilentR(string rScriptFilePath)
{
System.Diagnostics.Process cmd = new Process();
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.FileName = #"C:\Program Files\R\bin\Rscript.exe";
cmd.StartInfo.Arguments = rScriptFilePath;
cmd.StartInfo.CreateNoWindow = false;
//avoid this unless you must control the app via stdin
//cmd.StartInfo.RedirectStandardInput = true;
cmd.Start();
}
Then write something that calls it after working out where the script is. For example if you have your directory structure as:
RLauncherCSharpApp.exe
rscripts\ui.R
rscripts\global.R
rscripts\batchTrigger.R
Then in c# you can:
//take exe Path, remove exe name and add rscripts folder and batchtrigger.R file name
var rbt = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "rscripts", "batchTrigger.R");
StartSilentR(rbt);
Or, say you want to search all the subfolders of the app's folder looking for all files called batchTrigger.R:
var exeFolder = Path.GetDirectoryName(Application.ExecutablePath);
string[] paths = Directory.GetFiles(exeFolder, "batchTrigger.R", SearchOption.AllDirectories);
//maybe add them to a list view and the user can click one to launch ..

Process.Start("microsoft-edge:") throws Win32Exception in dot net core

When I execute the following line of code:
Process.Start("microsoft-edge:");
Or
Process.Start("microsoft-edge:http://localhost");
It gives me this error:
System.ComponentModel.Win32Exception: 'The system cannot find the file specified.'
When I run microsoft-edge: using Win+R it works.
When I run the same code in .net framework it works.
I'm using .netcore 3.0.0-preview6-27804-01
Any idea why this is happening?
Edit:
These are not working either:
Process.Start(#"c:\Windows\System32\LaunchWinApp.exe:http://localhost");
Process.Start(#"http://localhost");
All other executables on my system work.
Also this is working too but I can't open a specific webpage with it:
Process.Start("explorer", #"shell:Appsfolder\Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge");
You cannot simply open a url with the expected call Process.Start("url");
You have to create a ProcessStartInfo and pass your browser and url as arguments:
Process.Start(new ProcessStartInfo("cmd", $"/c start microsoft-edge:http://localhost") { CreateNoWindow = true });
(edit) The use of ProcessStartInfo is required because we need to set its CreateNoWindow = true to prevent cmd window from showing up.
But as this code not only can be run on a Windows machine, i'd consider using something more cross-platform specific like this (https://brockallen.com/2016/09/24/process-start-for-urls-on-net-core/):
public void OpenBrowser(string url)
{
try
{
Process.Start(url);
}
catch
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Process.Start("xdg-open", url);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Process.Start("open", url);
}
else
{
throw;
}
}
}
And call it with OpenBrowser("http://localhost");
The exact root cause is indicated here: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.useshellexecute?view=netframework-4.8
"The default is true on .NET Framework apps and false on .NET Core apps."
This means the direct solution is to set UseShellExecute. The following is basically the same as the original except making an explicit Process object to modify a setting.
Process pWeb = new Process();
pWeb.StartInfo.UseShellExecute = true;
pWeb.StartInfo.FileName = "microsoft-edge:http://localhost";
pWeb.Start();
Credit to #Lex Li and #Bizhan for their comments leading to this solution.
This variation seems to do the job as well:
Process.Start(new ProcessStartInfo("msedge") {
UseShellExecute = true,
Arguments = "http://localhost" });
It avoids the strangeness of the microsoft-edge:<url> syntax thus allowing for more chrome arguments to be passed in the usual way.
If anyone knows of a reason to avoid this, feel free to speak up :)
Windows Run window (Win+R) know how to resolve "microsoft-edge:" to the Path Of Executable. When you call it through Run window, it first resolves to the path. Then the actual executable from that path is executed.
With Process.Start, there is no this resolution of path. It only look for some paths like application path or PATH variables. Obviously, it does not find the executable to run and hence the error.
If you have a path variable declared in your system using quotes, you must fully qualify that path when starting any process found in that location. Otherwise, the system will not find the path. For example, if c:\mypath is not in your path, and you add it using quotation marks: path = %path%;"c:\mypath", you must fully qualify any process in c:\mypath when starting it.
Source
Note that command line parameters are not allowed by this overload:
This overload does not allow command-line arguments for the process. If you need to specify one or more command-line arguments for the process, use the Process.Start(ProcessStartInfo) or Process.Start(String, String) overloads.

c# start console programm (lame) and execute it

I have written a c# program. Now I would like to convert .avi files in .mp3 files with lame. I've installed the command-line application. Manually it works fine. Now I would like to automate the process:
C# Application => starts Console and run "lame.exe" with the parameters.
I would like to convert more than one file. How can I do this?
Thanks
I don't know how lame works but can't you get a list of all the files you want to convert, iterate through the list with a foreach loop and run the "lame.exe" for every file?
If you can call Lame manually using CMD you should be available to do the same with this:
public void ConvertFileWithLame(string pathToLame, string fileToConvert){
// Use ProcessStartInfo class.
ProcessStartInfo startInfo = new ProcessStartInfo(pathToLame, fileToConvert);
try{
// Start the process with the info we specified.
// Call WaitForExit and then the using-statement will close.
using (Process exeProcess = Process.Start(startInfo)){
exeProcess.WaitForExit();
}
}
catch{
// Log error.
}
}
P.D: Remember that your command must be in your PATH, or indicates the path on the "/path/commandName.exe"
all you have to do is implement a foreach loop for a Directory.GetFiles, then within the foreach loop use a Process.Start to run your command. Please see below for example code:
foreach(var t in Directory.GetFiles("path"))
{
System.Diagnostics.Process.Start("cmd.exe", $"lame command here with interpolated values (t will point to full path of file)");
}
What you could try to do is see if lame.exe accepts multiple arguments for filenames, so then instead of iterating over the filenames, you can just add them in like below
Process.Start("lame.exe", "file1 file2 file3 etc");

Network COPY Cmds within C# - File Not Found?

I'm trying to copy a file over to a networked folder on a mapped drive. I tested out COPY in my command line which worked, so I thought I'd try automating the process within C#.
ProcessStartInfo PInfo;
Process P;
PInfo = new ProcessStartInfo("COPY \"" + "c:\\test\\test.txt" + "\" \"" + "w:\\test\\what.txt" + "\"", #"/Z");
PInfo.CreateNoWindow = false; //nowindow
PInfo.UseShellExecute = true; //use shell
P = Process.Start(PInfo);
P.WaitForExit(5000); //give it some time to finish
P.Close();
Raises an exception : System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
What am I missing? Would I have to add anything else to the command parameters?
I've tried File.Copy but it doesn't appear to work (File.Exists("<mappeddriveletter>:\\folder\\file.txt");) brings up false.
This SO post contains an example
Run Command Prompt Commands
how to do it right. You need to call cmd.exe with /c copy as a parameter.
Well, for the technical bit: copy in itself is not an executable, but merely a command interpreted by cmd. So basically, you'd have to start cmd.exe as a process, and pass it a flag that makes it run the copy command (which you'll also have to supply as a parameter).
Anyways, I'd side with Promit and recommend looking into File.Copy or something similar.
e: Ah, missed your comment on Promit's answer when I posted this.
Wouldn't it be a lot easier to use File.Copy ?

system() to c# without calling cmd.exe

how to translate system("") to C# without calling cmd.exe?
edit: i need to throw something like "dir"
If I correctly understood your question, you're looking for Process.Start.
See this example (from the docs):
// Opens urls and .html documents using Internet Explorer.
void OpenWithArguments()
{
// url's are not considered documents. They can only be opened
// by passing them as arguments.
Process.Start("IExplore.exe", "www.northwindtraders.com");
// Start a Web page using a browser associated with .html and .asp files.
Process.Start("IExplore.exe", "C:\\myPath\\myFile.htm");
Process.Start("IExplore.exe", "C:\\myPath\\myFile.asp");
}
Edit
As you said you needed something like the "dir" command, I would suggest you to take a look at DirectoryInfo. You can use it to create your own directory listing. For example (also from the docs):
// Create a DirectoryInfo of the directory of the files to enumerate.
DirectoryInfo DirInfo = new DirectoryInfo(#"\\archives1\library");
DateTime StartOf2009 = new DateTime(2009, 01, 01);
// LINQ query for all files created before 2009.
var files = from f in DirInfo.EnumerateFiles()
where DirInfo.CreationTimeUtc < StartOf2009
select f;
// Show results.
foreach (var f in files)
{
Console.WriteLine("{0}", f.Name);
}
As other folks noted, it's Process.Start. Example:
using System.Diagnostics;
// ...
Process.Start(#"C:\myapp\foo.exe");
I need to throw something like "dir"
If you need to run DIR, then you need to call cmd.exe as dir is internal to cmd.exe
Not sure if I understand your question. Are you looking for Process.Start?
Do you actually want the equivalent? You may not depending on what exactly you're trying to do.
For example calling copy from the command line has a C# equivalent itself, File.Copy, for dir there's a whole Directory class for getting info (these are a quick 2 out of thousands of examples). Depending on what you're after, C# most likely has a library/function for the specific command you're trying to run, usually a more robust method as well, instead of a global handler.
If the global "invoke this" is what you're after, then as the other answers suggest, using the System.Diagnostics.Process class is your best bet.
If you want to execute a command-line (cmd.exe) command, such as "dir" or "time" or "mkdir", pass the command as an argument to cmd.exe with the flag /C.
For example,
cmd.exe /C dir
or
cmd.exe /C mkdir "New Dir"

Categories