How to open a webbrowser at a URL on windows


I want a rust program to try to open a URL on windows using the default browser. I tried using std::process::Command::new("cmd.exe").arg("/C").arg("start").arg(&url) but it just errors out on Windows 7. I found the webbrowser crate but in the source code it does exactly the same thing. (I tried it anyway, and it didn’t work.)

I know that rustup has this code (or at least a non-stable one does), but I only use stable.


The open crate should do the trick. That said, it has a really weird bug where it won’t work if your program terminates too soon after you tell it to open a URL. It’s fine for everything else, though.

As for the cmd thing, have you tried passing "start URL" as a single argument?


I looked at open’s source code and mine was almost the same. So I changed mine to match and it still doesn’t work. I’ve tried with simple urls like and complex ones like but neither works.

Opening a URL seems pretty fundamental to me so I hope it ends up being added to the std library.


I have now tried using ShellExecuteW as used here: rustup code (scroll to line 377) which is similar to that used here: url_open crate (scroll to line 34).

My code (Windows-specific) is here: Playground.

The ShellExecuteW call appears to work, but it does not start up a web browser. My default browser is Firefox, but Windows start understands that.

I’ve now found the problem (hinted at by someone earlier): timing. I now call open_url and sleep for 10 secs and this is sufficient for the browser to be started.

But now I have a new problem: when my program ends (which it does after opening the browser), it closes the browser. I want it to open the web page, terminate, and leave the web browser running…

So maybe ShellExecuteW is the wrong thing to be calling?


My use case is that someone might run my console app with -h and get the usual help text, but I also want to offer a --doc option which will print a URL of where the docs are on my website, and also open the user’s browser at that URL if possible.

I tried ShellExecuteW, libc’s system but neither are any better than process::Command. Here’s what I’ve got:

fn open_url(url: &str) -> bool {
    if let Ok(mut child) = Command::new("cmd.exe")
            .arg("/C").arg("start").arg("").arg(&url).spawn() {
        thread::sleep(time::Duration::new(3, 0)); // On windows need to allow time for browser to start
        if let Ok(status) = child.wait() {
            return status.success();

Good behavior: If the user’s web browser is already open, this works fine. It opens a new tab in the browser at the given url and then continues (in my case terminates), leaving the new tab in the browser.

Bad behavior: But if the user doesn’t have a browser open, it opens the browser at the url, then after terminating, the browser is unceremoniously shut down. And because of this failure case, the open_url() function shown here is useless.

Incidentally I’ve observed the same good and bad behaviors when using ShellExecuteW.


I’ve had another go at this. I’ve copied & slightly modified code from sccache.

Here’s the calling code:

fn doc() -> Result<i32> {
    let reply = run_detached("cmd.exe /C start \"\" doc.html");
    thread::sleep(time::Duration::new(5, 0)); // Windows needs time to get going...

and here’s the copied/modified code:

fn run_detached(cmd: &str) -> Result<i32> {
    //use std::error::Error as StdError;
    use std::io::Error;
    use std::mem;
    use std::ptr;
    use winapi::shared::minwindef::{TRUE, FALSE, DWORD};
    use winapi::um::handleapi::CloseHandle;
    use winapi::um::processthreadsapi::{CreateProcessW, PROCESS_INFORMATION,
    use widestring::WideCString;
    let cmd = cmd.replace("&", "^&");
    let cli = WideCString::from_str(&cmd).unwrap();
    let mut pi = PROCESS_INFORMATION {
        hProcess: ptr::null_mut(),
        hThread: ptr::null_mut(),
        dwProcessId: 0,
        dwThreadId: 0,
    let mut si: STARTUPINFOW = unsafe { mem::zeroed() };
    si.cb = mem::size_of::<STARTUPINFOW>() as DWORD;
    if unsafe {
                cli.as_ptr(),       // appname or command line
                ptr::null_mut(),    // command line or NULL and all above
                ptr::null_mut(),    // security
                ptr::null_mut(),    // security
                FALSE,              // inherit handles
                CREATE_NEW_PROCESS_GROUP,   // flags
                ptr::null_mut(),    // env
                ptr::null(),        // cwd
                &mut si,            // startup info
                &mut pi)            // process info
                == TRUE } {
        unsafe {
    } else {
        bail!("failed to run '{}': {:?}", cmd, Error::last_os_error())
        //bail!("failed to run {}: {}",
        //      cmd, Error::last_os_error().description())

When I do this in the console it works perfectly:
cmd.exe /C start "" doc.html
But when I run the program, it doesn’t work:

V:\myapp>myapp.exe --doc
Error: failed to run 'cmd.exe /C start "" doc.html': Os { code: 123, kind: Other, message: "The filename, directory name, or volume label syntax is incorrect." }

BTW It is best to give start an empty string as its first argument.

PS Cargo.toml:

widestring = "0.3"
winapi = { version = "0.3", features = ["winuser", "handleapi",
                                        "processthreadsapi"] }


My mistake!

Using std::process::Command works fine. The problem I had was I always tested using cargo run. If I run the .exe directly it correctly opens the web browser if not already running, or opens a new tab if it is, and in either case, leaves the web browser after the rust .exe terminates.