22

How can I get Windows special folders like My Documents, Desktop, etc. from my Python script? Do I need win32 extensions?

It must work on Windows 2000 to Windows 7.

martineau
  • 119,623
  • 25
  • 170
  • 301
Primoz
  • 4,079
  • 17
  • 56
  • 67
  • related: [Find system folder locations in Python](http://stackoverflow.com/q/2063508/4279) – jfs Jun 30 '15 at 11:49
  • List of windows folder constants: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid – djvg Nov 18 '19 at 14:02

5 Answers5

23

Should you wish to do it without the win32 extensions, you can use ctypes to call SHGetFolderPath:

>>> import ctypes.wintypes
>>> CSIDL_PERSONAL= 5       # My Documents
>>> SHGFP_TYPE_CURRENT= 0   # Want current, not default value

>>> buf= ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
>>> ctypes.windll.shell32.SHGetFolderPathW(0, CSIDL_PERSONAL, 0, SHGFP_TYPE_CURRENT, buf)
0
>>> buf.value
u'C:\\Documents and Settings\\User\\My Documents'
bobince
  • 528,062
  • 107
  • 651
  • 834
  • 1
    Your code (also) illustrates one of the limitations of using `ctypes` instead of the `win32` extensions, namely the fact that the former doesn't supply any of the often needed Window's constants like those defined in `win32com.shellcon` -- so one has to look-up their values and add each of them manually. – martineau May 20 '13 at 22:00
  • Nice solution. And working on my Windows 8. However, the MS Doc (http://msdn.microsoft.com/en-us/library/bb762181%28VS.85%29.aspx) says that it's `deprecated`. I'm not sure what are the implications... – Zvika Dec 07 '15 at 20:16
  • THANKS A LOT. BTW constant values are here: http://www.installmate.com/support/im9/using/symbols/functions/csidls.htm – lowtech Apr 21 '17 at 21:45
  • By the way, I had to do `ctypes.windll.shell32.SHGetFolderPathW(0, CSIDL_PERSONAL, 0, SHGFP_TYPE_CURRENT, buf)` twice. The first time I got error code `-2147024890`. – Maxwell175 Feb 22 '19 at 17:32
  • Regarding `deprecated`: The SHGetKnownFolderPath is [much more cumbersome](https://gist.github.com/mkropat/7550097) to use, so I would use win32com in that case. Using the answer here for now, to keep down my dependencies :). – thomasa88 Jul 19 '20 at 07:53
22

You can do it with the pywin32 extensions:

from win32com.shell import shell, shellcon
print shell.SHGetFolderPath(0, shellcon.CSIDL_MYPICTURES, None, 0)
# prints something like C:\Documents and Settings\Username\My Documents\My Pictures
# (Unicode object)

Check shellcon.CSIDL_xxx for other possible folders.

I think using pywin32 is the best way. Else you'd have to use ctypes to access the SHGetFolderPath function somehow (other solutions might be possible but these are the ones I know).

AndiDog
  • 68,631
  • 21
  • 159
  • 205
  • 1
    Thanks! What exactly is shell in windows and what is shellcon ? – Primoz Oct 04 '10 at 21:06
  • @Primoz: I don't know what the abbreviations mean, but all *con modules in the pywin32 package are autogenerated from Windows header files and only contain definitions. The `win32com.shell` module contains functions from shell32.dll. – AndiDog Oct 04 '10 at 21:23
  • The docs mention constants `SHGFP_TYPE_CURRENT` and `SHGFP_TYPE_DEFAULT` for the last parameter of `SHGetFolderPath()`, but these aren't defined in `shellcon` for some reason... I wonder why not. – Craig McQueen Nov 08 '10 at 03:09
  • Note that not all shellcon.CSIDL_xxx values are valid on all machines. For example on my machine CSIDL_PERSONAL (5) is valid but CSIDL_MYDOCUMENTS (12) is not. http://blogs.technet.com/b/heyscriptingguy/archive/2010/06/08/hey-scripting-guy-how-can-query-the-contents-of-a-special-folder-on-a-windows-7-computer.aspx has a great powershell script for listing all of the special values on a particular machine. – Dave Jun 09 '13 at 22:37
  • 1
    note that My Documents is not retrievable this way. You need to use: `df = shell.SHGetDesktopFolder() pidl = df.ParseDisplayName(0, None, "::{450d8fba-ad25-11d0-98a8-0800361b1103}")[1] mydocs = shell.SHGetPathFromIDList(pidl)` – v.oddou Sep 09 '14 at 05:21
  • Can I get reverse functionality. Like I have path as "C:\Windows\dir1\dir2\xyz.dll" and I want to get output as "csidl_windows\dir1\dir2\xyz.dll"? – ajay_t Aug 12 '15 at 07:00
  • You can do prefix matching against CSIDL's that you want to check, but there's no direct way to reverse it. I also wouldn't know any use case for it. – AndiDog Aug 12 '15 at 11:22
  • @AndiDog, what is the benefit of this approach over using ctypes? – alpha_989 May 16 '18 at 17:04
  • `ctypes` isn't easy to get right. Luckily someone provided a correct (I guess) answer, but manually writing calls to lots of C functions and getting the data right can be non-trivial. No huge difference otherwise and actually ctypes usage will remove one required dependency. – AndiDog May 22 '18 at 08:58
9
import win32com.client
oShell = win32com.client.Dispatch("Wscript.Shell")
print oShell.SpecialFolders("Desktop")
antyrat
  • 27,479
  • 9
  • 75
  • 76
Don
  • 477
  • 4
  • 5
6

Try winshell (made exactly for this purpose):

import winshell

print 'Desktop =>', winshell.desktop ()
print 'Common Desktop =>', winshell.desktop (1)
print 'Application Data =>', winshell.application_data ()
print 'Common Application Data =>', winshell.application_data (1)
print 'Bookmarks =>', winshell.bookmarks ()
print 'Common Bookmarks =>', winshell.bookmarks (1)
print 'Start Menu =>', winshell.start_menu ()
print 'Common Start Menu =>', winshell.start_menu (1)
print 'Programs =>', winshell.programs ()
print 'Common Programs =>', winshell.programs (1)
print 'Startup =>', winshell.startup ()
print 'Common Startup =>', winshell.startup (1)
print 'My Documents =>', winshell.my_documents ()
print 'Recent =>', winshell.recent ()
print 'SendTo =>', winshell.sendto ()
martineau
  • 119,623
  • 25
  • 170
  • 301
ChristopheD
  • 112,638
  • 29
  • 165
  • 179
0

A little bit hacky, but without the need of a special import

import os
os.popen('echo %appdata%').read().strip()
Kipi
  • 165
  • 2
  • 10