Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::process::Command feature request: .launch() or .start() #50679

Closed
mark-summerfield opened this issue May 12, 2018 · 11 comments
Closed

std::process::Command feature request: .launch() or .start() #50679

mark-summerfield opened this issue May 12, 2018 · 11 comments
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@mark-summerfield
Copy link
Contributor

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.

@sfackler
Copy link
Member

Isn't that what Command::spawn does?

@pietroalbini pietroalbini added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. C-feature-request Category: A feature request, i.e: not implemented / a PR. labels May 12, 2018
@mark-summerfield
Copy link
Contributor Author

mark-summerfield commented May 12, 2018

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.

@the8472
Copy link
Member

the8472 commented May 12, 2018

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?

@mark-summerfield
Copy link
Contributor Author

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)

@abonander
Copy link
Contributor

abonander commented May 13, 2018

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?

@retep998
Copy link
Member

retep998 commented May 13, 2018

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: rust-lang/cargo#4575

@mark-summerfield
Copy link
Contributor Author

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!

@mbodm
Copy link

mbodm commented May 11, 2022

I would give you 10+ likes , if i could. Your „mistake“ saved my day! 😁

@Zymlex
Copy link

Zymlex commented Jun 9, 2024

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...

@lucioreyli
Copy link

lucioreyli commented Jul 26, 2024

Only Command::new("rundll32.exe") or omitting Command::new("rundll32") looks great, if case C:/ disk doesn't exists

@Zymlex
Copy link

Zymlex commented Jul 29, 2024

Only Command::new("rundll32.exe") or omitting Command::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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants