skratchdot / open-golang

Open a file, directory, or URI using the OS's default application for that object type. Optionally, you can specify an application to use.
MIT License
783 stars 65 forks source link

Open folder in foreground #16

Open niondir opened 5 years ago

niondir commented 5 years ago

When opening a folder location, the window (at least on windows) appears in background behind other applications. Is there any way to put it in foreground?

skratchdot commented 5 years ago

@Niondir - in windows, this is the command that is used: https://github.com/skratchdot/open-golang/blob/master/open/exec_windows.go#L27

Here are the start docs: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/start

I don't know of a way to force it to open in the foreground. If you find a way that works, you can post the command here, and we could try to submit a PR.

xiegeo commented 5 years ago

You can try SetForegroundWindow if you can get HWND of the window, but windows prevent apps from going into the foreground in some cases, so I couldn't get that to work dependably for me.

The reason that your new window appears in the background is probably of the same reason. If you have a background process that opens a new window without user interaction, at least from how the OS see it, the best it will let you do is flash the taskbar.

I have also tried BringWindowToTop, SwitchToThisWindow, and ShowWindow, but they all seem to have the same limitations.

niondir commented 5 years ago

Do you know a reliable way of getting the HWND of the just startet process? Or do we need to iterate all handles and find it somehow by name? I'm really not into window APIs :)

xiegeo commented 5 years ago

@Niondir If you know the name, you can use FindWindowW

windowName := "window name"
user32, err = syscall.LoadDLL("user32.dll")
findWindow, err = user32.FindProc("FindWindowW")
hwnd, _, err = findWindow.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(windowName))))
EliCDavis commented 3 years ago

So it seems programs launched by your golang application are only put in the foreground if your golang application itself is in the foreground. So before launching your application, just put your golang application in the foreground first.

Here's what I've done to make a program get launched in the foreground, even though the parent program is in the background initially:

package main

import (
    "fmt"
    "log"
    "os"
    "os/exec"
    "syscall"
    "time"
    "unsafe"
)

var (
    kernel32            = syscall.MustLoadDLL("Kernel32.dll")
    getConsoleWindow    = kernel32.MustFindProc("GetConsoleWindow")
    user32              = syscall.MustLoadDLL("user32.dll")
    setForegroundWindow = user32.MustFindProc("SetForegroundWindow")
)

func getWindowPointer() (syscall.Handle, error) {
    r, _, err := syscall.Syscall(getConsoleWindow.Addr(), 0, 0, 0, 0)
    return syscall.Handle(r), err
}

func bringWindowToForeground(hwnd syscall.Handle) (int, error) {
    sucess, _, err := syscall.Syscall(setForegroundWindow.Addr(), 1, uintptr(hwnd), 0, 0)
    return int(sucess), err
}

func main() {

    winPnt, err := getWindowPointer()
    if err != nil {
        log.Println("errpr", err)
    } 
    fmt.Printf("this windows pointer: %d;\n", int(winPnt))

        // Allows me time to manually click over to another window for sake of example.
    time.Sleep(time.Second * 5)

    success, err := bringWindowToForeground(winPnt)
    if err != nil {
        log.Printf("error bringing to foreground:  %+v\n", err)
    }
    fmt.Printf("success: %d;\n", int(success))

    output, err := exec.Command("my program").Output()
    if err == nil {
        os.Stdout.Write(output)
    } else {
        log.Panicln(err)
    }
}

If you know how to make this fail please let me know! I'm definitely out of my comfort zone with all this, and barely know what I'm doing.

EDIT: Can confirm this method still works once the application is properly installed on a user's machine (Tested on Windows 10 Pro)

skratchdot commented 3 years ago

@EliCDavis - Thank you for sharing what worked for you!