ncruces / zenity

Zenity dialogs for Golang, Windows, macOS
https://pkg.go.dev/github.com/ncruces/zenity
MIT License
736 stars 37 forks source link

Context cancellation does not close file selection dialogs immediately on Windows #119

Open gz-95 opened 2 months ago

gz-95 commented 2 months ago

Thanks for this really helpful library! Just having some trouble with closing the dialogs programmatically.

It seems that when the context is canceled, the dialog remains open until the user interacts with it e.g. navigating different directories etc. Here's an example code that reproduces the issue on Windows 10.0.19045 Build 19045

package main

import (
    "context"
    "fmt"

    "github.com/ncruces/zenity"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    stop := context.AfterFunc(ctx, func() {
        fmt.Println("canceled!")
    })

    defer stop()

    file, err := zenity.SelectFile(
        zenity.Context(ctx),
    )

    if err != nil {
        panic(err)
    }

    fmt.Println("select", file)
}

Basically, the call to zenity.SelectFile is blocked with no way of returning even if the context has expired unless the user is actively interacting with the underlying dialog.

ncruces commented 2 months ago

I don't have a windows machine handy. Do you know which specific windows API is blocked?

gz-95 commented 2 months ago

In my testing, it appears that IModalWindow::Show continues to block until active user interaction even though a successful call to IFileDialog::Close has returned.

I'm starting to wonder if this is just the way these Windows functions are implemented?

Also, it looks like dialogHookProc never gets called even with a successful SetWindowsHookEx registration. That explained why I wasn't seeing this line firing.

ncruces commented 2 months ago

Thanks for the detailed report. Yeah, I'm not seeing a way out of this…

gz-95 commented 2 months ago

Agreed. I've ditched IFileDialog::Close because I'm convinced that it was only ever intended to be called inside IFileDialogEvents callbacks.

Workaround: if I call CoInitializeEx with COINIT_APARTMENTTHREADED, somehow SetWindowsHookEx seems to work again for the dialogs. So I went with that and just call PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0) when the context expires.

ncruces commented 2 months ago

This was put in place because of #89. I'm a bit reluctant to go back on that.