Closed mark-summerfield closed 6 years ago
Isn't that what Command::spawn
does?
Not on Windows. If you use spawn() to run cmd.exe /C start "" http://www.site.com/page.html"
. If the user has a browser already running, this works fine, and the rust app can terminate with no problems. But if they don't have a browser running the browser will be started: but then when the rust app terminates, it will also terminate the browser. And this is the problem I'm trying to solve.
The docs on Child
say
There is no implementation of Drop for child processes, so if you do not ensure the Child has exited then it will continue to run, even after the Child handle to the child process has gone out of scope.
So it should already work. Have you tried usiing start
instead of cmd
?
Here's an example:
use std::process::Command;
use std::thread;
use std::time;
fn main() {
println!("Start");
let _child = Command::new("cmd.exe")
.arg("/C").arg("start").arg("").arg("http://www.rust-lang.org")
.spawn().expect("failed to launch browser");
thread::sleep(time::Duration::new(10, 0)); // Windows needs time!
println!("End");
}
If no web browser is running: If you do cargo run
this will compile & output 'Start', then after a second or two will start the browser at the correct page. Then, after ~10 sec the browser will close and 'End' is printed. However if a web browser is already running, the browser is left to continue after 'End' is printed (which is the behavior I'm after in either case).
It is not possible to just use start
on its own since it is not a .exe. For example:
use std::process::Command;
use std::thread;
use std::time;
fn main() {
println!("Start");
let _child = Command::new("start")
.arg("").arg("http://www.rust-lang.org")
.spawn().expect("failed to launch browser");
thread::sleep(time::Duration::new(10, 0)); // Windows needs time!
println!("End");
}
Doesn't work:
V:\tmp\myapp>cargo run --release --
Compiling myapp v0.1.0 (file:///V:/tmp/myapp)
Finished release [optimized] target(s) in 1.67 secs
Running `target\release\myapp.exe`
Start
thread 'main' panicked at 'failed to launch browser: Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }', libcore\result.rs:945:5 note: Run with `RUST_BACKTRACE=1` for a backtrace. error: process didn't exit successfully: `target\release\myapp.exe` (exit code: 101)
What happens if you start the command without piped I/O? The Child
may not be killed when it's dropped but it also contains handles for the child's stdio pipes which are closed on-drop; cmd or the browser might be quitting because those pipes are being closed but it may stay alive if no pipes were opened to begin with.
Addendum: spawn()
actually causes the child to inherit the current process' stdio which is closed when the process exits. If you set all three streams to Stdio::null()
does the browser still quit with your program?
cargo run
cargo run
creates a job object which terminates all processes when cargo
terminates. This means that when your 10 second sleep timer is up and your program exits, any processes that were spawned by your program will be terminated. This is not the fault of Command
but of the way cargo
chooses to use job objects.
Related issue: https://github.com/rust-lang/cargo/issues/4575
retep998: you are quite right! When I run the .exe directly it works as expected: the browser is launched at the right page or a new tab is opened if it is already running, and it keeps running even when the rust .exe finishes.
Sorry for my mistake!
I would give you 10+ likes , if i could. Your „mistake“ saved my day! 😁
let mut cmd = Command::new("C:/Windows/System32/rundll32.exe");
cmd.args(
["url.dll,FileProtocolHandler", url]
);
cmd.spawn()?;
Not the best solution, but better than nothing...
Only Command::new("rundll32.exe")
or omitting Command::new("rundll32")
looks great, if case C:/ disk doesn't exists
Only
Command::new("rundll32.exe")
or omittingCommand::new("rundll32")
looks great, if case C:/ disk doesn't exists
hm, as in the case of a windows disk installer that use X:
in the case of just calling the command by name, there are search priorities and through this the binaries can be spoofed + if you don't specify an extension, the com program will be called first, which can also be spoofed.
The current API for std::process::Command() is ideal for starting an external process and waiting for it to finish. However, there are use cases where you want to start an external process, get immediate feedback on whether it started or not, and then continue (or finish), without waiting for the external process to finish at all.
For example, you might have
--help
for showing command line options, and--manual
to show a full manual which you want to show by starting a web browser at a given URL but don't want the browser window to close when the rust app finishes straight after opening the browser. Similarly, you might have an app that produces results and has a--show
option that tells the app to launch an app to show the results (spreadsheet, photoeditor, whatever); and again, you want the results window to hang around even though the command line app has finished.This is probably easy on Unix; but I can't find a way to do it on Windows.