在Python中读取.lnk文件的目标?

49

我正在尝试从Python读取快捷方式(.lnk)文件的目标文件/目录。有没有一种简单的方法可以做到?规范对我来说太复杂了。

我的最终目标是在Windows XP和Vista上找到“(我的)视频”文件夹。在XP上,默认情况下,它位于%HOMEPATH%\My Documents\My Videos,在Vista上则位于%HOMEPATH%\Videos。然而,用户可以重新定位此文件夹。在这种情况下,%HOMEPATH%\Videos文件夹将不存在,并被指向新的“我的视频”文件夹的%HOMEPATH%\Videos.lnk所替代。我想要它的绝对位置。

11个回答

58

使用Python(通过WSH)创建快捷方式

import sys
import win32com.client 

shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut("t:\\test.lnk")
shortcut.Targetpath = "t:\\ftemp"
shortcut.save()


使用Python(通过WSH)读取快捷方式的目标

import sys
import win32com.client 

shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut("t:\\test.lnk")
print(shortcut.Targetpath)

2
太好了,我找到了这个!我差点放弃并写一个vbs脚本然后从我的python脚本中调用它。这个方法好多了。+1 - Matthew Scouten
2
对于目标参数,shortcut.Arguments。 - seongjoo
2
很奇怪。每次调用print(shortcut.Targetpath)时,我总是得到一个空字符串。我正在对一个文件夹进行操作。这种方法也适用于文件夹吗? - Zheng Liu
对我来说也是一样的。如果我将Windows创建的文件内容与此方法创建的文件进行比较,那么Windows版本中至少有更多的信息。此外,Targetpath在Windows版本中也会产生一些输出。 - maggie
如何向 shortcut.Targetpath 添加参数。 - ajinzrathod
@MatthewScouten 以防万一,这里有vbs脚本可以创建/更新快捷方式:https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/ToolAdaptors/vbs 或 https://github.com/andry81/contools/tree/trunk/Scripts/Tools/ToolAdaptors/vbs: make_shortcut.vbs, update_shortcut.vbs. - Andry

24

我知道这是一个较旧的帖子,但我感觉关于在原始问题中注明的使用链接规范的方法并没有太多信息。

我的快捷方式目标实现无法使用win32com模块,在经过大量搜索后,我决定自己开发。在我的限制条件下,似乎没有其他东西能够实现我所需的功能。希望这能帮助其他处于同样困境的人们。

它使用了微软提供的二进制结构,用于MS-SHLLINK

import struct

path = 'myfile.txt.lnk'    
target = ''

with open(path, 'rb') as stream:
    content = stream.read()
    # skip first 20 bytes (HeaderSize and LinkCLSID)
    # read the LinkFlags structure (4 bytes)
    lflags = struct.unpack('I', content[0x14:0x18])[0]
    position = 0x18
    # if the HasLinkTargetIDList bit is set then skip the stored IDList 
    # structure and header
    if (lflags & 0x01) == 1:
        position = struct.unpack('H', content[0x4C:0x4E])[0] + 0x4E
    last_pos = position
    position += 0x04
    # get how long the file information is (LinkInfoSize)
    length = struct.unpack('I', content[last_pos:position])[0]
    # skip 12 bytes (LinkInfoHeaderSize, LinkInfoFlags, and VolumeIDOffset)
    position += 0x0C
    # go to the LocalBasePath position
    lbpos = struct.unpack('I', content[position:position+0x04])[0]
    position = last_pos + lbpos
    # read the string at the given position of the determined length
    size= (length + last_pos) - position - 0x02
    temp = struct.unpack('c' * size, content[position:position+size])
    target = ''.join([chr(ord(a)) for a in temp])

2
希望我们能将这个精彩的代码整合成一个完整的对象,然后加入到标准库中。 - Michael Scott Asato Cuthbert
4
已修改以支持Unicode和本地编码 https://gist.github.com/Winand/997ed38269e899eb561991a0c663fa49 - Winand
1
我一直在寻找这个答案,我差点自己动手做了! - GLJ

12

或者,您可以尝试使用SHGetFolderPath()。以下代码可能有效,但我现在不在Windows机器上,因此无法测试。

import ctypes

shell32 = ctypes.windll.shell32

# allocate MAX_PATH bytes in buffer
video_folder_path = ctypes.create_string_buffer(260)

# 0xE is CSIDL_MYVIDEO
# 0 is SHGFP_TYPE_CURRENT
# If you want a Unicode path, use SHGetFolderPathW instead
if shell32.SHGetFolderPathA(None, 0xE, None, 0, video_folder_path) >= 0:
    # success, video_folder_path now contains the correct path
else:
    # error

这似乎应该可以工作,但ctypes表示它无法调用该函数(以及其他几个已记录的shell32函数)。我在Windows XP SP3上运行Python 2.6。 - Ellen Teapot
1
可以工作,但有两个问题。 1)实际上该函数被称为SHGetFolderPathA(注意末尾的A) 2)“我的视频”文件夹的ID是0xE。 0x37是“公共视频”文件夹。 我已经让它工作了。谢谢! - Etienne Perot
你发现得很好 - 我已经修正了答案。 - Adam Rosenfield

11

基本上直接调用Windows API。这是在谷歌搜索后找到的一个很好的例子:

import os, sys
import pythoncom
from win32com.shell import shell, shellcon

shortcut = pythoncom.CoCreateInstance (
  shell.CLSID_ShellLink,
  None,
  pythoncom.CLSCTX_INPROC_SERVER,
  shell.IID_IShellLink
)
desktop_path = shell.SHGetFolderPath (0, shellcon.CSIDL_DESKTOP, 0, 0)
shortcut_path = os.path.join (desktop_path, "python.lnk")
persist_file = shortcut.QueryInterface (pythoncom.IID_IPersistFile)
persist_file.Load (shortcut_path)

shortcut.SetDescription ("Updated Python %s" % sys.version)
mydocs_path = shell.SHGetFolderPath (0, shellcon.CSIDL_PERSONAL, 0, 0)
shortcut.SetWorkingDirectory (mydocs_path)

persist_file.Save (shortcut_path, 0)

这段内容来自http://timgolden.me.uk/python/win32_how_do_i/create-a-shortcut.html

你可以搜索 "python ishelllink" 查询其他示例。

此外,API 参考文档也是有帮助的:http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx


2
只想提一下,这种 pythoncom 方法在 Python2 上使用 ASCII api,如果 lnk 路径指向包含 Unicode 字符的位置,可能会导致问题。WSH 方法使用 WCHAR api,不会出现这个问题。 - jichi

8
我也意识到这个问题很老,但我发现答案对我的情况最相关。
与Jared的答案类似,我也无法使用win32com模块。因此,Jared从MS-SHLLINK获取二进制结构使我部分完成了工作。我需要在Windows和Linux上读取快捷方式,在Windows上,快捷方式是在samba共享上创建的。Jared的实现对我来说并不完全有效,我认为这只是因为我遇到了一些不同的快捷方式格式变化。但是,它给了我需要的启动(感谢Jared)。
因此,这里有一个名为MSShortcut的类,它扩展了Jared的方法。但是,该实现仅限于Python3.4及以上版本,因为使用了Python3.4中添加的一些pathlib功能。
#!/usr/bin/python3

# Link Format from MS: https://msdn.microsoft.com/en-us/library/dd871305.aspx
# Need to be able to read shortcut target from .lnk file on linux or windows.
# Original inspiration from: https://dev59.com/9XRC5IYBdhLWcg3wJNif

from pathlib import Path, PureWindowsPath
import struct, sys, warnings

if sys.hexversion < 0x03040000:
    warnings.warn("'{}' module requires python3.4 version or above".format(__file__), ImportWarning)


# doc says class id = 
#    00021401-0000-0000-C000-000000000046
# requiredCLSID = b'\x00\x02\x14\x01\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46'
# Actually Getting: 
requiredCLSID   = b'\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46'  # puzzling

class ShortCutError(RuntimeError):
    pass


class MSShortcut():
    """
    interface to Microsoft Shortcut Objects.  Purpose:
    - I need to be able to get the target from a samba shared on a linux machine
    - Also need to get access from a Windows machine.
    - Need to support several forms of the shortcut, as they seem be created differently depending on the 
      creating machine.
    - Included some 'flag' types in external interface to help test differences in shortcut types

    Args:
        scPath (str): path to shortcut

    Limitations:
        - There are some omitted object properties in the implementation. 
          Only implemented / tested enough to recover the shortcut target information. Recognized omissions:
          - LinkTargetIDList
          - VolumeId structure (if captured later, should be a separate class object to hold info)
          - Only captured environment block from extra data 
        - I don't know how or when some of the shortcut information is used, only captured what I recognized, 
          so there may be bugs related to use of the information
        - NO shortcut update support (though might be nice)
        - Implementation requires python 3.4 or greater
        - Tested only with Unicode data on a 64bit little endian machine, did not consider potential endian issues

    Not Debugged:
        - localBasePath - didn't check if parsed correctly or not.
        - commonPathSuffix
        - commonNetworkRelativeLink

    """

    def __init__(self, scPath):
        """
        Parse and keep shortcut properties on creation
        """
        self.scPath = Path(scPath)

        self._clsid = None
        self._lnkFlags = None
        self._lnkInfoFlags = None
        self._localBasePath = None
        self._commonPathSuffix = None
        self._commonNetworkRelativeLink = None
        self._name = None
        self._relativePath = None
        self._workingDir = None
        self._commandLineArgs = None
        self._iconLocation = None
        self._envTarget = None

        self._ParseLnkFile(self.scPath)  


    @property
    def clsid(self): 
        return self._clsid

    @property
    def lnkFlags(self): 
        return self._lnkFlags

    @property
    def lnkInfoFlags(self): 
        return self._lnkInfoFlags

    @property
    def localBasePath(self): 
        return self._localBasePath

    @property
    def commonPathSuffix(self): 
        return self._commonPathSuffix

    @property
    def commonNetworkRelativeLink(self): 
        return self._commonNetworkRelativeLink

    @property
    def name(self): 
        return self._name    

    @property
    def relativePath(self): 
        return self._relativePath    

    @property
    def workingDir(self): 
        return self._workingDir    

    @property
    def commandLineArgs(self): 
        return self._commandLineArgs    

    @property
    def iconLocation(self): 
        return self._iconLocation    

    @property
    def envTarget(self): 
        return self._envTarget    


    @property
    def targetPath(self):
        """
        Args:
            woAnchor (bool): remove the anchor (\\server\path or drive:) from returned path.

        Returns:
            a libpath PureWindowsPath object for combined workingDir/relative path
            or the envTarget

        Raises:
            ShortCutError when no target path found in Shortcut
        """
        target = None
        if self.workingDir:
            target = PureWindowsPath(self.workingDir)
            if self.relativePath:
                target = target / PureWindowsPath(self.relativePath)
            else: target = None

        if not target and self.envTarget:
            target = PureWindowsPath(self.envTarget)

        if not target:
            raise ShortCutError("Unable to retrieve target path from MS Shortcut: shortcut = {}"
                               .format(str(self.scPath)))  

        return target    

    @property
    def targetPathWOAnchor(self):
        tp = self.targetPath
        return tp.relative_to(tp.anchor)




    def _ParseLnkFile(self, lnkPath):        
        with lnkPath.open('rb') as f:
            content = f.read()

            # verify size  (4 bytes)
            hdrSize = struct.unpack('I', content[0x00:0x04])[0]
            if hdrSize != 0x4C:
                raise ShortCutError("MS Shortcut HeaderSize = {}, but required to be = {}: shortcut = {}"
                                   .format(hdrSize, 0x4C, str(lnkPath)))

            # verify LinkCLSID id (16 bytes)            
            self._clsid = bytes(struct.unpack('B'*16, content[0x04:0x14]))
            if self._clsid != requiredCLSID:
                raise ShortCutError("MS Shortcut ClassID = {}, but required to be = {}: shortcut = {}"
                                   .format(self._clsid, requiredCLSID, str(lnkPath)))        

            # read the LinkFlags structure (4 bytes)
            self._lnkFlags = struct.unpack('I', content[0x14:0x18])[0]
            #logger.debug("lnkFlags=0x%0.8x" % self._lnkFlags)
            position = 0x4C

            # if HasLinkTargetIDList bit, then position to skip the stored IDList structure and header
            if (self._lnkFlags & 0x01):
                idListSize = struct.unpack('H', content[position:position+0x02])[0]
                position += idListSize + 2

            # if HasLinkInfo, then process the linkinfo structure  
            if (self._lnkFlags & 0x02):
                (linkInfoSize, linkInfoHdrSize, self._linkInfoFlags, 
                 volIdOffset, localBasePathOffset, 
                 cmnNetRelativeLinkOffset, cmnPathSuffixOffset) = struct.unpack('IIIIIII', content[position:position+28])

                # check for optional offsets
                localBasePathOffsetUnicode = None
                cmnPathSuffixOffsetUnicode = None
                if linkInfoHdrSize >= 0x24:
                    (localBasePathOffsetUnicode, cmnPathSuffixOffsetUnicode) = struct.unpack('II', content[position+28:position+36])

                #logger.debug("0x%0.8X" % linkInfoSize)
                #logger.debug("0x%0.8X" % linkInfoHdrSize)
                #logger.debug("0x%0.8X" % self._linkInfoFlags)
                #logger.debug("0x%0.8X" % volIdOffset)
                #logger.debug("0x%0.8X" % localBasePathOffset)
                #logger.debug("0x%0.8X" % cmnNetRelativeLinkOffset)
                #logger.debug("0x%0.8X" % cmnPathSuffixOffset)
                #logger.debug("0x%0.8X" % localBasePathOffsetUnicode)
                #logger.debug("0x%0.8X" % cmnPathSuffixOffsetUnicode)

                # if info has a localBasePath
                if (self._linkInfoFlags & 0x01):
                    bpPosition = position + localBasePathOffset

                    # not debugged - don't know if this works or not
                    self._localBasePath = UnpackZ('z', content[bpPosition:])[0].decode('ascii')
                    #logger.debug("localBasePath: {}".format(self._localBasePath))

                    if localBasePathOffsetUnicode:
                        bpPosition = position + localBasePathOffsetUnicode
                        self._localBasePath = UnpackUnicodeZ('z', content[bpPosition:])[0]
                        self._localBasePath = self._localBasePath.decode('utf-16')               
                        #logger.debug("localBasePathUnicode: {}".format(self._localBasePath))

                # get common Path Suffix
                cmnSuffixPosition = position + cmnPathSuffixOffset               
                self._commonPathSuffix = UnpackZ('z', content[cmnSuffixPosition:])[0].decode('ascii')
                #logger.debug("commonPathSuffix: {}".format(self._commonPathSuffix))
                if cmnPathSuffixOffsetUnicode:
                    cmnSuffixPosition = position + cmnPathSuffixOffsetUnicode
                    self._commonPathSuffix = UnpackUnicodeZ('z', content[cmnSuffixPosition:])[0]
                    self._commonPathSuffix = self._commonPathSuffix.decode('utf-16')               
                    #logger.debug("commonPathSuffix: {}".format(self._commonPathSuffix))


                # check for CommonNetworkRelativeLink
                if (self._linkInfoFlags & 0x02):
                    relPosition = position + cmnNetRelativeLinkOffset
                    self._commonNetworkRelativeLink = CommonNetworkRelativeLink(content, relPosition)

                position += linkInfoSize 

            # If HasName
            if (self._lnkFlags & 0x04):
                (position, self._name) = self.readStringObj(content, position)
                #logger.debug("name: {}".format(self._name))     

            # get relative path string
            if (self._lnkFlags & 0x08):
                (position, self._relativePath) = self.readStringObj(content, position)
                #logger.debug("relPath='{}'".format(self._relativePath))

            # get working dir string
            if (self._lnkFlags & 0x10):
                (position, self._workingDir) = self.readStringObj(content, position)
                #logger.debug("workingDir='{}'".format(self._workingDir))

            # get command line arguments
            if (self._lnkFlags & 0x20):
                (position, self._commandLineArgs) = self.readStringObj(content, position)
                #logger.debug("commandLineArgs='{}'".format(self._commandLineArgs))

            # get icon location
            if (self._lnkFlags & 0x40):
                (position, self._iconLocation) = self.readStringObj(content, position)
                #logger.debug("iconLocation='{}'".format(self._iconLocation))

            # look for environment properties             
            if (self._lnkFlags & 0x200):
                while True:
                    size = struct.unpack('I', content[position:position+4])[0]
                    #logger.debug("blksize=%d" % size)
                    if size==0: break

                    signature = struct.unpack('I', content[position+4:position+8])[0]
                    #logger.debug("signature=0x%0.8x" % signature)     

                    # EnvironmentVariableDataBlock          
                    if signature == 0xA0000001:  

                        if (self._lnkFlags & 0x80): # unicode
                            self._envTarget = UnpackUnicodeZ('z', content[position+268:])[0]
                            self._envTarget = self._envTarget.decode('utf-16')
                        else:
                            self._envTarget = UnpackZ('z', content[position+8:])[0].decode('ascii')

                        #logger.debug("envTarget='{}'".format(self._envTarget))

                    position += size


    def readStringObj(self, scContent, position):
        """ 
        returns:
            tuple: (newPosition, string)
        """
        strg = ''
        size = struct.unpack('H', scContent[position:position+2])[0]
        #logger.debug("workingDirSize={}".format(size))
        if (self._lnkFlags & 0x80): # unicode
            size *= 2
            strg = struct.unpack(str(size)+'s', scContent[position+2:position+2+size])[0]
            strg = strg.decode('utf-16')               
        else:
            strg = struct.unpack(str(size)+'s', scContent[position+2:position+2+size])[0].decode('ascii')
        #logger.debug("strg='{}'".format(strg))
        position += size + 2 # 2 bytes to account for CountCharacters field

        return (position, strg)




class CommonNetworkRelativeLink():

    def __init__(self, scContent, linkContentPos):

        self._networkProviderType = None
        self._deviceName = None
        self._netName = None

        (linkSize, flags, netNameOffset, 
         devNameOffset, self._networkProviderType) = struct.unpack('IIIII', scContent[linkContentPos:linkContentPos+20])

        #logger.debug("netnameOffset = {}".format(netNameOffset))
        if netNameOffset > 0x014:
            (netNameOffsetUnicode, devNameOffsetUnicode) = struct.unpack('II', scContent[linkContentPos+20:linkContentPos+28])
            #logger.debug("netnameOffsetUnicode = {}".format(netNameOffsetUnicode))
            self._netName = UnpackUnicodeZ('z', scContent[linkContentPos+netNameOffsetUnicode:])[0]
            self._netName = self._netName.decode('utf-16')  
            self._deviceName = UnpackUnicodeZ('z', scContent[linkContentPos+devNameOffsetUnicode:])[0]
            self._deviceName = self._deviceName.decode('utf-16')               
        else:
            self._netName = UnpackZ('z', scContent[linkContentPos+netNameOffset:])[0].decode('ascii')
            self._deviceName = UnpackZ('z', scContent[linkContentPos+devNameOffset:])[0].decode('ascii')

    @property    
    def deviceName(self):
        return self._deviceName

    @property    
    def netName(self):
        return self._netName

    @property    
    def networkProviderType(self):
        return self._networkProviderType



def UnpackZ (fmt, buf) :
    """
    Unpack Null Terminated String
    """
    #logger.debug(bytes(buf))

    while True :
        pos = fmt.find ('z')
        if pos < 0 :
            break
        z_start = struct.calcsize (fmt[:pos])
        z_len = buf[z_start:].find(b'\0')
        #logger.debug(z_len)
        fmt = '%s%dsx%s' % (fmt[:pos], z_len, fmt[pos+1:])
        #logger.debug("fmt='{}', len={}".format(fmt, z_len))
    fmtlen = struct.calcsize(fmt)
    return struct.unpack (fmt, buf[0:fmtlen])


def UnpackUnicodeZ (fmt, buf) :
    """
    Unpack Null Terminated String
    """
    #logger.debug(bytes(buf))
    while True :
        pos = fmt.find ('z')
        if pos < 0 :
            break
        z_start = struct.calcsize (fmt[:pos])
        # look for null bytes by pairs
        z_len = 0
        for i in range(z_start,len(buf),2):
            if buf[i:i+2] == b'\0\0':
                z_len = i-z_start
                break

        fmt = '%s%dsxx%s' % (fmt[:pos], z_len, fmt[pos+1:])
       # logger.debug("fmt='{}', len={}".format(fmt, z_len))
    fmtlen = struct.calcsize(fmt)
    return struct.unpack (fmt, buf[0:fmtlen])

我希望这也能帮到其他人。 谢谢


6

我并不是特别喜欢现有的答案,因为我不想不停地导入更多的库,而且在我的测试机上“shell”选项也不太稳定。我选择读取“.lnk”文件,然后使用正则表达式来提取路径。对于我的目的,我正在寻找最近打开的pdf文件并读取这些文件的内容:

# Example file path to the shortcut
shortcut = "shortcutFileName.lnk"

# Open the lnk file using the ISO-8859-1 encoder to avoid errors for special characters
lnkFile = open(shortcut, 'r', encoding = "ISO-8859-1")

# Use a regular expression to parse out the pdf file on C:\
filePath = re.findall("C:.*?pdf", lnkFile.read(), flags=re.DOTALL)

# Close File
lnkFile.close()

# Read the pdf at the lnk Target
pdfFile = open(tmpFilePath[0], 'rb')

评论:

很显然,这适用于 pdf,但需要相应地指定其他文件扩展名。


我喜欢这种方法,它不需要使用Windows shell或WSL,也不需要创建类等。谢谢。 - Diego-MX
1
不错的方法!但是有一些快捷方式可以让你得到两个匹配项,而第一个匹配项几乎无法使用。因此,我将您的表达式调整为更复杂的表达式 [a-zA-Z]:[\\\\/][ \\S]*?\.pdf,以避免与 0x00 匹配。 - AntonK
经过一些评估后,我倾向于认为解析的方法不够通用,不能在所有情况下使用。例如,当lnk文件中仅存在相对路径时(例如..\\..\\..\\..\\..\\..\\Program Files\\ImageGlass\\ImageGlass.exe),就会出现错误,该路径是以宽字符形式存储的(可能是由msi安装程序生成的)。因此,我将转而使用LnkParse3或类似的解决方案。 - AntonK

1

使用PowerShell从.lnk文件中读取目标路径,这是Python中更稳定的解决方案。

  • 只使用标准库避免引入额外的依赖项,如win32com
  • 这种方法适用于无法使用Jared答案的.lnk文件,更多详情
  • 我们避免直接读取文件,因为它感觉像黑客,并且有时会失败
import subprocess


def get_target(link_path) -> (str, str):
    """
    Get the target & args of a Windows shortcut (.lnk)
    :param link_path: The Path or string-path to the shortcut, e.g. "C:\\Users\\Public\\Desktop\\My Shortcut.lnk"
    :return: A tuple of the target and arguments, e.g. ("C:\\Program Files\\My Program.exe", "--my-arg")
    """
    # get_target implementation by hannes, https://gist.github.com/Winand/997ed38269e899eb561991a0c663fa49
    ps_command = \
        "$WSShell = New-Object -ComObject Wscript.Shell;" \
        "$Shortcut = $WSShell.CreateShortcut(\"" + str(link_path) + "\"); " \
        "Write-Host $Shortcut.TargetPath ';' $shortcut.Arguments "
    output = subprocess.run(["powershell.exe", ps_command], capture_output=True)
    raw = output.stdout.decode('utf-8')
    launch_path, args = [x.strip() for x in raw.split(';', 1)]
    return launch_path, args

# to test
shortcut_file = r"C:\Users\REPLACE_WITH_USERNAME\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Accessibility\Narrator.lnk"
a, args = get_target(shortcut_file)
print(a)  # C:\WINDOWS\system32\narrator.exe 

(您可以删除->类型提示,以使其在旧版Python中运行)

我注意到在运行大量快捷方式时速度很慢。您可以使用Jared的方法,检查结果是否为None,如果是,则运行此代码以获取目标路径。


1

使用基于正则表达式的直接解析方法(在答案中提出)对于我遇到的所有快捷方式都不可靠。其中一些只有相对路径,例如..\\..\\..\\..\\..\\..\\Program Files\\ImageGlass\\ImageGlass.exe(由msi安装程序生成),并且存储为宽字符,在Python中处理起来很棘手。
因此,我发现了一个名为LnkParse3的Python模块,易于使用并满足我的需求。
以下是一个示例脚本,用于显示作为第一个参数传递的lnk文件的目标:

import LnkParse3
import sys

with open(sys.argv[1], 'rb') as indata:
    lnk = LnkParse3.lnk_file(indata)
    print(lnk.lnk_command)

0

我来到这个帖子是为了寻找一种解析“.lnk”文件并获取目标文件名的方法。

我发现另一个非常简单的解决方案:

pip install comtypes

那么

from comtypes.client import CreateObject
from comtypes.persist import IPersistFile
from comtypes.shelllink import ShellLink

# MAKE SURE THIS VAT CONTAINS A STRING AND NOT AN OBJECT OF 'PATH'
# I spent too much time figuring out the problem with .load(..) function ahead
pathStr="c:\folder\yourlink.lnk"

s = CreateObject(ShellLink)
p = s.QueryInterface(IPersistFile)
p.Load(pathStr, False)
print(s.GetPath())
print(s.GetArguments())
print(s.GetWorkingDirectory())
print(s.GetIconLocation())
try:
    # the GetDescription create exception in some of the links
    print(s.GetDescription())
except Exception as e:
    print(e)
print(s.Hotkey)
print(s.ShowCmd)

基于这个很棒的答案... https://dev59.com/6Jnga4cB1Zd3GeqPYWyf#43856809


-1

这很容易,就像打开“.exe”文件一样简单。在这里,我们将使用os模块来实现。您只需创建一个快捷方式.lnk并将其存储在任何您选择的文件夹中。然后,在任何Python文件中,首先导入os模块(已安装,只需导入)。然后,使用一个变量,比如说path,并为其分配一个字符串值,其中包含您的.lnk文件的位置。只需创建所需应用程序的快捷方式。最后,我们将使用os.startfile()来打开我们的快捷方式。

需要记住的要点:

  1. 位置应该在双引号内。
  2. 最重要的是,打开“属性”。然后,在其中打开“详细信息”。在那里,您可以获得您的快捷方式的确切名称。请在最后写上“.lnk”。

现在,您已经完成了该过程。我希望它能对您有所帮助。如果需要额外的帮助,我在底部留下了我的代码。

import os

path = "C:\\Users\\hello\\OneDrive\\Desktop\\Shortcuts\\OneNote for Windows 10.lnk"

os.startfile(path)

在我的代码中,我使用了变量 path,并且创建了一个 OneNote 的快捷方式。在路径中,我定义了 OneNote 快捷方式的位置。因此,当我使用os.startfile(path)时,os 模块将打开在变量 path 中定义的快捷方式文件。

Python 并不特别关心您使用单引号还是双引号。称它们为“反引号”有点误导(花式引号看起来像反引号;而 ASCII 引号则不太一样)。对于 Windows 路径名,使用原始字符串(在开头引号前加上 r)通常比每个反斜杠都加倍更有意义;尽管为了兼容性,Windows 实际上也允许您将反斜杠替换为正斜杠。 - tripleee
无论如何,问题是如何解决链接,而不是如何打开它。 - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接