17

I need to determine Windows short file name from my python code. For that I can find a solution using the win32api.

import win32api
long_file_name='C:\Program Files\I am a file'
short_file_name=win32api.GetShortPathName(long_file_name)

Reference: http://blog.lowkster.com/2008/10/spaces-in-directory-names-i-really-love.html

Unfortunately for that I need to install pywin32 or ActivePython which is not possible in my case.

Also reference from SO:

Getting short path in python: Getting short path in python

1
  • Note that generating short filenames is optional in NTFS and recommended to be disabled on systems that have directories with thousands of files because it slows down access considerably, and short filenames are not supported at all with ReFS and exFAT. There are various better ways to work around the classic DOS MAX_PATH limit - such as "\\?\" device paths, subst/mapped drives, mount points (junctions), and symbolic links. Commented Oct 31, 2019 at 21:36

1 Answer 1

25

You can use ctypes. According to the documentation on MSDN, GetShortPathName is in KERNEL32.DLL. Note that the real functions are GetShortPathNameW for wide (Unicode) characters and GetShortPathNameA for single-byte characters. Since wide characters are more general, we'll use that version. First, set the prototype according to the documentation:

import ctypes
from ctypes import wintypes
_GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW
_GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD]
_GetShortPathNameW.restype = wintypes.DWORD

GetShortPathName is used by first calling it without a destination buffer. It will return the number of characters you need to make the destination buffer. You then call it again with a buffer of that size. If, due to a TOCTTOU problem, the return value is still larger, keep trying until you've got it right. So:

def get_short_path_name(long_name):
    """
    Gets the short path name of a given long path.
    http://stackoverflow.com/a/23598461/200291
    """
    output_buf_size = 0
    while True:
        output_buf = ctypes.create_unicode_buffer(output_buf_size)
        needed = _GetShortPathNameW(long_name, output_buf, output_buf_size)
        if output_buf_size >= needed:
            return output_buf.value
        else:
            output_buf_size = needed
Sign up to request clarification or add additional context in comments.

5 Comments

Hi, the piece of code seems to go perfectly for me, except when I add the last folder. Would you have an idea on why ? Here is the path C:\data\SIEA\_0 Mise à niveau des boites\Zone NRA de Ferney\Ornex 04-05\APD_e\06_Fichier Adresse - IPE
The call could fail. So use kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) instead of ctypes.windll.kernel32. Then if needed == 0, raise an exception via raise ctypes.WinError(ctypes.get_last_error()).
Very handy! The only suggestion I'd make is to start with output_buf_size=len(long_name), which will allow it to work on the first try almost all the time, due to the fact that the short_path is rarely longer than the input path.
Brief addendum to @ErykSun's comment: as a slight shorthand, you can do if needed == 0: raise ctypes.WinError() which will do GetLastError() for you. I also didn't need to specify use_last_error=True this way, but YMMV.
That failed, unfortunately: AttributeError: module 'ctypes' has no attribute 'windll'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.