github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/runners/deploy/link_win.go (about) 1 //go:build windows 2 // +build windows 3 4 package deploy 5 6 import ( 7 "strings" 8 9 "github.com/go-ole/go-ole" 10 "github.com/go-ole/go-ole/oleutil" 11 12 "github.com/ActiveState/cli/internal/errs" 13 "github.com/ActiveState/cli/internal/fileutils" 14 "github.com/ActiveState/cli/internal/locale" 15 "github.com/ActiveState/cli/internal/logging" 16 ) 17 18 func shouldSkipSymlink(symlink, fpath string) (bool, error) { 19 // If the existing symlink already matches the one we want to create, skip it 20 if fileutils.FileExists(symlink) { 21 shortcut, err := newShortcut(symlink) 22 if err != nil { 23 return false, errs.Wrap(err, "Could not create shortcut interface") 24 } 25 26 symlinkTarget, err := oleutil.GetProperty(shortcut, "TargetPath") 27 if err != nil { 28 return false, locale.WrapError(err, "err_link_target", "Could not resolve target of link: {{.V0}}", symlink) 29 } 30 31 isAccurate, err := fileutils.PathsEqual(fpath, symlinkTarget.ToString()) 32 if err != nil { 33 return false, locale.WrapError(err, "err_symlink_accuracy_unknown", "Could not determine whether link is owned by State Tool: {{.V0}}.", symlink) 34 } 35 if isAccurate { 36 return true, nil 37 } 38 } 39 40 return false, nil 41 } 42 43 func link(fpath, symlink string) error { 44 if strings.HasSuffix(symlink, ".exe") { 45 symlink = strings.Replace(symlink, ".exe", ".lnk", 1) 46 } 47 logging.Debug("Creating shortcut, destination: %s symlink: %s", fpath, symlink) 48 49 shortcut, err := newShortcut(symlink) 50 if err != nil { 51 return errs.Wrap(err, "Could not create shortcut interface") 52 } 53 54 if _, err = oleutil.PutProperty(shortcut, "TargetPath", fpath); err != nil { 55 return errs.Wrap(err, "Could not set TargetPath on lnk") 56 } 57 if _, err = oleutil.CallMethod(shortcut, "Save"); err != nil { 58 return errs.Wrap(err, "Could not save lnk") 59 } 60 return nil 61 } 62 63 func newShortcut(target string) (*ole.IDispatch, error) { 64 // ALWAYS errors with "Incorrect function", which can apparently be safely ignored.. 65 _ = ole.CoInitialize(0) 66 67 oleShellObject, err := oleutil.CreateObject("WScript.Shell") 68 if err != nil { 69 return nil, errs.Wrap(err, "Could not create shell object") 70 } 71 72 defer oleShellObject.Release() 73 wshell, err := oleShellObject.QueryInterface(ole.IID_IDispatch) 74 if err != nil { 75 return nil, errs.Wrap(err, "Could not interface with shell object") 76 } 77 78 defer wshell.Release() 79 cs, err := oleutil.CallMethod(wshell, "CreateShortcut", target) 80 if err != nil { 81 return nil, errs.Wrap(err, "Could not call CreateShortcut on shell object") 82 } 83 return cs.ToIDispatch(), nil 84 }