Network COPY Cmds within C# - File Not Found? - c#

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 ?

Related

Running remote batch file with PsExec and C#

I'm trying to run a remote batch file - already located on the remote machine - using PsExec, called via Process in C#. I've confirmed that all required files already exist, but believe I may have a problem with my syntax, as the redirected output indicates that it can't find the file specified.
The machine against which PsExec runs is dynamic, which is the myArray[0].MachineName value (this pulls in without issue).
wsStopProcess.StartInfo.FileName = #"C:\Windows\system32\PsExec.exe";
wsStopProcess.StartInfo.Arguments = #" \\" + myArray[0].MachineName + #"D:\stopprofile.bat";
wsStopProcess.StartInfo.UseShellExecute = false;
wsStopProcess.StartInfo.CreateNoWindow = true;
wsStopProcess.StartInfo.RedirectStandardOutput = true;
wsStopProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
wsStopProcess.Start();
Any ideas on what appears to be formatted incorrectly? I'm guessing it's too many backslashes (or not enough!) somewhere.
I think the main problem is you do not have a space between the two arguments.
Try this:
wsStopProcess.StartInfo.Arguments = #"\\" + myArray[0].MachineName + #" D:\stopprofile.bat";
I would also warn you that I could not get psexec to work 100%, despite trying many different things.
Try this:
wsStopProcess.StartInfo.Arguments = #"\\" + myArray[0].MachineName + #" D$\stopprofile.bat";
So instead of using : try $ sign. Also setting breakpoint on the above line while debugging will help you to see the exact path.

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

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"
});

How to send series of commands to a command window process?

We have a few commands(batch files/executables) on our network path which we have to call to initialize our 'development environment' for that command window. It sets some environmental variables, adds stuff to the Path etc. (Then only whatever working commands we type will be recognized & I don't know what goes inside those initializing commands)
Now my problem is, I want to call a series of those 'working commands' using a C# program, and certainly, they will work only if the initial setup is done. How can I do that? Currently, I'm creating a batch file by scratch from the program like this for example:
file.Writeline("InitializationStep1.bat")
file.Writeline("InitializeStep2.exe")
file.Writeline("InitializeStep3.exe")
Then the actual commands
file.Writeline("Dowork -arguments -flags -blah -blah")
file.Writeline("DoMoreWork -arguments -flags -blah -blah")
Then finally close the file writer, and run this batch file.
Now if I directly execute this using Process.<strike>Run</strike>Start("cmd.exe","Dowork -arguments"); it won't run.
How can I achieve this in a cleaner way, so that I have to run the initialization commands only once? (I could run cmd.exe each time with all three initializers, but they take a lot of time so I want to do it only once)
As #Hakeem has pointed out, System.Diagnostic.Process does not have a static Run method. I think you are referring to the method Start.
Once you have completed building the batch file, then simply execute it using the following code,
Process p = new Process();
p.StartInfo.FileName = batchFilePath;
p.StartInfo.Arguments = #"-a arg1 -b arg2";
p.Start();
Note that the # symbol is required to be prefixed to the argument string so that escape sequence characters like \ are treated as literals.
Alternative code
Process.Start(batchFilePath, #"-a arg1 -b arg2");
or
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = batchFilePath;
processStartInfo.Arguments = #"-a arg1 -b arg2";
Process.Start(processStartInfo);
More information
Process.Start method
Example of multi command batch file
dir /O
pause
dir
pause
Save this file as .bat and then execute using the Start method. In this case you can specify the argument with the command in the batch file itself (in the above example, the /O option is specified for the dir command.
I suppose you already have done the batch file creation part, now just append the arguments to the commands in the batch file.
Redirecting Input to a process
Since you want to send multiple commands to the same cmd process, you can redirect the standard input of the process to the take the input from your program rather than the keyboard.
Code is inspired from a similar question at: Execute multiple command lines with the same process using C#
private string ProcessRunner()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
if (process != null)
{
process.StandardInput.WriteLine("dir");
process.StandardInput.WriteLine("mkdir testDir");
process.StandardInput.WriteLine("echo hello");
//process.StandardInput.WriteLine("yourCommand.exe arg1 arg2");
process.StandardInput.Close(); // line added to stop process from hanging on ReadToEnd()
string outputString = process.StandardOutput.ReadToEnd();
return outputString;
}
return string.Empty;
}
The method returns the output of the command execution. In a similar fashion, you could also redirect and read the StandardOuput stream of the process.
The Process.Run method that you mentioned, is that from the Process class in System.Diagnostics namespace? AFAIK, the Process type doesn't have either a static or instance method named Run. If you haven't already I'd try with the Start method on Process, either instance or static

Getting output from one executable in an other one

I'm currently trying to get the output of an executable console-app into an other one. To be exact, a little overview of what I'm trying to do:
I have one executable which I cannot edit and neither see it's code. It writes some (quite a bunch to be honest) lines into the console when executed.
Now I want to write another executable that starts the one above and reads the things it writes.
Seems simple to me, so I started coding but ended up with an error message saying that StandardOut has not been redirected or the process hasn't started yet.
I tried it using this kinda structure (C#):
Process MyApp = Process.Start(#"C:\some\dirs\foo.exe", "someargs");
MyApp.Start();
StreamReader _Out = MyApp.StandardOutput;
string _Line = "";
while ((_Line = _Out.ReadLine()) != null)
Console.WriteLine("Read: " + _Line);
MyApp.Close();
I can open the executable and it also does open the one inside, but as soon as it comes to reading the returned values, the app crashes.
What am I doing wrong?!
Take a look at the documentation for the Process.StandardOutput property. You will need to set a boolean indicating that you want the stream redirected as well as disabling shell execute.
Note from the documentation:
To use StandardOutput, you must set ProcessStartInfo..::.UseShellExecute to false, and you must set ProcessStartInfo..::.RedirectStandardOutput to true. Otherwise, reading from the StandardOutput stream throws an exception
You would need to change your code a little bit to adjust for the changes:
Process myApp = new Process(#"C:\some\dirs\foo.exe", "someargs");
myApp.StartInfo.UseShellExecute = false;
myApp.StartInfo.RedirectStandardOutput = false;
myApp.Start();
string output = myApp.StandardOutput.ReadToEnd();
p.WaitForExit();
you could try setting processStartInfo.RedirectStandardOutput = true;
As noted above, you can use RedirectStandardOutput as here.
Another, dirtier way is something like
using (Process child = Process.Start
("cmd", #"/c C:\some\dirs\foo.exe someargs > somefilename"))
{
exeProcess.WaitForExit();
}
And then read its output from somefilename

How can I make my .NET application erase itself?

How can I make my C# app erase itself (self-destruct)? Here's two ways that I think might work:
Supply another program that deletes the main program. How is this deleter program deleted then, though?
Create a process to CMD that waits a few seconds then deletes your file. During those few seconds, you close your application.
Both of those methods seem inefficient. I have a feeling that there's some built-in flag or something in Windows that allows for such stuff. How should I do it? Also, can you provide some sample code?
UPDATE: Thanks for all your answers! I'm going to try them, and see where that gets me.
First of all, some people have asked why I'd want my app to do this. Here's the answer: a few days ago, I read the Project Aardvark spec that Joel Spolsky posted on his blog, and it mentioned that the client app would delete itself after the remote session. I'm wondering how this works, and how, if I ever need to do this, I can accomplish such a feat.
Here's a little overview of what's been suggested:
Create a registry entry that tells Windows to delete the file on reboot
Launch CMD with a ping command to wait a few seconds and then delete the file
Both of those, of course, have their disadvantages, as outlined in the comments.
However, would such a method as outlined below work?
There are two executables: Program.exe and Cleaner.exe. The former is the program itself, the latter is the app that deletes Program.exe and itself (if it's loaded into memory, as I'm about to explain). Is it possible for Program.exe (which has dependencies) to load all of Cleaner.exe, which doesn't have any dependencies, into memory and run it?
If this is possible, could Cleaner.exe be packaged inside Program.exe, loaded into memory, and run?
There's a MoveFileEx API, which, when given a MOVEFILE_DELAY_UNTIL_REBOOT flag, will delete specified file on next system startup.
There's a great CodeProject Article about this topic.
Edit: Basically it's a simple cmd-call which will delete the specified files after some seconds.
Process.Start("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del " + Application.ExecutablePath);
Application.Exit();
You will never be able to guarantee that this will work, as long as you require a physical presence on the machine. For example:
What if the app fails to release a resource in a timely fashion while you're trying to delete it? An error occurs, and the app remains.
The behavior of one app starting another which then deletes the first app is very suspicious from an AV perspective. You are likely to trigger defenses on a user's machine which may kill the process that's trying to kill your original app.
If you do something like delete a file at reboot, what if the user moves your file in between or makes a copy? It's not in the original spot anymore, and the app remains.
If your application requires this level of security, consider hosting it on a machine you control (e.g., by providing a web service and letting a stub client access it that way).
On a somewhat related note, one is also tempted to speculate about the motives of someone who (1) requires a physical presence on someone's machine and (2) wants to delete the evidence that the app existed.
A correction to #Bobby answer, in case people will find it useful - executable path needs to be quoted. Additionally, below is setting cmd.exe window to be hidden (otherwise it flashes as a black console window) and converted to run without relying on System.Windows.Forms assembly (the Application class).
var exepath = Assembly.GetEntryAssembly().Location;
var info = new ProcessStartInfo("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del \"" + exepath + "\"");
info.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(info).Dispose();
Environment.Exit(0);
There is also FileOptions.DeleteOnClose, but that requires the file to be open for writing. You might be able to do it with a sequence like this (untested):
Program launches as Original.exe, and detects (from its own name) that it needs to trigger the self-destruct function.
Original.exe creates a new file Temp.exe with FileOptions.DeleteOnClose and copies its own content into it, but does not close it yet
Original.exe opens a second, read-only handle to Temp.exe and closes the first write handle. The read-only handle can co-exist with an execute handle, whilst keeping the file open to delay auto-deletion.
Original.exe launches Temp.exe. Temp.exe detects that it has been launched from the temp directory and bypasses the self-destruct sequence and continues normal operation.
Original.exe exits (taking its read-only handle to Temp.exe with it.)
Temp.exe continues running. When it exits, the file Temp.exe will no longer be in use so it will be deleted automatically.
Edit #2: Actually I don't think this is possible, because it relies on the kernel opening the file with the FILE_SHARE_DELETE flag, which is unlikely.
sorted by NJ
c#
the other codes does not work so its simple
if u create bath file that loops to del application and
the batch file itself
u can use takkill command to kill the process if u dont want to use
application.close method
`string delname = "test.cmd";
string fileurl = Application.ExecutablePath;
System.IO.StreamWriter file = new System.IO.StreamWriter(delname);
file.WriteLine(":Repeat");
file.WriteLine("del \"" + fileurl + "\"");
file.WriteLine("if exist \"" + fileurl + "\" goto Repeat");
file.WriteLine("del \"" + delname + "\"");
file.Close();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.FileName = delname;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(startInfo);`
`
Th3 3e3 one is not 3d parts ov one
I5
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Think CMD
int sectosleep = 5000;
string exename = "yourexe.exe";
string location = #"c:\yourexe.exe"
Process.Start("cmd.exe", "/C taskkill /f /im " + exename + " & ping 1.1.1.1 -n 1 -w " + sectosleep + " > Nul & Del /F /Q \"" + location + "\"");
;>
I know reflector deletes itself if you use an old version and choose not to update. You might try to figure out what it does. I would start with FileMon and see if it spawns any processes to achieve this.
Since my application (a Windows Service) is installed via the Windows Installer, I self-delete using this:
Dim uninstall_params As String = "/x {MY-PRODUCTS-GUID} /qn /norestart REBOOT=ReallySuppress"
proc.StartInfo = New ProcessStartInfo("msiexec.exe", uninstall_params)
proc.Start()
Environment.Exit(-1)
Sorry--it's in VB, but it should be easily convertible to C#.
Works in Windows 7 & 8, **ENSURE you run your application with admin privileges or you will get an error.
This code exists elsewhere so I can't take full credit I found I made it work for me by adding "Application.Exit();"
static void autodelete()
{
string batchCommands = string.Empty;
string exeFileName = Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", string.Empty).Replace("/", "\\");
batchCommands += "#ECHO OFF\n"; // Do not show any output
batchCommands += "ping 127.0.0.1 > nul\n"; // Wait approximately 4 seconds (so that the process is already terminated)
batchCommands += "echo j | del /F "; // Delete the executeable
batchCommands += exeFileName + "\n";
batchCommands += "echo j | del deleteMyProgram.bat"; // Delete this bat file
File.WriteAllText("deleteMyProgram.bat", batchCommands);
Process.Start("deleteMyProgram.bat");
Application.Exit();
}
This is the Uninstall.exe:
Shutdown.
Wait for 3 sec.
Try to kill that task if it is still running.
Wait for 3 sec.
Delete the app directory with the Uninstall.exe in it.
public void Uninstall()
{
var delPath = AppDomain.CurrentDomain.BaseDirectory;
var procId = Process.GetCurrentProcess().Id;
var psi = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C timeout 3 & Taskkill /F /PID {procId} & timeout 3 & rd /s /q \"{delPath}\"",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(psi);
Application.Current.Shutdown();
}

Categories