Skip to content

Commit

Permalink
Fix crash issue on some special Windows machines.
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Jan 26, 2024
1 parent 341ccb8 commit 97e77b9
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 9 deletions.
68 changes: 59 additions & 9 deletions file_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,60 @@ package zenity
import (
"fmt"
"path/filepath"
"runtime"
"syscall"
"unicode/utf16"
"unsafe"

"github.com/ncruces/zenity/internal/win"
)

// coInitializeEx initializes the COM library for use by the calling thread,
// sets the thread's concurrency model, and creates a new apartment for the thread if one is required.
//
// The second call to GetOpenFileName crashes on some special machines:
// https://stackoverflow.com/questions/35366998/2nd-call-to-getopenfilename-crashes-without-error-on-win-8-1-64-bit-machine
//
// The following was tested on a crashed machine ( Windows 10 Professional 22H2 19045.3803 ):
//
// Case 1:
// - CoInitializeEx was not called.
// - The first call to GetOpenFileName ( with OFN_EXPLORER ) succeeds.
// - The second call to GetOpenFileName ( with OFN_EXPLORER ) crashes.
//
// Case 2:
// - The first call to CoInitializeEx succeeds.
// - The first call to GetOpenFileName ( with OFN_EXPLORER ) succeeds.
// - Call CoUninitialize to close the COM library on the current thread.
// - The second call to CoInitializeEx succeeds on the same thread.
// - The second call to GetOpenFileName ( with OFN_EXPLORER ) crashes.
//
// Case 3:
// - The first call to CoInitializeEx succeeds.
// - The first call to GetOpenFileName ( with OFN_EXPLORER ) succeeds.
// - CoUninitialize was not called.
// - The second call to CoInitializeEx on the same thread fails but ignores it.
// - The second call to GetOpenFileName ( with OFN_EXPLORER ) succeeds.
//
// https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-couninitialize
// The documentation says CoUninitialize should be called on application shutdown.
// It is hard to call CoUninitialize in each thread when the process exits in Golang.
// So, we let the operating system to clean it up after the process exits.
func coInitializeEx() error {
err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE)
if err != nil && err != win.RPC_E_CHANGED_MODE && err != win.S_FALSE {
return err
}
return nil
}

func selectFile(opts options) (string, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := coInitializeEx(); err != nil {
return "", err
}

if opts.directory {
res, _, err := pickFolders(opts, false)
return res, err
Expand Down Expand Up @@ -54,6 +100,12 @@ func selectFile(opts options) (string, error) {
}

func selectFileMultiple(opts options) ([]string, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := coInitializeEx(); err != nil {
return nil, err
}

if opts.directory {
_, res, err := pickFolders(opts, true)
return res, err
Expand Down Expand Up @@ -122,6 +174,12 @@ func selectFileMultiple(opts options) ([]string, error) {
}

func selectFileSave(opts options) (string, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := coInitializeEx(); err != nil {
return "", err
}

if opts.directory {
res, _, err := pickFolders(opts, false)
return res, err
Expand Down Expand Up @@ -174,16 +232,8 @@ func pickFolders(opts options, multi bool) (string, []string, error) {
owner, _ := opts.attach.(win.HWND)
defer setup(owner)()

err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE)
if err != win.RPC_E_CHANGED_MODE {
if err != nil {
return "", nil, err
}
defer win.CoUninitialize()
}

var dialog *win.IFileOpenDialog
err = win.CoCreateInstance(
err := win.CoCreateInstance(
win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL,
win.IID_IFileOpenDialog, unsafe.Pointer(&dialog))
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/win/ole32.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (

E_CANCELED = windows.ERROR_CANCELLED | windows.FACILITY_WIN32<<16 | 0x80000000
RPC_E_CHANGED_MODE = syscall.Errno(windows.RPC_E_CHANGED_MODE)
S_FALSE = syscall.Errno(windows.S_FALSE)
)

func CoInitializeEx(reserved uintptr, coInit uint32) error {
Expand Down

0 comments on commit 97e77b9

Please sign in to comment.