WKHAllen
WKHAllen

Reputation: 61

How to start a Chrome process and wait for it to terminate

I am attempting to start a Chrome process (in app mode) and then wait for the user to close it.

The Problem: the Chrome process does not block. It's almost as if in launching the process, Chrome spawns another process and returns from the original one. For the sake of the package I am working on, I need to be able to tell when the Chrome app window has been closed.

I am aware that this can likely be done using selenium, though I would very much prefer to keep my package as lightweight as possible.

So far as I can tell, there are no flags that can be passed to the Chrome executable that force it to block.

My code, shown below, will successfully launch the Chrome window, but will then immediately return. The printed debug info shows that the process completes immediately.

package main

import (
    "fmt"
    "os/exec"
    "runtime"
)

func openChrome(url string, appMode bool) error {
    var urlArgs []string
    if appMode {
        urlArgs = []string{"--app=" + url}
    } else {
        urlArgs = []string{"--chrome-frame", url}
    }

    switch runtime.GOOS {
    case "windows":
        var chrome string
        if runtime.GOARCH == "386" {
            chrome = "C:/Program Files/Google/Chrome/Application/chrome.exe"
        } else {
            chrome = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"
        }
        cmd := exec.Command(chrome, urlArgs...)
        err := cmd.Run()
        fmt.Println(cmd.ProcessState.Pid())
        fmt.Println(cmd.ProcessState.Exited())
        fmt.Println(cmd.ProcessState.Success())
        fmt.Println(cmd.Process.Pid)
        if err != nil {
            return err
        }

    case "darwin":
        urlArgs = append([]string{"-b", "com.google.Chrome", "--args"}, urlArgs...)
        var cmd *exec.Cmd
        if runtime.GOARCH == "386" {
            cmd = exec.Command("open", urlArgs...)
        } else {
            cmd = exec.Command("open", urlArgs...)
        }
        err := cmd.Start()
        if err != nil {
            return err
        }

    default:
        cmd := exec.Command("google-chrome", urlArgs...)
        err := cmd.Start()
        if err != nil {
            return err
        }
    }

    return nil
}

func main() {
    openChrome("https://stackoverflow.com/", true)
}

Launching the Chrome executable via command terminal produces the same result; the window opens and operates as expected, but execution of the command does not block:

C:\WINDOWS\system32>"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

C:\WINDOWS\system32>

So how can one start a Chrome process and detect when the actual spawned process has terminated?

Upvotes: 3

Views: 1335

Answers (1)

Lars M Bek
Lars M Bek

Reputation: 31

While working on a similar project and couldn't find a solution like you, I kept working at it and got it working without any use of external code.

This is a Go program that starts a new Chrome browser process and opens the URL "https://google.dk/" in a new window. (you can also use http://localhost:12345 etc, if you host a local webserver)

For this solution it is important that you use --user-data-dir as otherwise it will not be attached to the process. The --user-data-dir installs files inside a chosen location. (Chrome files). In this example i chose the Chrome localappdata location. Note: It changing size constantly between 16mb to 100mb per application when used.

The program sets up a signal handler to gracefully shut down the program when a SIGINT or SIGTERM signal is received. When a signal is received, the program will kill the Chrome process and exit. The Chrome process is started using the exec.Command function, which creates and returns an *exec.Cmd struct.

The cmd.Start function is called to start the Chrome process. The program then sets up a signal handler to listen for SIGINT and SIGTERM signals, and a goroutine is launched to handle the signals. When a signal is received, the goroutine will kill the Chrome process and exit the program. Finally, the program waits for the Chrome process to finish using the cmd.Wait function.

package main

import (
    "os"
    "os/exec"
    "os/signal"
    "syscall"
)

func main() {
    // Start a new Chrome process
    path := "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
    //path = "chrome.exe"
    cmd := exec.Command(path, "--app=https://google.dk/", "--user-data-dir="+os.Getenv("localappdata")+"/Google/Chrome/InstalledApps/NewCompanyName/NewProjectName")
    cmd.Start()

    // Set up a signal handler to gracefully shutdown the program
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
    go func() {
        <-sigs
        cmd.Process.Kill()
        os.Exit(0)
    }()

    // Wait for the process to finish
    cmd.Wait()
}

I hope this helps, it works perfectly for my projects, let me know This solution is for windows only, but you can add a launcher for linux also, that is more simple and uses chromium instead.

NOTE: I haven't found a solution to when the program gets killed by Task Manager, then the chrome frontend "app" is still running

Upvotes: 2

Related Questions