How to get Windows' special folders for currently logged-in user?
Asked Answered
A

6

24

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.

Atheistic answered 4/10, 2010 at 20:26 Comment(2)
related: Find system folder locations in PythonVolkman
List of windows folder constants: learn.microsoft.com/en-us/windows/win32/shell/knownfolderidBenildas
S
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).

Saltire answered 4/10, 2010 at 20:42 Comment(9)
Thanks! What exactly is shell in windows and what is shellcon ?Atheistic
@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.Saltire
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.Diophantus
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. blogs.technet.com/b/heyscriptingguy/archive/2010/06/08/… has a great powershell script for listing all of the special values on a particular machine.Evangelineevangelism
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)Dwan
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"?Comptom
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.Saltire
@AndiDog, what is the benefit of this approach over using ctypes?Gamekeeper
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.Saltire
T
25

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'
Turtledove answered 4/10, 2010 at 21:36 Comment(6)
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.Acetabulum
Nice solution. And working on my Windows 8. However, the MS Doc (msdn.microsoft.com/en-us/library/bb762181%28VS.85%29.aspx) says that it's deprecated. I'm not sure what are the implications...Keiko
THANKS A LOT. BTW constant values are here: installmate.com/support/im9/using/symbols/functions/csidls.htmMikkimiko
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.Petiolule
Regarding deprecated: The SHGetKnownFolderPath is much more cumbersome to use, so I would use win32com in that case. Using the answer here for now, to keep down my dependencies :).Yellows
@thomasa88: indeed! And, as SHGetFolderPathW() is already a wrapper to SHGetKnownFolderPath() since Windows Vista, this will possibly be available in the foreseeable future, deprecated or not...Joleen
S
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).

Saltire answered 4/10, 2010 at 20:42 Comment(9)
Thanks! What exactly is shell in windows and what is shellcon ?Atheistic
@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.Saltire
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.Diophantus
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. blogs.technet.com/b/heyscriptingguy/archive/2010/06/08/… has a great powershell script for listing all of the special values on a particular machine.Evangelineevangelism
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)Dwan
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"?Comptom
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.Saltire
@AndiDog, what is the benefit of this approach over using ctypes?Gamekeeper
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.Saltire
D
9
import win32com.client
oShell = win32com.client.Dispatch("Wscript.Shell")
print oShell.SpecialFolders("Desktop")
Dineric answered 2/3, 2011 at 15:44 Comment(0)
K
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 ()
Krona answered 4/10, 2010 at 20:37 Comment(2)
It's just a wrapper around pywin32, and if I need pywin32, I rather used it without another layer.Atheistic
Ridiculously simple and straightforward--and therefore beautiful.Sugarplum
S
0

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

import os
os.popen('echo %appdata%').read().strip()
Summerville answered 17/1, 2019 at 18:50 Comment(1)
Why not use os.environ?Colobus
W
0

https://pypi.org/project/userpaths is an alternatve for that.

# Get the user's My Documents folder
import userpaths
my_docs = userpaths.get_my_documents()

You can directly use like that.

Whereabouts answered 3/3 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.