For instance, let's say I have a folder with the following in it:
log.bat
clear.bat
new.bat
init.exe
Each .bat file calls init once or more times. I do not have access to any of the .bat files, so there is not way that I can pass a variable to init.exe. One thing to know about init is a C# application and can accept arguments.
Possibilities:
DOSKEYS - Turns out that they don't work for .bat files.
Environment Variables - I thought I could name an environment variable called init that would do something like init %~n0 to get the batch file name. Sadly, this doesn't work either.
Hacky Alias - Make a batch file named init.bat (as the .bat files call init, not init.exe). Then, in the init.bat file, I would simply put init.exe %~n0. Two things went wrong with this. First, the .bat files for some reason took init.exe priority over init.bat, and so the batch file alias wasn't even called. Secondly, the %~n0 part expanded to init, as it was called from init.bat, not the other batch files.
Am I out of luck? Or is there a hacky method that could work for this?
C:\Windows\system32>wmic process where "commandline like 'notepad'" get parentprocessid
ParentProcessId
5908
C:\Windows\system32>wmic process where "processid=5908" get commandline
CommandLine
C:\Windows\system32\cmd.exe /c ""C:\Users\User\Desktop\New Text Document (2.bat" "
Or to see all info on that batch process
wmic process where "processid=5908" get /format:list
This is not the most elegant solution, but if there's only one of those batch file running at a given time, you could try to list all the cmd.exe processes with Process.GetProcessesByName("cmd"), then find the one running one of the batch file by extracting its command line argument using this approach: https://stackoverflow.com/a/2633674/6621790
The ideas of both Remi and Noodles helped me come to this answer. In C#, I used the following to get the PID of the terminal calling the executable:
//Get PID of current terminal
//Reference: https://github.com/npocmaka/batch.scripts/blob/master/hybrids/.net/getCmdPID.bat
var myId = Process.GetCurrentProcess().Id;
var query = String.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
var search = new ManagementObjectSearcher("root\\CIMV2", query);
var results = search.Get().GetEnumerator();
if (!results.MoveNext())
{
Console.WriteLine("Error");
Environment.Exit(-1);
}
var queryObj = results.Current;
var parentId = queryObj["ParentProcessId"];
int myPid = Convert.ToInt32(parentId);
Related
I have created a script using Python2.7 and compiled it using pyinstaller into an exe of the same name, in this case "GeneralStats.py" turns into "GeneralStats.exe" using --onefile and -w arguments.
When called with C# I use:
var pythonDirectory = (Directory.GetCurrentDirectory());
var filePathExe1 = Path.Combine(pythonDirectory + "\\Python\\GeneralStats.exe");
Process.Start(filePathExe1);
When called outside of C#, so in my local files I can run the .exe and the result is a text file with lots of values in (Running correctly).
However, when ran with C# in this format, I get an error that "GeneralStats returned -1!"
Which I have had issues with before, but it was a simple python error that when I returned to my code and ran it, I would receive an error that I overlooked.
This time my python code returns no errors and works outside of C#.
Any ideas of why this could be? I can provide any code or file directories necessary, please just ask if you feel it would help with debugging.
EDIT:
Solved by removing:
var filePathExe1 = Path.Combine(pythonDirectory + "\\Python\\GeneralStats.exe");
Process.Start(filePathExe1);
And replacing with:
ProcessStartInfo _processStartInfo = new ProcessStartInfo();
_processStartInfo.WorkingDirectory = Path.Combine(pythonDirectory + "\\Python");
_processStartInfo.FileName = #"GeneralStats.exe";
_processStartInfo.CreateNoWindow = true;
Process myProcess = Process.Start(_processStartInfo);
You need to set the working directory for the Process - it is probably trying to load files from its working directory but isn't finding them.
See, e.g. this:
Use the ProcessStartInfo.WorkingDirectory property to set it prior to starting the process. If the property is not set, the default working directory is %SYSTEMROOT%\system32.
Set it to the path where GeneralStats.exe is.
I am a newbie programmer wanting to make averaging videos. I have made a program to create n .bat files doing the average of n images, now I would like to execute them as fast as possible.
The .bat files are independent.
I am in a Windows environment.
I have looked at C# multi threading (threadpool, parrallel.for, parralel.foreach etc), but none of the functions there seems to work. I have no illusion that it's me who's doing something wrong though.
Powershell has a function doing what I want, but only for other powershell commands.
The code I have now that mostly works is:
(complete solution at https://github.com/Madsfoto/ParallelExecutionForEach )
var paths = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.bat"); // have a list of all .bat files in the current directory
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false; // above is to not see the cmd window
proc.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory(); // It's easier than having to specify where this program will be run.
Parallel.ForEach(paths, new ParallelOptions { MaxDegreeOfParallelism = 4 }, currentFile => // 4 is set because I have 4 cores to use
{
proc.StartInfo.FileName = currentFile; // Set the currentfile as the one being executed. currentFile is the name of the .bat file to execute
proc.Start(); // execute the .bat file
proc.WaitForExit();
File.Delete(currentFile);
});
I get System.InvalidOperationException: No process is associated with this object and System.UnauthorizedAccessException’s when I run more than 3-4 processes at the same time.
I suspect that it is the WaitForExit() that is giving me problems, but do not have the skills to debug it.
I have looked at Threading.Task as well, but my skill is not good enough to use it.
So the solution I am after is as follows:
Execute either 1 input file with x lines of independent action or x files with 1 action, with a limit of y processes at the same time either set at compile or runtime.
Programming language is not important to me, although my preference is understandable C#.
(The result is something like https://www.youtube.com/watch?v=ph6-6bYTgs0, with n frames averaged together)
The solution was to move the
proc.Start(); // execute the .bat file
proc.WaitForExit();
try
{
File.Delete(FileName);
}
catch
{
}
code into it's own function (with the bookkeeping stuff (defining proc etc)).
The File.Delete() was the culprit, it turns out that there might be a bug in the Parallel.ForEach, but I have been unable to reproduce it reliably (experimenting gives errors ~0.01% of the time), but this way it works. It does require people to rerun the executable, but that is a burden I can justify pushing to the user.
The github link has been updated with a working version.
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");
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 ?
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"