una por pln linea leer especifica con archivo python directory shortcut target lnk

por - ¿Lectura del objetivo de un archivo.lnk en Python?



leer una linea especifica de un archivo en python (5)

Intento leer el archivo / directorio de destino de un archivo de acceso directo ( .lnk ) de Python. ¿Hay una forma libre de dolor de cabeza para hacerlo? La especificación .lnk [PDF] está muy por encima de mi cabeza. No me importa usar API solo de Windows.

Mi objetivo final es encontrar la carpeta "(My) Videos" en Windows XP y Vista. En XP, de forma predeterminada, está en %HOMEPATH%/My Documents/My Videos , y en Vista es %HOMEPATH%/Videos . Sin embargo, el usuario puede reubicar esta carpeta. En el caso, la carpeta %HOMEPATH%/Videos deja de existir y se reemplaza por %HOMEPATH%/Videos.lnk que apunta a la nueva carpeta "My Videos" . Y quiero su ubicación absoluta.


Básicamente llame a la API de Windows directamente. Aquí hay un buen ejemplo que se encuentra después de buscar en Google:

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)

Esto es de http://timgolden.me.uk/python/win32_how_do_i/create-a-shortcut.html .

Puede buscar "python ishelllink" para otros ejemplos.

Además, la referencia de API también ayuda: http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx


Alternativamente, puedes intentar usar SHGetFolderPath () . El siguiente código podría funcionar, pero no estoy en una máquina con Windows ahora mismo, así que no puedo probarlo.

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


Crear un acceso directo usando Python (a través de WSH)

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


Lea el objetivo de un acceso directo usando Python (a través de WSH)

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


También me doy cuenta de que esta pregunta es antigua, pero las respuestas me parecieron más relevantes para mi situación.

Al igual que la respuesta de Jared, tampoco pude usar el módulo win32com. Así que el uso de Jared de la estructura binaria de MS-SHLLINK me ayudó a llegar allí. Necesitaba leer accesos directos tanto en Windows como en Linux, donde Windows creó los accesos directos en un recurso compartido de samba. La implementación de Jared no funcionó para mí, creo que solo porque encontré algunas variaciones diferentes en el formato de acceso directo. Pero, me dio el comienzo que necesitaba (gracias a Jared).

Entonces, aquí hay una clase llamada MSShortcut que expande el enfoque de Jared. Sin embargo, la implementación es solo Python3.4 y superior, debido al uso de algunas características de pathlib agregadas en 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://.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])

Espero que esto ayude a otros también. Gracias


Sé que este es un hilo más antiguo, pero creo que no hay mucha información sobre el método que utiliza la especificación del enlace como se señaló en la pregunta original.

La implementación de mi objetivo de acceso directo no pudo usar el módulo win32com y después de mucha búsqueda, decidí crear la mía. Nada más parecía lograr lo que necesitaba bajo mis restricciones. Espero que esto ayude a otras personas en esta misma situación.

Utiliza la estructura binaria que Microsoft ha proporcionado para 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])