Search code examples
pythonwindowswinapictypes

How to get the filepath of the user's profile picture using Python?


I want to get the filepath of the user's profile picture in Windows using Python.

I've found the following approach in VB6:

Option Explicit
'KERNEL32
Private Declare Function GetVersion Lib "KERNEL32" () As Long
'SHELL32
Private Declare Function SHGetUserPicturePath Lib "SHELL32" Alias "#261" (ByVal pUserOrPicName As Long, ByVal sguppFlags As Long, ByVal pwszPicPath As Long, ByVal picPathLen As Long) As Long
Private Declare Function xp_SHGetUserPicturePath Lib "SHELL32" Alias "#233" (ByVal pUserOrPicName As Long, ByVal sguppFlags As Long, ByVal pwszPicPath As Long) As Long

Private Const SGUPP_CREATEPICTURESDIR = &H80000000

Public Function LoadUserTile() As IPictureDisp
    Dim sPath   As String

    sPath = String$(256, vbNullChar)

    Select Case (GetVersion() And &HFF)
        Case 5
            Call xp_SHGetUserPicturePath(0, SGUPP_CREATEPICTURESDIR, StrPtr(sPath))
        Case 6
            Call SHGetUserPicturePath(0, SGUPP_CREATEPICTURESDIR, StrPtr(sPath), 256)
    End Select

    sPath = Left$(sPath, InStr(1, sPath, vbNullChar) - 1)

    Set LoadUserTile = LoadPicture(sPath)
End Function

But I don't know how to translate it to Python using ctypes, as the functions used are not documented by the MSDN. I've found this alternative resource, though.

I've also tried to access this folder:

%ProgramData%\Microsoft\User Account Pictures\Guest.bmp
%ProgramData%\Microsoft\User Account Pictures\User.bmp

But there are stored the default profile pictures, not the current ones.


Solution

  • Listing [Python.Docs]: ctypes - A foreign function library for Python.

    [Airesoft.UnDoc]: SHGetUserPicturePath (and the referenced SHGetUserPicturePathEx) contains the exact needed info:

    Copies the users account picture to a temporary directory and returns the path or returns various paths relating to user pictures

    Syntax

    HRESULT WINAPI SHGetUserPicturePath (
        LPCWSTR pwszPicOrUserName,
        DWORD sguppFlags,
        LPWSTR pwszPicPath,
        UINT picPathLen
    )
    

    Although the table at the end of the page lists Win 8.1 as the newest version, it also works on Win 10.

    Notes:

    • Needless to say that being not part of the public API, it shouldn't be relied on (except maybe for demo / learning purposes), as the behavior might change or it could completely disappear. Don't use this code in production!

    code00.py:

    #!/usr/bin/env python
    
    import sys
    import ctypes as ct
    from ctypes import wintypes as wt
    
    
    SGUPP_DIRECTORY = 0x01
    SGUPP_DEFAULTDIRECTORY = 0x02
    SGUPP_CREATEPICTURESDIR = 0x80000000
    
    
    def main(*argv):
        shell32 = ct.WinDLL("shell32.dll")
        SHGetUserPicturePathW = shell32[261]
        SHGetUserPicturePathW.argtypes = (wt.LPWSTR, wt.DWORD, wt.LPWSTR, wt.UINT)
        SHGetUserPicturePathW.restype = ct.c_long
    
        buf_len = 0xFF
        buf = ct.create_unicode_buffer(buf_len)
        flags = SGUPP_CREATEPICTURESDIR
        res = SHGetUserPicturePathW(None, flags, buf, buf_len)
        print("    SHGetUserPicturePathW returned {0:016X}\n    Path set to: [{1:s}]".format(res, buf.value))
    
    
    if __name__ == "__main__":
        print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")),
                                                        64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        rc = main(*sys.argv[1:])
        print("\nDone.")
        sys.exit(rc)
    

    Output:

    e:\Work\Dev\StackOverflow\q059927534>"e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 064bit on win32
    
        SHGetUserPicturePathW returned 0000000000000000
        Path set to: [C:\Users\cfati\AppData\Local\Temp\cfati.bmp]
    
    Done.
    

    Related: [SO]: Get user picture.