7

I would like to write a utility script that changes the icon of folders with a specific name. Is this possible in python? If not, is there any other way of doing this?

Thanks

moinudin
  • 134,091
  • 45
  • 190
  • 216
Waldo Bronchart
  • 10,152
  • 4
  • 23
  • 29

2 Answers2

12
import os
import ctypes
from ctypes import POINTER, Structure, c_wchar, c_int, sizeof, byref
from ctypes.wintypes import BYTE, WORD, DWORD, LPWSTR, LPSTR
import win32api    

HICON = c_int
LPTSTR = LPWSTR
TCHAR = c_wchar
MAX_PATH = 260
FCSM_ICONFILE = 0x00000010
FCS_FORCEWRITE = 0x00000002
SHGFI_ICONLOCATION = 0x000001000    

class GUID(Structure):
    _fields_ = [
        ('Data1', DWORD),
        ('Data2', WORD),
        ('Data3', WORD),
        ('Data4', BYTE * 8)]

class SHFOLDERCUSTOMSETTINGS(Structure):
    _fields_ = [
        ('dwSize', DWORD),
        ('dwMask', DWORD),
        ('pvid', POINTER(GUID)),
        ('pszWebViewTemplate', LPTSTR),
        ('cchWebViewTemplate', DWORD),
        ('pszWebViewTemplateVersion', LPTSTR),
        ('pszInfoTip', LPTSTR),
        ('cchInfoTip', DWORD),
        ('pclsid', POINTER(GUID)),
        ('dwFlags', DWORD),
        ('pszIconFile', LPTSTR),
        ('cchIconFile', DWORD),
        ('iIconIndex', c_int),
        ('pszLogo', LPTSTR),
        ('cchLogo', DWORD)]

class SHFILEINFO(Structure):
    _fields_ = [
        ('hIcon', HICON),
        ('iIcon', c_int),
        ('dwAttributes', DWORD),
        ('szDisplayName', TCHAR * MAX_PATH),
        ('szTypeName', TCHAR * 80)]    

def seticon(folderpath, iconpath, iconindex):
    """Set folder icon.

    >>> seticon(".", "C:\\Windows\\system32\\SHELL32.dll", 10)

    """
    shell32 = ctypes.windll.shell32

    folderpath = unicode(os.path.abspath(folderpath), 'mbcs')
    iconpath = unicode(os.path.abspath(iconpath), 'mbcs')

    fcs = SHFOLDERCUSTOMSETTINGS()
    fcs.dwSize = sizeof(fcs)
    fcs.dwMask = FCSM_ICONFILE
    fcs.pszIconFile = iconpath
    fcs.cchIconFile = 0
    fcs.iIconIndex = iconindex

    hr = shell32.SHGetSetFolderCustomSettings(byref(fcs), folderpath,
                                              FCS_FORCEWRITE)
    if hr:
        raise WindowsError(win32api.FormatMessage(hr))

    sfi = SHFILEINFO()
    hr = shell32.SHGetFileInfoW(folderpath, 0, byref(sfi), sizeof(sfi),
                                SHGFI_ICONLOCATION)
    if hr == 0:
        raise WindowsError(win32api.FormatMessage(hr))

    index = shell32.Shell_GetCachedImageIndexW(sfi.szDisplayName, sfi.iIcon, 0)
    if index == -1:
        raise WindowsError()

    shell32.SHUpdateImageW(sfi.szDisplayName, sfi.iIcon, 0, index)
cgohlke
  • 9,142
  • 2
  • 33
  • 36
  • I'm not sure how to get this working again, but it doesn't work in Python 3.x -- "unicode" is deprecated, and I get an OSError if I comment out those two lines. – Stack Johan Jun 25 '17 at 23:37
1

From my understanding to do this one must Customise Folders with Desktop.ini. Though there might be more information on this question.

Community
  • 1
  • 1
William
  • 1,007
  • 7
  • 11
  • Awesome, that's exactly what I wanted. All I have to do is generate that file, and windows wil pick it up I guess. Thanks very much! – Waldo Bronchart Jan 11 '11 at 23:51
  • 1
    Not quite. The file attributes must include System and Hidden, and without updating the icon cache Windows might not display the correct icon. – cgohlke Jan 12 '11 at 00:08
  • @Waldo, expecting Windows to do things is a dangerous habit ;) but I hope I helped anyway. – William Jan 12 '11 at 00:29
  • Better use the SHGetSetFolderCustomSettings and SHChangeNotify API functions via ctypes. – cgohlke Jan 12 '11 at 00:31
  • @William which version of the desktop.ini does this reffer to, doing a simple search of C drive has returned 65 results for desktop.ini in latest version of windows sept 2020 – Ryan Stone Sep 25 '20 at 18:29