//go:build windows package main import ( "bytes" "encoding/base64" "fmt" "os" "os/exec" "strings" ) // Common file dialog (WinForms SaveFileDialog) owned by an invisible topmost // form so it appears above the printing app. OverwritePrompt asks before // replacing an existing file. const saveDialogPS = ` $ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.Windows.Forms [Windows.Forms.Application]::EnableVisualStyles() function D($v) { [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($v)) } $d = New-Object Windows.Forms.SaveFileDialog $d.Title = 'Save timestamped print as' $d.Filter = 'PDF files (*.pdf)|*.pdf' $d.DefaultExt = 'pdf' $d.AddExtension = $true $d.OverwritePrompt = $true $d.CheckPathExists = $true $d.InitialDirectory = D $env:VERAE_SAVE_DIR $d.FileName = D $env:VERAE_SAVE_NAME $o = New-Object Windows.Forms.Form $o.TopMost = $true $o.ShowInTaskbar = $false $o.FormBorderStyle = 'None' $o.StartPosition = 'CenterScreen' $o.Size = New-Object Drawing.Size(1, 1) $o.Opacity = 0 $o.Show() $o.Activate() $r = $d.ShowDialog($o) $o.Close() if ($r -eq [Windows.Forms.DialogResult]::OK) { 'ok|' + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($d.FileName)) } else { 'cancel' } ` func nativeSaveDialog(name, dir string) (string, bool, error) { cmd := exec.Command("powershell.exe", "-NoProfile", "-NonInteractive", "-STA", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", saveDialogPS) cmd.Env = append(os.Environ(), "VERAE_SAVE_NAME="+b64(name), "VERAE_SAVE_DIR="+b64(dir)) hideWindow(cmd) var out, errb bytes.Buffer cmd.Stdout, cmd.Stderr = &out, &errb if err := cmd.Run(); err != nil { return "", false, fmt.Errorf("%v: %s", err, strings.TrimSpace(errb.String())) } s := strings.TrimSpace(out.String()) if s == "cancel" { return "", false, nil } if !strings.HasPrefix(s, "ok|") { return "", false, fmt.Errorf("unexpected dialog output") } p, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(s, "ok|")) if err != nil || len(p) == 0 { return "", false, fmt.Errorf("bad dialog output") } return string(p), true, nil }