I have a console application that runs .bat and .vbs files.
The method which starts these processes is as follows:
public void runProcess(string aPath,string aName,string aFiletype)
{
string stInfoFileName;
string stInfoArgs;
if(aFiletype == "bat")
{
stInfoFileName = (#aPath + #aName);
stInfoArgs = string.Empty;
}
else
{ //vbs
stInfoFileName = #"cscript";
stInfoArgs = "//B //Nologo " + aName;
}
this.aProcess.StartInfo.FileName = stInfoFileName;
this.aProcess.StartInfo.Arguments = stInfoArgs;
this.aProcess.StartInfo.WorkingDirectory = #aPath;
this.aProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
this.aProcess.Start();
Console.WriteLine("information passed from batch: ???");
this.aProcess.WaitForExit(); //<-- Optional if you want program running until your script exit
this.aProcess.Close();
}
The current .bat files which it is starting are used to send data over ftp (ftp, ftps, or sftp).
When the process is running all information is shown in the process windows e.g. an error might occur, the file is not transferred and a message detailing this is displayed in this "child" process window. Once the process has finished the "child" window disappears along with the messages.
Can I somehow return this useful information to my main console application window?
Check process exit code (Process.ExitCode) and/or capture console output.
See Capturing console output from a .NET application (C#)
Though you should better use some native .NET SFTP/FTP library.
For SFTP, see SFTP Libraries for .NET
For FTP and FTPS, use can use the FtpWebRequest from .NET framework.
If you want all-in-one solution, I can humbly suggest you my WinSCP .NET assembly. With the assembly, you can have the same code for SFTP, FTP and FTPS. Internally, the assembly actually runs WinSCP console application. So it is doing the same thing you are trying to code yourself.
If you set this.aProcess.StartInfo.UseShellExecute to false then the child process should use the existing console window.
For batch files, this change also means you'll need to explicitly invoke cmd /c to run them, in exactly the same way you're currently invoking cscript /B to run .vbs scripts.
Related
I'm using C#.
I have C# console applicaiton that I want to get the output from another C# application.
I'm get my process as follow:
Process[] proc = Process.GetProcessByName("MyConsoleApp");
How can I get the output?
I can't run the application from my another C# application, I'm only can get the process by name.
What I'm trying:
proc[0].StartInto.RedirectStandardOutput = true;
proc[0].StandardOutput.ReadLine();//Throw an exception
And I'm got an exception:
standard out has not been redirected or the process hasn't started yet
So How can I read the output without to run the process?
If you want to redirect standard output, process needs to be run by your program, otherwise OS won't let you do it [security measure, you wouldn't want random programs to access your password store or browser when you are logging in to your bank account, would you?], in such case, you first need to set UseShellExecute to false.
So like this:
proc[0].StartInto.UseShellExecute = false;
proc[0].StartInto.RedirectStandardOutput = true;
proc[0].StandardOutput.ReadLine();
But if you really are unable to run process from your program, you need to set up other way for them to communicate, be it a temporary file somewhere on disk, pipe, Azure Service Bus, there are many ways, but you cannot make one process just find other process and read it's output.
You cannot access the standard output of any random process on Windows unless you use the Win32 debugging APIs.
The only way to do this is to create the process yourself.
I have a VB6 executable which is accessing some system environment variables. I have implemented a .NET console application which checks if those environment variables exist, creates them if needed, and then runs the VB6 application by calling Process.Start.
Doing this, the VB6 application cannot find the environment variables and it says they don't exist.
If I run the VB6 application from Windows Explorer it works fine and can find the variables.
So it seems the VB6 app is running under the context of .NET console app and cannot access the system environment variables!
Code to set the environment vars .NET Cosnole app:
foreach(var varObject in Variables)
{
var envVar = Envrionment.GetEnvironmentVariable(varObject.Name ,
EnvironmentVariableTarget.Machine);
if(string.IsNullOrEmpty(envVar)
{
Environment.SetEnvironmentVariable(varObject.Name,varObject.Value,
EnvironmentVariableTarget.Machine);
}
}
Code to run the VB6 app from .NET Cosnole app:
var processInfo = new ProcessStartInfo(VB6ApplicationFilePath);
processInfo.UseShellExecute = true
processInfo.WindwoStyle= ProcessWindowStyle.Hidden;
Process.Start(processInfo);
A copy of a program's environment is passed to a program that it starts. As it is a copy the second program only sees the state it was in when given it (and changes it made). No other program can change another program's environment.
When using ShellExecute (which you tell ProcessStart to) you are asking Explorer to start the program for you. The program will get a copy of Explorer's environment.
When changing the system environment, programs can send a message to all windows open saying environment has changed (as setx does - see setx /?). But ONLY Explorer.exe pays attention to this message. So only programs started by explorer after explorer receives this message will see the changes.
These are the API calls that .NET calls. In Windows all programs are started by CreateProcessEx (or older programs CreateProcess). Shellexecute and ShellexecuteEx process the command like you typed it in Explorer's Start - Run dialog (Winkey + R) then changes it and calls CreateProcessEx.
At the command prompt. Type
set MyCat=PewResearch
cmd /k echo %MyCat%
We set an environment variable, start a new command prompt that prints that variable.
This is the message that notifies
WM_SETTINGCHANGE
The system sends the WM_SETTINGCHANGE message to all
top-level windows when the SystemParametersInfo function changes a
system-wide setting or when policy settings have changed.
Applications should send WM_SETTINGCHANGE to all top-level windows
when they make changes to system parameters. (This message cannot be
sent directly to a window.) To send the WM_SETTINGCHANGE message to
all top-level windows, use the SendMessageTimeout function with the
hwnd parameter set to HWND_BROADCAST.
I have a Windows console application that is launched via a schedule setup in Task Scheduler. This console application, as part of its normal runtime, will launch a command prompt in order to run a java program. No, I have no control over the design of the Java program. It was supplied to me as is and I have no rights or access to make changes to it. I also cannot implement it in another language. I must use what was given to me.
At any rate, when my console application tries to run the command prompt it will work just fine if I'm launching the application manually. However, when I try it as an action within Task Scheduler, my console application will start and run as expected until it needs to launch the command prompt. At this point, the console application exits. No error message or code is provided.
How do I get the command prompt window to start as a new window from within my console application when no one is logged into the server?
Thanks for any hints or suggestions you can provide.
* UPDATE *
Here is the code snippet that launches the program from within my console application:
string parameter_save_path = #"C:\output\folder"
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo start_info = new System.Diagnostics.ProcessStartInfo();
start_info.WorkingDirectory = #"C:\mtselect-client";
start_info.FileName = "cmd.exe";
start_info.Arguments = "/C run.bat \"" + parameter_save_path + "\"";
process.StartInfo = start_info;
process.Start();
process.WaitForExit();
The run.bat is what launches the java program.
I think it's too late for this message, but...
Maybe in your batch file you are running your java application with something like: java -jar ApplicationName
First I would do should be comment out the "#echo off" from the batch file, next trace out the batch lines with one echo "x" (being x a natural number starting from 1 and increasing by 1 in each ocurrence). Next I will add a line with java -version, and so I will be sure java app is installed and accesible.
Maybe java needs be ran by an authenticated user and so have java_home defined. Maybe the application needs some JVM parameters like memory size, etc.
Have good luck, tell me and I will try to help
I have a windows service that I would like to be automatically and silently updated. I started using wyBuild to implement this, but have had some issues with it, and decided to try to build my own. I've written a standalone exe that can be called to do the update procedure: checks for a new zip file with the update, downloads it, unzips, stop the windows service, copy files from the zip, then restart the service. This exe works fine when I run it from the commandline and wasn't really difficult to write.
However, now I would like the service (the same one being updated) to shell out to the updater exe to update itself. I first tried Process.Start:
var proc = Process.Start(pathToUpdaterExe);
proc.WaitForExit(60000);
This called the updater, but when the updater stops the service, the process is killed and the update stops. I did some searching and it sounds like the solution is to use a separate AppDomain. This is what I have now:
Evidence baseEvidence = AppDomain.CurrentDomain.Evidence;
Evidence objEvidence = new System.Security.Policy.Evidence(baseEvidence);
AppDomainSetup setup = new AppDomainSetup();
var updateDomain = AppDomain.CreateDomain("updateDomain", objEvidence, setup);
updateDomain.ExecuteAssembly(updater);
AppDomain.Unload(updateDomain);
However, now I get the error System.IO.IOException: "The process cannot access the file 'C:\Program Files (x86)\Company\Service\Service.dll' because it is being used by another process" when attempting to copy over the new Service.dll
Again, I've stopped the service at this point. I've confirmed this with logging. I can't imagine what would have Service.dll still locked, so I added code to check to see what is locking it:
public static IEnumerable<Process> GetProcessesLocking(string filePath)
{
var result = new List<Process>();
result.Clear();
var processes = Process.GetProcesses();
foreach (Process proc in processes)
{
try
{
if (proc.HasExited) continue;
foreach (ProcessModule module in proc.Modules)
{
if ((module.FileName.ToLower().CompareTo(filePath.ToLower()) == 0))
{
result.Add(proc);
break;
}
}
}
catch (Exception ex)
{
Log(ex.ToString());
Log("There was an error checking " + proc.ProcessName );
}
}
return result;
}
However this code indicates that nothing has a lock on the dll (result is empty and nothing is logged indicating an error).
I suspect I'm running afoul of some UAC issue that is the real cause of the IOException. The windows service runs as LocalSystem. All that to ask: How should I be running the update exe from the windows service such that it has rights to copy files in c:\Program Files?
Update
As the comments and answer suggest, Process.Start can work, but there is some nuance. You have to start cmd.exe and use it to start the updater. I also found I could not use a full path for the updater exe and that I needed to set UseShellExecute=false. This is my final working code that launches the updater from the .NET service:
var cmd = "/c start updater.exe";
var startInfo = new ProcessStartInfo("cmd.exe");
startInfo.Arguments = cmd;
startInfo.WorkingDirectory = AssemblyDirectory;
startInfo.UseShellExecute = false;
var proc = Process.Start(startInfo);
I did this exact thing - using a simpler (some might say kludgy) approach. The service:
Produces a batch command,
Downloads the new executables to a staging location,
Starts a process: cmd.exe which, in turn, runs the batch script w/o waiting for it to complete, and then
Immediately terminates itself.
The batch command:
Pings 127.0.0.1 five times,
Copies the executables to the final location and,
Restarts the service.
Works like clockwork. The ping is a reliable 5 second delay - lets the service shutdown before copying the files.
Edit:
Just for completeness - I realized that by batch cmd is pinging 127.0.0.1 not 128.0.0.1 and so I edited this answer to reflect that. I suppose either works - but 128.0.0.1 pings timeout, where 127.0.0.1 resolves to "me". Since I'm only using it as a poor-man's delay, it serves the purpose either way.
I am working on windows application. i have to run some window exe from my app, i am able to do the same but when i close my application these exe remains on running condition, i am not getting how can i close those exe. Please suggest some tips.
To run the Process
private void StartChildProcess(string fileName)
{
Process newProcess = new Process();
newProcess.StartInfo = new ProcessStartInfo(fileName); ;
newProcess.Start();
localProcess.Push(newProcess);
}
To close the process
private void CloseStartedProcesses()
{
while (localProcess.Count > 0)
{
Process process = localProcess.Pop();
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
Some options:
Setup some communication system so the Main application can alert the other application to shutdown (read up on some WCF information or remoting)
Create a do.shutdown file and let the second application check if that file exists, simple but efficient.
Use the process.Kill options
Use Sendkey or equivalent to send a 'quit' key combination
Use Windows API - P/Invoke. FindWindow() or EnumWindows() to get the window handle. Then you can send WM_CLOSE or WM_QUIT to end the application via the SendMessage() function.
Note that if the application checks for user input on exiting (like a MessageBox asking weather the user really wants to quit) the only option might be to send WM_DESTROY which would be equivalent to Process.Kill (at least in respects to causing data loss - I am not certain it is the absolute equivalent).
Try this:
Process[] p = Process.GetProcessesByName("osk");
foreach (var item in p)
{
item.Kill();
}
The reason that the EXE you've ran from your application doesn't terminate once you close your application is probably because the 2nd application runs as a DIFFERENT, SEPARATE process.
If you run another process with System.Diagnostics.Process, it will remain in background until terminated manually or until it finishes it's job.
try this Process proc = Process.GetProcessesByName("processname");
proc.Kill();