File copy using robo copy and process - c#

I am creating a File copy program which will copy large number of files(~100,000) with size ~50 KB using ROBOCOPY command.
For each file, I am creating a new process and passing the ROBOCOPY command and arguments as follow:
using (Process p = new Process)
{
p.StartInfo.Arguments = string.Format("/C ROBOCOPY {0} {1} {2}",
sourceDir, destinationDir, fileName);
p.StartInfo.FileName = "CMD.EXE";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.Start();
p.WaitForExit();
}
Instead of creating a process for each file, I am looking for a better approach, which will be good in terms of performance and design. Can someone suggest a better method?

This question is a bit old but I thought I would answer to help anyone who still land on it. I wrote a library called RoboSharp (https://github.com/tjscience/RoboSharp) that brings all of the goodness in Robocopy to c#. Take a look if you require the power of Robocopy in c#.

Process p = new Process();
p.StartInfo.Arguments = string.Format("/C Robocopy /S {0} {1}", "C:\\source", "C:\\destination");
p.StartInfo.FileName = "CMD.EXE";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.Start();
p.WaitForExit();
/C Robocopy -> this is a command to run robocopy
/S -> This will help to copy sub folders as well as Files

I would just use System.IO. Should be plenty fast enough, and your filename could be a wildcard.
using System.IO;
// snip your code... providing fileName, sourceDir, destinationDir
DirectoryInfo dirInfo = new DirectoryInfo(sourceDir);
FileInfo[] fileInfos = dirInfo.GetFiles(fileName);
foreach (FileInfo file in fileInfos)
{
File.Copy(file.FullName, Path.Combine(destinationDir, file.Name), true); // overwrites existing
}

You should call File.Copy in a loop.

Robocopy can use up to 128 thread by itself. It makes a huge difference. By default it uses 8.
See https://pureinfotech.com/robocopy-multithreaded-file-copy-windows-10/

.cmd has following lines
Start ROBOCOY src dest a* b* c* /z /w:1 r:1
Start ROBOCOY src dest d* e* f* g* /z /w:1 r:1
Start ROBOCOY src dest h* K* P* Y* /z /w:1 r:1
Start ROBOCOY src dest xry* srp* /z /w:1 r:1
When I run > Robocopy sample.cmd
I starts with 4 multiple windows copying files simultaneously as per above commands, it waits
for another file, as it has wait time, if file is being used by another process. Is is more
faster as it do job simultaneously.
Now I am developing GUI using C# windows to run the process instead going to command console and
start
main()
{
process.start( "path of sample.cmd" )
process.waitforexit()
label.text=" sucessful copy"
}
However, if it takes control of one process, i.e. cmd.exe and and there are 4 robocopy processes in
taskmanager. when cmd.exe process completes, it returns the cursor to label.text "Sucesssfully
completed". While there are robocopy processes still running. you can see the robocopy windows
doing the copying process.
Here is the question: I want to take control of all the processes (cmd.exe and robocopy.exe)
programatically in C#, so that when the label.text should display "successfully completed" only
when all commands are successfully completed", if one fails, then there is no point in the GUI.
option 2 (similar to Biju has written above): is it better to remove robocopy command scripts from
sample.cmd(batch file) file and write code to run the 4 robocopy lines in C#, but how to run the
robocooy script line written .cmd file, as they have arguments as well. I code runs each robocopy
process then each will return to the next line of code and if it fails, we can catch the error and
display in the message box.
Hope this will help... However, I am looking for more better way, if somebody can improve on the same.

Related

C# How to start .bat File with the normal Behaviour of Windows

I created this simple .bat File:
mstsc C:\Temp\example.rdp
DEL C:\Temp\example.rdp
The normal behaviour of the .bat file is to open the rdp dialog and wait til I close the RDP connection. After that the .rdp file will be deleted.
This works perfect for me.
Now I want to open the .bat file from my C# WPF project with a click on a button.
Process p = new Process();
p.StartInfo.FileName = #"cmd.exe";
p.StartInfo.WorkingDirectory = #"C:\Temp";
p.StartInfo.Arguments = "/k " + "example.bat";
p.Start();
I tried all different arguments but the result is always the same the .bat file won't wait for the mstsc command to finish.
Do you have an idea to get this work?
Edit:
I want the .bat file because i want to delete the .rdp file although my program isn't running and i don't want to close all rdp connections when i close the program.
Why are you not directly running mstsc?
Process.Start("mstsc", "dir-to-blah.rdp").WaitForExit();
File.Delete("dir-to-blah.rdp");
try using the "start" command.
start is documented here: https://ss64.com/nt/start.html
you should be able to do something like
Process p = new Process();
p.StartInfo.FileName = #"cmd.exe";
p.StartInfo.WorkingDirectory = #"C:\Temp";
p.StartInfo.Arguments = "start /w" + "/k " + "example.bat";
p.Start();
(as usual, my code is completely untested - leaving the final solution up to the OP)

forcing to close a file

I have file that another process using it.
and I want to force closing the file. so that I will work on the fill.
I tried to use Handle.exe however it didn't find the process
would appreiciate some help here is my code:
Process tool = new Process();
tool.StartInfo.FileName = handlPath;
tool.StartInfo.Arguments = _pathDirectory + " /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern))
{
Process.GetProcessById(int.Parse(match.Value)).Kill();
}
I'm sure, that if a program really holds an exclusive access to a file, it has a reason to do it. For example, Windows Explorer holds it when the file is in copying process.
Very often, programs open a file for a writing, but do not actively write to it. For example, when you open a document in MS Word, it is copied to the temp file and a source file is just "open for writing". You'll still have an exception if you use standard File.Open method, but you can copy it to a temp file using File.Copy.
Alternatively, you can explicitly specify FileShare.ReadWrite parameter and get an access to a file. In this case, other program will have problems with accessing a file.
If you have mentioned the file name or type it would have been more easier, anyway try using the cmd for this. Get the process name and replace ProcessName.exe of the following code.
First you'll have to add using System.Diagnostics; on top.
Process.Start("cmd.exe", "/c taskkill /F /IM ProcessName.exe");

Is there a way to execute an exe stored with a temp file name?

I'm writing a native exe stored as an embedded resource to the file returned by Path.GetTempFileName. That function is desirable since it lets me ignore the implementation details that avoid the race condition. The only downside is that it returns a filename with the extension .tmp.
Process.Start opens the tmp-named-exe file in Notepad, on my system. Apparently you can specify the class and verb manually using p/invoke (see https://stackoverflow.com/a/12681219/521757). Before I do that, is there any way to accomplish the same thing using the .NET framework?
I think it is possible.. set the Process.StartInfo.UseShellExecute property to False prior to starting the process, e.g.:
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = #"c:\tmp\a123.tmp";
p.StartInfo.UseShellExecute = false;
p.Start();
This will start the process directly and the file should be considered to be executable itself. Does it work for you?
Why don't you use Path.GetRandomFileName() in conjunction with Path.GetTempPath()?
This way you still have a unique temporary file and path name and you can append '.exe', eliminating the current problem that you have with executing a file with a '.tmp' extension.
As a sideline, I hope that you remember to delete the temporary files after you have used them, especially when using Path.GetTempFileName(), since it can cause issues when you reach 65535 temporary files. Which is very possible and frustrating to debug when that actually happens on production code.
this should work for you
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = //your filename .tmp
proc.StartInfo.UseShellExecute = false;
proc.Start();

How to run "Run Bar" commands?

I need to run a command "control bthprops.cpl" in a C# program. This command brings up Bluetooth Settings control panel window. I tried running it using Process.Start() but the bluetooth window doesn't show up. I also tried writing a BAT file to the disk and executing it though my program, but still has the same problem. Is there any way to accomplish this?
//Dump BAT File and execute it
string path = System.IO.Directory.GetCurrentDirectory()+"startBT.bat";
string[] content = {"control bthprops.cpl"};
System.IO.File.WriteAllLines(path, content);
//Execute BAT file
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = path;
p.Start();
No need to go with a BAT file, this single line ought to open the specified control panel;
System.Diagnostics.Process.Start("control", "bthprops.cpl");
Since I don't have aforementioned bthprops.cpl; at least this works on W7 (open desktop settings)
System.Diagnostics.Process.Start("control", "desk.cpl");
If your control panel has tabs, you can even select what tab to open;
System.Diagnostics.Process.Start("control", "bthprops.cpl,,2");
Supply the full path and start it, for example:
var path = Path.Combine(Environment.SystemDirectory, "bthprops.cpl");
if (File.Exists(path))
{
Process.Start(path);
}

How to pass string to batch file through c# and how batch file will accept string argument?

My scenario in C# project is user will pass path like "c:\homedir\mydir" to batch file then batch file should accept this path and create directory at the specified path.
I don't know how to pass string to batch file through c# and how batch file will accept string and process it.
Create a process and pass your argument(s) through the StartInfo.Arguments property.
Process proc = new Process();
proc.StartInfo.FileName = //path to your BAT file
proc.StartInfo.Arguments = String.Format("{0}", #"C:\homedir\mydir");
//set the rest of the process settings
proc.Start();
That will load your BAT file and pass whichever arguments you've added. Your BAT file can access the arguments using %1 for the first argument, %2 for the second one etc...
Since you didn't gave us any information, I just give an example of these subjects.
First of all, you need to use Process class include System.Diagnostics namespace.
Provides access to local and remote processes and enables you to start
and stop local system processes.
An example with a batch file:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "yourbatchfile.bat";
p.Start();
For passing arguments, you can use ProcessStartInfo.Arguments property.
Gets or sets the set of command-line arguments to use when starting
the application.

Categories