代码之家  ›  专栏  ›  技术社区  ›  Etienne Perot

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

  •  23
  • Etienne Perot  · 技术社区  · 16 年前

    我正在尝试读取快捷方式的目标文件/目录( .lnk )来自Python的文件。有没有不头痛的方法?这个规格太高了。 我不介意只使用Windows API。

    我的最终目标是找到 "(My) Videos" %HOMEPATH%\My Documents\My Videos ,在Vista上是 %HOMEPATH%\Videos . 但是,用户可以重新定位此文件夹。在这种情况下 文件夹不再存在,并被替换为 %HOMEPATH%\Videos.lnk "My Videos" 文件夹我想要它的绝对位置。

    5 回复  |  直到 3 年前
        1
  •  48
  •   SaoPauloooo    16 年前

    使用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
  •  16
  •   Glycerine    7 年前

    我的快捷目标实现无法使用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])
    
        3
  •  11
  •   Adam Rosenfield    16 年前

    或者,您可以尝试使用 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
    
        4
  •  10
  •   gak    16 年前

    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

        5
  •  5
  •   shimrot    9 年前

    我也意识到这个问题由来已久,但我发现答案与我的情况最为相关。

    MS-SHLLINK 给了我一段路。我需要在Windows和Linux上阅读快捷方式,其中快捷方式由Windows在samba共享上创建。Jared的实现对我来说不太管用,我想这只是因为我在快捷方式格式上遇到了一些不同的变化。但是,它给了我需要的开始(谢谢Jared)。

    这里有一个名为MSShortcut的类,它扩展了Jared的方法。但是,由于使用了Python3.4中添加的一些pathlib特性,因此该实现仅限于Python3.4及更高版本

    #!/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: http://stackoverflow.com/questions/397125/reading-the-target-of-a-lnk-file-in-python
    
    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
  •  2
  •   Diego-MX    4 年前

    我真的不喜欢任何可用的答案,因为我不想继续导入越来越多的库,而且“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 但需要相应地指定其他文件扩展名。

        7
  •  0
  •   tripleee    5 年前

    os .lnk 并将其存储在您选择的任何文件夹中。然后,在任何Python文件中,首先导入 模块(已安装,仅导入)。然后,使用一个变量,比如 path ,并为其指定一个字符串值,其中包含 文件只需创建所需应用程序的快捷方式。最后,我们将使用 os.startfile()

    1. 该位置应在双倒逗号内。

    现在,您已经完成了该过程。我希望它能帮助你。为了获得更多的帮助,我在底部留下了我的代码。

    import os
    
    path = "C:\\Users\\hello\\OneDrive\\Desktop\\Shortcuts\\OneNote for Windows 10.lnk"
    
    os.startfile(path)
    

    路径 作为变量,我为OneNote创建了快捷方式。在路径中,我定义了OneNote快捷方式的位置。所以当我使用 os.startfile(path) 这个 操作系统

        8
  •  0
  •   Dev    4 年前

    rb 模式)。以下是完成此任务的代码:

    with open('programs.lnk - Copy','rb') as f:
        destination=f.read()
    

    我目前正在使用Python3.9.2,如果您在这方面遇到问题,请告诉我,我将尝试修复它。

    推荐文章