【问题标题】:Create a Windows Shortcut (.lnk) in Go在 Go 中创建 Windows 快捷方式 (.lnk)
【发布时间】:2015-09-07 11:59:40
【问题描述】:

我想为桌面和 Golang 中的开始菜单创建一个 Windows 快捷方式 (.lnk)。

我实际上是通过gowin 模块获得了桌面和开始菜单文件夹,我想为这些位置创建一个快捷方式。

我搜索了但我没有找到任何 golang 项目。我应该创建它吗?还有其他漂亮的方法吗?

【问题讨论】:

  • 我不知道windows,也无权访问它进行测试。但是,AFAIU lnk 相当于 *nix 上的符号链接。所以我会尝试os.Symlink,因为我希望它可以跨平台工作,除非另有说明。 (我可能将符号链接和快捷方式混为一谈).

标签: windows go desktop-shortcut


【解决方案1】:

使用https://github.com/go-ole/go-ole

func makeLink(src, dst string) error {
    ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_SPEED_OVER_MEMORY)
    oleShellObject, err := oleutil.CreateObject("WScript.Shell")
    if err != nil {
        return err
    }
    defer oleShellObject.Release()
    wshell, err := oleShellObject.QueryInterface(ole.IID_IDispatch)
    if err != nil {
        return err
    }
    defer wshell.Release()
    cs, err := oleutil.CallMethod(wshell, "CreateShortcut", dst)
    if err != nil {
        return err
    }
    idispatch := cs.ToIDispatch()
    oleutil.PutProperty(idispatch, "TargetPath", src)
    oleutil.CallMethod(idispatch, "Save")
    return nil
}

【讨论】:

  • 一年后,我终于有了真正的解决方案!谢谢:D
  • 在生产环境中运行了几个月后,我注意到一些罕见的失败,并显示消息“CoInitialize 尚未被调用。”。要解决此问题,您需要确保调用 runtime.LockOSThread(),或使用github.com/go-ole/go-ole/issues/124 中所述的 comshim 库
  • 在此处添加 dstsrc 应为 string 类型。这会根据需要在后台自动转换为适当的 UTF16 编码。
【解决方案2】:

不,在 golang 中没有任何漂亮的方法可以创建 .lnk 文件。

主要原因是 .lnk 文件是特定于 Windows 的。

在 Windows 中,即使是原生程序也需要使用 OLE(对象链接和嵌入)和 COM(组件对象模型)来创建快捷方式文件,如 this answer 中所述。

在我看来,在 golang 中解决此问题的一种方法是使用gowin,并尝试与 OLE COM 通信。

编写一个本机 Windows 组件来执行创建 .lnk 文件的实际工作,并通过您的 go 程序生成它的进程。

【讨论】:

  • 谢谢!如果成功,我会尝试并(显然)将其发布到 Github 上! c:
  • @AlexisPaques 请告诉我,当你这样做的时候。
  • 首先我将只使用外部解决方案,然后,也许会尝试在 golang 中这样做
【解决方案3】:

来自this subject的外部程序解决方案:

来自NirSoft的快捷方式可执行文件

shortcut "f:\winnt\system32\calc.exe" "~$folder.desktop$" "Windows Calculator" 
shortcut "f:\winnt\system32\calc.exe" "~$folder.programs$\Calculators" "Windows Calculator" 
shortcut "f:\Program Files\KaZaA\Kazaa.exe" "c:\temp\MyShortcuts" "Kazaa" 
shortcut "f:\Program Files" "c:\temp\MyShortcuts" "Program Files Folder" "" "f:\winnt\system32\shell32.dll" 45 
shortcut "f:\Program Files" "c:\temp\MyShortcuts" "Program Files Folder" "" "" "" "max"

来自Optimumx的快捷方式可执行文件

Shortcut.exe /f:"%USERPROFILE%\Desktop\sc.lnk" /a:c  /t:%USERPROFILE%\Desktop\scrum.pdf

.vbs

Set oWS = WScript.CreateObject("WScript.Shell")
sLinkFile = "C:\MyShortcut.LNK"
Set oLink = oWS.CreateShortcut(sLinkFile)
    oLink.TargetPath = "C:\Program Files\MyApp\MyProgram.EXE"
 '  oLink.Arguments = ""
 '  oLink.Description = "MyProgram"   
 '  oLink.HotKey = "ALT+CTRL+F"
 '  oLink.IconLocation = "C:\Program Files\MyApp\MyProgram.EXE, 2"
 '  oLink.WindowStyle = "1"   
 '  oLink.WorkingDirectory = "C:\Program Files\MyApp"
oLink.Save

Powershell 脚本

set TARGET='D:\Temp'
set SHORTCUT='C:\Temp\test.lnk'
set PWS=powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile

%PWS% -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut(%SHORTCUT%); $S.TargetPath = %TARGET%; $S.Save()"

【讨论】:

    【解决方案4】:

    AWFUL Working 使用 VBS 的 golang 解决方案;

    package main
    
    import(
        "bytes"
        "fmt"
        "io/ioutil"
        "os"
        "os/exec"
    )
    
    func createShortcut(linkName string, target string, arguments string, directory string, description string, destination string) {
        var scriptTxt bytes.Buffer
        scriptTxt.WriteString("option explicit\n\n")
        scriptTxt.WriteString("sub CreateShortCut()\n")
        scriptTxt.WriteString("dim objShell, strDesktopPath, objLink\n")
        scriptTxt.WriteString("set objShell = CreateObject(\"WScript.Shell\")\n")
        scriptTxt.WriteString("strDesktopPath = objShell.SpecialFolders(\"")
        scriptTxt.WriteString(destination)
        scriptTxt.WriteString("\")\n")
        scriptTxt.WriteString("set objLink = objShell.CreateShortcut(strDesktopPath & \"\\")
        scriptTxt.WriteString(linkName)
        scriptTxt.WriteString(".lnk\")\n")
        scriptTxt.WriteString("objLink.Arguments = \"")
        scriptTxt.WriteString(arguments)
        scriptTxt.WriteString("\"\n")
        scriptTxt.WriteString("objLink.Description = \"")
        scriptTxt.WriteString(description)
        scriptTxt.WriteString("\"\n")
        scriptTxt.WriteString("objLink.TargetPath = \"")
        scriptTxt.WriteString(target)
        scriptTxt.WriteString("\"\n")
        scriptTxt.WriteString("objLink.WindowStyle = 1\n")
        scriptTxt.WriteString("objLink.WorkingDirectory = \"")
        scriptTxt.WriteString(directory)
        scriptTxt.WriteString("\"\n")
        scriptTxt.WriteString("objLink.Save\nend sub\n\n")
        scriptTxt.WriteString("call CreateShortCut()")
        fmt.Print(scriptTxt.String())
    
        filename := fmt.Sprintf("lnkTo%s.vbs", destination)
        ioutil.WriteFile(filename, scriptTxt.Bytes(), 0777)
        cmd := exec.Command("wscript", filename)
        err := cmd.Run()
        if err != nil {
            fmt.Println(err)
        }
        cmd.Wait()
        os.Remove(filename)
        return
    }
    

    【讨论】:

    • 您可以使用反引号 Go 字符串文字来避免需要转义引号 (\")。您还可以使用它们来制作可读的多行字符串文字以减少 WriteString 调用的数量你做的。你也可以在单个字符串文字中使用text/template之类的模板,以使其更具可读性(并且仍然可以轻松地将变量的内容放入脚本中)。
    • 表演怎么样?我将连接(+=)与一个简单的字符串一起使用,它是可读的,然后我读了这个:Stackoverflow,它的缓冲区速度快了 4000 倍(复制速度比缓冲区快 3 倍,但我认为可读性较差)
    • 看了文本/模板后,正是我需要的! :o
    【解决方案5】:

    如果出于某种原因您不想使用外部 go 包,这里有一个替代方案。 正如Alexis Paques 所提到的,您可以使用Powershell 在Windows 下创建快捷方式。优点是,它几乎已在所有 Windows 环境中可用。这是在 shell:startup 文件夹中创建快捷方式的实现,它将在启动时为当前用户自动启动链接程序:

    package main
    
    import (
        "bytes"
        "log"
        "os/exec"
        "strings"
    )
    
    type PowerShell struct {
        powerShell string
    }
    
    var WIN_CREATE_SHORTCUT = `$WshShell = New-Object -comObject WScript.Shell
    $Shortcut = $WshShell.CreateShortcut("$HOME\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\MyAPP.lnk")
    $Shortcut.TargetPath = "PLACEHOLDER"
    $Shortcut.Save()`
    
    // New create new session
    func New() *PowerShell {
        ps, _ := exec.LookPath("powershell.exe")
        return &PowerShell{
            powerShell: ps,
        }
    }
    
    func (p *PowerShell) execute(args ...string) (stdOut string, stdErr string, err error) {
        args = append([]string{"-NoProfile", "-NonInteractive"}, args...)
        cmd := exec.Command(p.powerShell, args...)
    
        var stdout bytes.Buffer
        var stderr bytes.Buffer
        cmd.Stdout = &stdout
        cmd.Stderr = &stderr
    
        err = cmd.Run()
        stdOut, stdErr = stdout.String(), stderr.String()
        return
    }
    
    // enableAutostartWin creates a shortcut to MyAPP in the shell:startup folder
    func enableAutostartWin() {
        ps := New()
        exec_path := "C:\\MyAPP.exe"
        WIN_CREATE_SHORTCUT = strings.Replace(WIN_CREATE_SHORTCUT, "PLACEHOLDER", exec_path, 1)
        stdOut, stdErr, err := ps.execute(WIN_CREATE_SHORTCUT)
        log.Printf("CreateShortcut:\nStdOut : '%s'\nStdErr: '%s'\nErr: %s",
            strings.TrimSpace(stdOut), stdErr, err)
    }
    

    此答案基于this SO answer 和此gist

    【讨论】:

      猜你喜欢
      • 2012-05-07
      • 2013-08-04
      • 1970-01-01
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 2010-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多