37

我正在尝试.lnk从 Python 读取快捷方式 ( ) 文件的目标文件/目录。有没有不头痛的方法呢?规格超出了我的想象。我不介意使用仅限 Windows 的 API。

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

4

8 回答 8

50

使用 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)
于 2009-02-20T22:59:37.977 回答
17

我知道这是一个较旧的线程,但我觉得没有太多关于使用原始问题中所述的链接规范的方法的信息。

我的快捷方式目标实现无法使用 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])
于 2015-03-09T21:55:13.263 回答
11

或者,您可以尝试使用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
于 2008-12-29T04:53:05.620 回答
10

基本上直接调用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

于 2008-12-29T04:44:23.003 回答
5

我也意识到这个问题很老,但我发现答案与我的情况最相关。

像 Jared 的回答一样,我也无法使用 win32com 模块。所以 Jared 对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])

我希望这对其他人也有帮助。谢谢

于 2016-04-19T00:37:09.843 回答
2

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

于 2020-06-18T21:14:22.560 回答
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模块将打开我在变量路径中定义的快捷方式文件。

于 2020-06-08T02:48:53.783 回答
0

这项工作可以在没有任何模块的情况下完成,这样做将返回具有快捷方式文件目标的 ab 字符串。基本上你所做的就是以读取二进制模式(rb模式)打开文件。这是完成此任务的代码:

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

我目前正在使用 python 3.9.2,如果您遇到此问题,请告诉我,我会尝试修复它。

于 2021-04-23T08:05:51.473 回答