15

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

Community
  • 1
  • 1
Exploring
  • 2,493
  • 11
  • 56
  • 97
  • 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. – Eryk Sun Oct 31 '19 at 21:36

1 Answers1

22

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
icktoofay
  • 126,289
  • 21
  • 250
  • 231
  • 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` – GuiOm Clair Jun 22 '17 at 18:39
  • 2
    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())`. – Eryk Sun Oct 31 '19 at 21:30
  • 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. – Dan Lenski Feb 26 '20 at 17:43
  • 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. – Colin Atkinson May 07 '21 at 02:41
  • That failed, unfortunately: `AttributeError: module 'ctypes' has no attribute 'windll'` – Oliver Feb 26 '22 at 15:48