Don't CoUninitialize, close MTA dialog.

This commit is contained in:
Nuno Cruces 2024-02-23 13:06:35 +00:00
parent 1e77b878b5
commit 99f1a258bc
3 changed files with 48 additions and 9 deletions

View file

@ -246,6 +246,18 @@ func fileOpenDialog(opts options, multi bool) (string, []string, bool, error) {
}
defer unhook()
if opts.ctx != nil && opts.ctx.Done() != nil {
wait := make(chan struct{})
defer close(wait)
go func() {
select {
case <-opts.ctx.Done():
dialog.Close(win.E_TIMEOUT)
case <-wait:
}
}()
}
err = dialog.Show(owner)
if opts.ctx != nil && opts.ctx.Err() != nil {
return "", nil, true, opts.ctx.Err()
@ -352,6 +364,18 @@ func fileSaveDialog(opts options) (string, bool, error) {
}
defer unhook()
if opts.ctx != nil && opts.ctx.Done() != nil {
wait := make(chan struct{})
defer close(wait)
go func() {
select {
case <-opts.ctx.Done():
dialog.Close(win.E_TIMEOUT)
case <-wait:
}
}()
}
err = dialog.Show(owner)
if opts.ctx != nil && opts.ctx.Err() != nil {
return "", true, opts.ctx.Err()
@ -428,14 +452,19 @@ func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uin
func coInitialize() (context.CancelFunc, error) {
runtime.LockOSThread()
err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE)
if err == nil || err == win.S_FALSE {
return func() {
win.CoUninitialize()
runtime.UnlockOSThread()
}, nil
// .NET uses MTA for all background threads, so do the same.
// If someone needs STA because they're doing UI,
// they should initialize COM themselves before.
err := win.CoInitializeEx(0, win.COINIT_MULTITHREADED|win.COINIT_DISABLE_OLE1DDE)
if err == win.S_FALSE {
// COM was already initialized, we simply increased the ref count.
// Make this a no-op by decreasing our ref count.
win.CoUninitialize()
return runtime.UnlockOSThread, nil
}
if err == win.RPC_E_CHANGED_MODE {
// Don't uninitialize COM; this is against the docs, but it's what .NET does.
// Eventually all threads will have COM initialized.
if err == nil || err == win.RPC_E_CHANGED_MODE {
return runtime.UnlockOSThread, nil
}
runtime.UnlockOSThread()

View file

@ -23,6 +23,7 @@ const (
CLSCTX_ALL = windows.CLSCTX_INPROC_SERVER | windows.CLSCTX_INPROC_HANDLER | windows.CLSCTX_LOCAL_SERVER | windows.CLSCTX_REMOTE_SERVER
E_CANCELED = windows.ERROR_CANCELLED | windows.FACILITY_WIN32<<16 | 0x80000000
E_TIMEOUT = windows.ERROR_TIMEOUT | windows.FACILITY_WIN32<<16 | 0x80000000
RPC_E_CHANGED_MODE = syscall.Errno(windows.RPC_E_CHANGED_MODE)
S_FALSE = syscall.Errno(windows.S_FALSE)
)

View file

@ -251,6 +251,15 @@ func (u *IFileDialog) SetTitle(title *uint16) (err error) {
return
}
func (u *IFileDialog) GetResult() (item *IShellItem, err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.GetResult, uintptr(unsafe.Pointer(&item)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (u *IFileDialog) SetDefaultExtension(extension *uint16) (err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.SetDefaultExtension, uintptr(unsafe.Pointer(extension)))
@ -260,9 +269,9 @@ func (u *IFileDialog) SetDefaultExtension(extension *uint16) (err error) {
return
}
func (u *IFileDialog) GetResult() (item *IShellItem, err error) {
func (u *IFileDialog) Close(res syscall.Errno) (err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.GetResult, uintptr(unsafe.Pointer(&item)))
hr, _, _ := u.call(vtbl.Close, uintptr(res))
if hr != 0 {
err = syscall.Errno(hr)
}