【问题标题】:How to create a Windows Shortcut in Powershell to a file with special characters in filename?如何在 Powershell 中为文件名中包含特殊字符的文件创建 Windows 快捷方式?
【发布时间】:2022-01-04 22:08:04
【问题描述】:

我通常不会遇到编程问题,因为我可以轻松找到大多数问题的答案。但我在这个问题上束手无策。

在 Powershell 中创建 Windows 快捷方式的众所周知的方法如下:

$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("F:\path\to\shortcut.lnk")
$Shortcut.TargetPath = "F:\some\other\path\to\targetfile.txt"
$Shortcut.Save()

然而,这种方法有一个缺点,我开始被越来越多的困扰:当文件名有特殊字符时它不起作用,例如笑脸????在文件名中:

"targetfile ????.txt"

我研究了这个问题并发现here WshShortcut 对象和 WScript 不能接受文件名中的 unicode。它似乎只适用于一组简单的字符。当然,当您在 Windows 中右键单击该文件并选择“创建快捷方式”时,Windows 在创建带有特殊字符的快捷方式时没有问题。

有人用 C# an alternative way to create shortcuts using Shell32 编写脚本,但我不知道它是否可以在 Powershell 中完成。而且它看起来像是一种旧方法,可能不适用于较新的 Windows 版本。

有人可以帮我解决这个问题吗?如何在 Powershell 中为文件名中包含特殊字符的文件创建 Windows 快捷方式?

【问题讨论】:

  • 为什么你的文件名中有表情符号?
  • @fit 您会遇到与其他 Unicode 代码点相同的问题。如果您对文件名中的表情符号感到不快,请假装您正在处理用户的 Documents 文件夹中的文件,其中用户名使用复杂的脚本。
  • @Fitzgery 我从 Youtube 和其他平台下载东西,然后在下载后整理它们,创建文件和东西的快捷方式。有时,Youtube 上的标题中包含表情符号,这意味着文件名最终会包含表情符号。我能做些什么?就是这样。我需要一种方法来克服 Powershell 的限制。

标签: windows powershell shortcut wscript.shell


【解决方案1】:

如有疑问,请使用 C#:

$ShellLinkCSharp = @'
namespace Shox
{
    using System;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    using System.Text;

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("000214F9-0000-0000-C000-000000000046")]
    [CoClass(typeof(CShellLinkW))]
    interface IShellLinkW
    {
        void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, uint fFlags);
        IntPtr GetIDList();
        void SetIDList(IntPtr pidl);
        void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName);
        void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
        void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
        void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
        void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
        void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
        ushort GetHotKey();
        void SetHotKey(ushort wHotKey);
        uint GetShowCmd();
        void SetShowCmd(uint iShowCmd);
        void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
        void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
        void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, [Optional] uint dwReserved);
        void Resolve(IntPtr hwnd, uint fFlags);
        void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
    }

    [ComImport]
    [Guid("00021401-0000-0000-C000-000000000046")]
    [ClassInterface(ClassInterfaceType.None)]
    class CShellLinkW { }

    public static class ShellLink
    {
        public static void CreateShortcut(
            string lnkPath,
            string targetPath,
            string description,
            string workingDirectory)
        {
            if (string.IsNullOrWhiteSpace(lnkPath))
                throw new ArgumentNullException("lnkPath");

            if (string.IsNullOrWhiteSpace(targetPath))
                throw new ArgumentNullException("targetPath");

            IShellLinkW link = new IShellLinkW();

            link.SetPath(targetPath);

            if (!string.IsNullOrWhiteSpace(description))
            {
                link.SetDescription(description);
            }

            if (!string.IsNullOrWhiteSpace(workingDirectory))
            {
                link.SetWorkingDirectory(workingDirectory);
            }

            IPersistFile file = (IPersistFile)link;
            file.Save(lnkPath, true);

            Marshal.FinalReleaseComObject(link);
        }
    }
}
'@

# Check if Shox.ShellLink class already exists; if not, import it:
if (-not ([System.Management.Automation.PSTypeName]'Shox.ShellLink').Type)
{
    Add-Type -TypeDefinition $ShellLinkCSharp
}


[Shox.ShellLink]::CreateShortcut(
    'F:\path\to\shortcut1.lnk',
    'F:\some\other\path\to\targetfile1 ?.txt',
    '?? my description ??',
    'F:\some\another\path\my_working_directory')

[Shox.ShellLink]::CreateShortcut(
    'F:\path\to\shortcut2.lnk',
    'F:\some\other\path\to\targetfile2 ?.txt',
    $null,  # No description
    $null)  # No working directory

【讨论】:

  • 哇乔治!!非常感谢您的慷慨回答。你是刚刚写的还是很久以前就准备好了?它确实有效吗?这准备好在 Powershell 中粘贴和执行了吗? $ShellLinkCSharp = @'...'@ 表示中间的位是 C#?剩下的就是 Powershell 代码?
  • 我在 Windows 10 中尝试过。我得到的不是“targetfile1 ?.lnk”,而是“targetfile1 ðŸ~.lnk”。特殊字符/表情符号未正确呈现。倒数第二行中的“..,'??'”是什么意思。 CreateShortcut 的第三个参数是什么?
  • 我从.NET WPF source 复制了它。是的,它有效,我已经在 Windows 8.1 和 10 上进行了测试。
  • 我猜你已经将它粘贴到 PowerShell 控制台中,默认情况下不支持 Unicode。尝试另存为文件并以这种方式执行。或使用 PowerShell ISE。第三个参数只是一个快捷方式描述(打开快捷方式属性时可以看到)。
  • @shox 是的,@' ... '@ 包含 C# 代码,Add-Type cmdlet 将其导入 PowerShell。见PowerShell Here-strings 和Add-Type。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-13
  • 2020-11-18
  • 1970-01-01
相关资源
最近更新 更多