Python windows上的符号链接?

Python windows上的符号链接?,python,winapi,symlink,pywin32,Python,Winapi,Symlink,Pywin32,有人知道从Python跨win32版本生成/读取符号链接的方法吗?理想情况下,应该有最少的特定于平台的代码,因为我需要我的应用程序跨平台。问题是,正如所解释的,例如,Windows本身对符号链接功能的支持在Windows版本中有所不同,因此,例如,在Vista(需要大量工作)中,您可以获得比XP或2000更多的功能(在其他win32版本上没有问题)。或者您可以使用快捷方式,当然,快捷方式有其自身的限制,并且不是“真正的”相当于Unix符号链接。因此,您必须明确指定您需要什么功能,您愿意在跨win

有人知道从Python跨win32版本生成/读取符号链接的方法吗?理想情况下,应该有最少的特定于平台的代码,因为我需要我的应用程序跨平台。

问题是,正如所解释的,例如,Windows本身对符号链接功能的支持在Windows版本中有所不同,因此,例如,在Vista(需要大量工作)中,您可以获得比XP或2000更多的功能(在其他win32版本上没有问题)。或者您可以使用快捷方式,当然,快捷方式有其自身的限制,并且不是“真正的”相当于Unix符号链接。因此,您必须明确指定您需要什么功能,您愿意在跨win32操作的祭坛上牺牲多少功能,等等——然后,我们可以解决如何实现您在
ctypes
win32all
调用方面选择的折衷方案……这是最起码的从某种意义上讲,NTFS文件系统有连接点,我认为您可以使用它们,您可以使用python win32 API模块来实现这一点,例如

import win32file

win32file.CreateSymbolicLink(fileSrc, fileTarget, 1)
如果您不想依赖win32 API模块,您可以始终使用
ctypes
并直接调用
CreateSymbolicLink
win32 API,例如

import ctypes

kdll = ctypes.windll.LoadLibrary("kernel32.dll")

kdll.CreateSymbolicLinkA("d:\\test.txt", "d:\\test_link.txt", 0)
MSDN()表示支持的最低客户端是WindowsVista

此外:这也适用于目录(使用第三个参数表示)。使用unicode支持时,它看起来如下所示:

kdll.CreateSymbolicLinkW(UR"D:\testdirLink", UR"D:\testdir", 1)
也看到

我将以下内容放入Lib/site packages/sitecustomize.py

import os

__CSL = None
def symlink(source, link_name):
    '''symlink(source, link_name)
       Creates a symbolic link pointing to source named link_name'''
    global __CSL
    if __CSL is None:
        import ctypes
        csl = ctypes.windll.kernel32.CreateSymbolicLinkW
        csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
        csl.restype = ctypes.c_ubyte
        __CSL = csl
    flags = 0
    if source is not None and os.path.isdir(source):
        flags = 1
    if __CSL(link_name, source, flags) == 0:
        raise ctypes.WinError()

os.symlink = symlink

或者,如果要使用pywin32,可以使用前面提到的方法,要读取,请使用:

from win32file import *
from winioctlcon import FSCTL_GET_REPARSE_POINT

__all__ = ['islink', 'readlink']

# Win32file doesn't seem to have this attribute.
FILE_ATTRIBUTE_REPARSE_POINT = 1024
# To make things easier.
REPARSE_FOLDER = (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)

# For the parse_reparse_buffer function
SYMBOLIC_LINK = 'symbolic'
MOUNTPOINT = 'mountpoint'
GENERIC = 'generic'

def islink(fpath):
    """ Windows islink implementation. """
    if GetFileAttributes(fpath) & REPARSE_FOLDER == REPARSE_FOLDER:
        return True
    return False


def parse_reparse_buffer(original, reparse_type=SYMBOLIC_LINK):
    """ Implementing the below in Python:

    typedef struct _REPARSE_DATA_BUFFER {
        ULONG  ReparseTag;
        USHORT ReparseDataLength;
        USHORT Reserved;
        union {
            struct {
                USHORT SubstituteNameOffset;
                USHORT SubstituteNameLength;
                USHORT PrintNameOffset;
                USHORT PrintNameLength;
                ULONG Flags;
                WCHAR PathBuffer[1];
            } SymbolicLinkReparseBuffer;
            struct {
                USHORT SubstituteNameOffset;
                USHORT SubstituteNameLength;
                USHORT PrintNameOffset;
                USHORT PrintNameLength;
                WCHAR PathBuffer[1];
            } MountPointReparseBuffer;
            struct {
                UCHAR  DataBuffer[1];
            } GenericReparseBuffer;
        } DUMMYUNIONNAME;
    } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

    """
    # Size of our data types
    SZULONG = 4 # sizeof(ULONG)
    SZUSHORT = 2 # sizeof(USHORT)

    # Our structure.
    # Probably a better way to iterate a dictionary in a particular order,
    # but I was in a hurry, unfortunately, so I used pkeys.
    buffer = {
        'tag' : SZULONG,
        'data_length' : SZUSHORT,
        'reserved' : SZUSHORT,
        SYMBOLIC_LINK : {
            'substitute_name_offset' : SZUSHORT,
            'substitute_name_length' : SZUSHORT,
            'print_name_offset' : SZUSHORT,
            'print_name_length' : SZUSHORT,
            'flags' : SZULONG,
            'buffer' : u'',
            'pkeys' : [
                'substitute_name_offset',
                'substitute_name_length',
                'print_name_offset',
                'print_name_length',
                'flags',
            ]
        },
        MOUNTPOINT : {
            'substitute_name_offset' : SZUSHORT,
            'substitute_name_length' : SZUSHORT,
            'print_name_offset' : SZUSHORT,
            'print_name_length' : SZUSHORT,
            'buffer' : u'',
            'pkeys' : [
                'substitute_name_offset',
                'substitute_name_length',
                'print_name_offset',
                'print_name_length',
            ]
        },
        GENERIC : {
            'pkeys' : [],
            'buffer': ''
        }
    }

    # Header stuff
    buffer['tag'] = original[:SZULONG]
    buffer['data_length'] = original[SZULONG:SZUSHORT]
    buffer['reserved'] = original[SZULONG+SZUSHORT:SZUSHORT]
    original = original[8:]

    # Parsing
    k = reparse_type
    for c in buffer[k]['pkeys']:
        if type(buffer[k][c]) == int:
            sz = buffer[k][c]
            bytes = original[:sz]
            buffer[k][c] = 0
            for b in bytes:
                n = ord(b)
                if n:
                    buffer[k][c] += n
            original = original[sz:]

    # Using the offset and length's grabbed, we'll set the buffer.
    buffer[k]['buffer'] = original
    return buffer

def readlink(fpath):
    """ Windows readlink implementation. """
    # This wouldn't return true if the file didn't exist, as far as I know.
    if not islink(fpath):
        return None

    # Open the file correctly depending on the string type.
    handle = CreateFileW(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0) \
                if type(fpath) == unicode else \
            CreateFile(fpath, GENERIC_READ, 0, None, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0)

    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
    buffer = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 16*1024)
    # Above will return an ugly string (byte array), so we'll need to parse it.

    # But first, we'll close the handle to our file so we're not locking it anymore.
    CloseHandle(handle)

    # Minimum possible length (assuming that the length of the target is bigger than 0)
    if len(buffer) < 9:
        return None
    # Parse and return our result.
    result = parse_reparse_buffer(buffer)
    offset = result[SYMBOLIC_LINK]['substitute_name_offset']
    ending = offset + result[SYMBOLIC_LINK]['substitute_name_length']
    rpath = result[SYMBOLIC_LINK]['buffer'][offset:ending].replace('\x00','')
    if len(rpath) > 4 and rpath[0:4] == '\\??\\':
        rpath = rpath[4:]
    return rpath

def realpath(fpath):
    from os import path
    while islink(fpath):
        rpath = readlink(fpath)
        if not path.isabs(rpath):
            rpath = path.abspath(path.join(path.dirname(fpath), rpath))
        fpath = rpath
    return fpath


def example():
    from os import system, unlink
    system('cmd.exe /c echo Hello World > test.txt')
    system('mklink test-link.txt test.txt')
    print 'IsLink: %s' % islink('test-link.txt')
    print 'ReadLink: %s' % readlink('test-link.txt')
    print 'RealPath: %s' % realpath('test-link.txt')
    unlink('test-link.txt')
    unlink('test.txt')

if __name__=='__main__':
    example()

如果你把它放在你正在发布的东西中,因为这种形式的符号链接只在Vista+上受支持。

Juntalis的代码不处理Unicode,所以我修改了它,使用ctypes,并使用struct简化了它。我还参考了

导入操作系统、ctypes、结构
从ctypes导入Windell、wintypes
FSCTL_GET_repasse_POINT=0x900a8
文件\属性\只读=0x0001
文件\u属性\u隐藏=0x0002
文件\属性\目录=0x0010
文件\u属性\u法线=0x0080
文件\属性\重新分析\点=0x0400
一般读取=0x8000000
一般写入=0x40000000
打开\u现有=3
文件读取属性=0x80
文件\标志\打开\重新分析\点=0x00200000
无效的\u句柄\u值=wintypes.HANDLE(-1).VALUE
无效的\u文件\u属性=0xFFFFFFFF
文件\标志\打开\重新分析\点=2097152
文件\u标志\u备份\u语义=33554432
#文件标志打开重新分析点文件标志备份语义
文件标志重分析备份=35651584
GetFileAttributes=windell.kernel32.GetFileAttributesW
_CreateFileW=windell.kernel32.CreateFileW
_DevIoCtl=windell.kernel32.DeviceIoControl
_DevIoCtl.argtypes=[
wintypes.HANDLE,#HANDLE hDevice
wintypes.DWORD,#DWORD dwIoControlCode
wintypes.LPVOID,#LPVOID lpInBuffer
wintypes.DWORD,#DWORD大小
wintypes.LPVOID,#LPVOID lpburuffer
wintypes.DWORD,#DWORD nOutBufferSize
指针(wintypes.DWORD),#LPDWORD lpbytes返回
wintypes.LPVOID]#LPOVERLAPPED LPOVERLAPPED
_DevIoCtl.restype=wintypes.BOOL
def islink(路径):
断言os.path.isdir(path),path
如果GetFileAttributes(路径)&文件属性重分析点:
返回真值
其他:
返回错误
def设备控制(hDevice、ioControlCode、输入、输出):
#设备控制功能
# http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
如果输入:
输入大小=长度(输入)
其他:
输入大小=0
如果isinstance(输出,int):
输出=类型。创建字符串缓冲区(输出)
输出大小=透镜(输出)
断言isinstance(输出,ctypes.Array)
bytesReturned=wintypes.DWORD()
状态=_DeviceOctl(HDDevice、ioControlCode、input、,
输入大小,输出,输出大小,字节返回,无)
打印“状态(%d)”%status
如果状态!=0:
返回输出[:bytesReturned.value]
其他:
一无所获
def CreateFile(路径、访问、共享模式、创建、标志):
return _CreateFileW(路径、访问、共享模式、无、创建、标志、无)
SymbolicClinkResparseFormat=“LHHHHHL”
SymbolClinkResparseSize=struct.calcsize(SymbolClinkResparseFormat);
def读取链接(路径):
“”“Windows readlink实现。”“”
#据我所知,如果文件不存在,则不会返回true。
断言islink(路径)
断言类型(路径)=unicode
#根据字符串类型正确打开文件。
hfile=CreateFile(路径,通用\u读取,0,打开\u现有,
文件\u标志\u重新分析\u备份)
#最大重新分析数据缓冲区大小=16384=(16*1024)
缓冲区=设备控制(hfile,FSCTL\u获取\u重新分析\u点,无,16384)
闭合手柄(hfile)
#最小可能长度(假设目标的长度大于0)
如果不是缓冲区或透镜(缓冲区)<9:
一无所获
#解析并返回我们的结果。
#typedef结构\u重新分析\u数据\u缓冲区{
#乌龙重铺标签;
#USHORT-repassedatalength;
#乌肖特保留;
#联合{
#结构{
#USHORT替代物偏移量;
#USHORT替代漆包线;
#USHORT PrintNameOffset;
#USHORT PrintNameLength;
#乌龙旗;
#WCHAR路径缓冲区[1];
#}符号解析缓冲区;
#结构{
#USHORT替代物偏移量;
#USHORT替代漆包线;
#USHORT PrintNameOffset;
#USHORT PrintNameLength;
#WCHAR路径缓冲区[1];
#}mountpointrepassebuffer;
#结构{
#UCHAR-DataBuffer[1];
#}GenericReparseBuffer;
#}dummy名称;
#}重新分析数据缓冲区,*准备数据缓冲区;
#仅句柄符号为ParseBuffer
(标记、数据长度、保留服务器、SubstituteNameOffset、SubstituteNameLength、,
打印名称偏移,打印
sys.getwindowsversion()[0] >= 6
import os, ctypes, struct
from ctypes import windll, wintypes

FSCTL_GET_REPARSE_POINT = 0x900a8

FILE_ATTRIBUTE_READONLY      = 0x0001
FILE_ATTRIBUTE_HIDDEN        = 0x0002
FILE_ATTRIBUTE_DIRECTORY     = 0x0010
FILE_ATTRIBUTE_NORMAL        = 0x0080
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400


GENERIC_READ  = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 3
FILE_READ_ATTRIBUTES = 0x80
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value

INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF

FILE_FLAG_OPEN_REPARSE_POINT = 2097152
FILE_FLAG_BACKUP_SEMANTICS = 33554432
# FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTI
FILE_FLAG_REPARSE_BACKUP = 35651584


GetFileAttributes = windll.kernel32.GetFileAttributesW
_CreateFileW = windll.kernel32.CreateFileW
_DevIoCtl = windll.kernel32.DeviceIoControl
_DevIoCtl.argtypes = [
    wintypes.HANDLE, #HANDLE hDevice
    wintypes.DWORD, #DWORD dwIoControlCode
    wintypes.LPVOID, #LPVOID lpInBuffer
    wintypes.DWORD, #DWORD nInBufferSize
    wintypes.LPVOID, #LPVOID lpOutBuffer
    wintypes.DWORD, #DWORD nOutBufferSize
    ctypes.POINTER(wintypes.DWORD), #LPDWORD lpBytesReturned
    wintypes.LPVOID] #LPOVERLAPPED lpOverlapped
_DevIoCtl.restype = wintypes.BOOL


def islink(path):
    assert os.path.isdir(path), path
    if GetFileAttributes(path) & FILE_ATTRIBUTE_REPARSE_POINT:
        return True
    else:
        return False


def DeviceIoControl(hDevice, ioControlCode, input, output):
    # DeviceIoControl Function
    # http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
    if input:
        input_size = len(input)
    else:
        input_size = 0
    if isinstance(output, int):
        output = ctypes.create_string_buffer(output)
    output_size = len(output)
    assert isinstance(output, ctypes.Array)
    bytesReturned = wintypes.DWORD()
    status = _DevIoCtl(hDevice, ioControlCode, input,
                       input_size, output, output_size, bytesReturned, None)
    print "status(%d)" % status
    if status != 0:
        return output[:bytesReturned.value]
    else:
        return None


def CreateFile(path, access, sharemode, creation, flags):
    return _CreateFileW(path, access, sharemode, None, creation, flags, None)


SymbolicLinkReparseFormat = "LHHHHHHL"
SymbolicLinkReparseSize = struct.calcsize(SymbolicLinkReparseFormat);

def readlink(path):
    """ Windows readlink implementation. """
    # This wouldn't return true if the file didn't exist, as far as I know.
    assert islink(path)
    assert type(path) == unicode

    # Open the file correctly depending on the string type.
    hfile = CreateFile(path, GENERIC_READ, 0, OPEN_EXISTING,
                       FILE_FLAG_REPARSE_BACKUP)
    # MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 = (16*1024)
    buffer = DeviceIoControl(hfile, FSCTL_GET_REPARSE_POINT, None, 16384)
    CloseHandle(hfile)

    # Minimum possible length (assuming length of the target is bigger than 0)
    if not buffer or len(buffer) < 9:
        return None

    # Parse and return our result.
    # typedef struct _REPARSE_DATA_BUFFER {
    #   ULONG  ReparseTag;
    #   USHORT ReparseDataLength;
    #   USHORT Reserved;
    #   union {
    #       struct {
    #           USHORT SubstituteNameOffset;
    #           USHORT SubstituteNameLength;
    #           USHORT PrintNameOffset;
    #           USHORT PrintNameLength;
    #           ULONG Flags;
    #           WCHAR PathBuffer[1];
    #       } SymbolicLinkReparseBuffer;
    #       struct {
    #           USHORT SubstituteNameOffset;
    #           USHORT SubstituteNameLength;
    #           USHORT PrintNameOffset;
    #           USHORT PrintNameLength;
    #           WCHAR PathBuffer[1];
    #       } MountPointReparseBuffer;
    #       struct {
    #           UCHAR  DataBuffer[1];
    #       } GenericReparseBuffer;
    #   } DUMMYUNIONNAME;
    # } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

    # Only handle SymbolicLinkReparseBuffer
    (tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength,
     PrintNameOffset, PrintNameLength,
     Flags) = struct.unpack(SymbolicLinkReparseFormat,
                            buffer[:SymbolicLinkReparseSize])
    print tag, dataLength, reserver, SubstituteNameOffset, SubstituteNameLength
    start = SubstituteNameOffset + SymbolicLinkReparseSize
    actualPath = buffer[start : start + SubstituteNameLength].decode("utf-16")
    # This utf-16 string is null terminated
    index = actualPath.find(u"\0")
    assert index > 0
    if index > 0:
        actualPath = actualPath[:index]
    if actualPath.startswith(u"?\\"):
        return actualPath[2:]
    else:
        return actualPath
dll = ctypes.windll.LoadLibrary("kernel32.dll")
dll.CreateHardLinkA(link_file, _log_filename, 0)
from subprocess import call
call(['mklink', 'LINK', 'TARGET'], shell=True)
import subprocess
subprocess.call(['cmd', '/c', 'mklink', '<path_for_symlink>', '<path_for_file>'])
A required privilege is not held by the client
import win32com.client
import pythoncom
import os

def create_shortcut(original_filepath, shortcut_filepath):
    shell = win32com.client.Dispatch("WScript.Shell")
    shortcut = shell.CreateShortCut(shortcut_filepath)
    shortcut.Targetpath = original_filepath
    shortcut.WindowStyle = 7
    shortcut.save()

create_shortcut(r'C:\Users\xxx\Desktop\test.jpg', 
                r'C:\Users\xxx\Desktop\test.lnk')