1

After reading a lot and trying for 2 days in a row, I managed to make something work by following this Microsoft tutorial on folder icons, but it's not perfect - changes don't take place immediately. I need to restart explorer / reboot my computer or wait a couple of minutes (randomly) until I see the old icon change to the new icon.

TL;DR

Expected result: I want to programmatically update my folder icon and see that the new icon take place instantly, without waiting. (using c++, .bat, node.js or anything else that would work, I don't mind really).

Problem: After chaining my .ico file, I expect to see the result immideiately, but I only see it after 30 seconds to 4 minutes or so. It changes instantly as well sometimes, but very rarely, and I want it to work 100% of the time.

Requirements and notes:

  • It must not restart Explorer.
  • It would be better if it would not clear the entire thumbnail cache from all folders and files, as it only needs to update a single folder icon.

Hopes / unfulfilling attempts

Renaming desktop.ini programmatically (Not working unfortunately)

I found a way to see the icon change immediately, but it worked only when manually renaming desktop.ini through the explorer itself. (changing desktop.ini to uppercase/lowercase or to desktop-temp.ini and then back to desktop.ini) When doing so, I see the folder icon change immediately.

That led me to a hope that I can do the same programmatically, so I tried this C++ code, that changes desktop.ini to desktop-temp.ini and back to desktop.ini, also with notifying windows that a change has occurred, but it did not work unfortunately.

#include <stdio.h>
#include <Windows.h>
#include <winbase.h>
#include <Tchar.h>
#include <shlobj.h> // for SHGetSpecialFolderPathA


const char folderpath[] = "C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon";

int main()
{
    // rename("C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon\\desktop.ini", "desktop-temp.ini");

    if (MoveFile(_T("C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon\\desktop.ini"), _T("desktop-temp.ini")))
    {
        printf("succeeded\n");
    }
    else
    {
        printf("Error %d\n", GetLastError());
    }

    SHChangeNotify(SHCNE_ALLEVENTS, SHCNF_NOTIFYRECURSIVE, NULL, NULL);
    SHChangeNotify(SHCNE_ALLEVENTS, SHCNF_NOTIFYRECURSIVE, folderpath, NULL);

    if (MoveFile(_T("C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon\\desktop-temp.ini"), _T("desktop.ini")))
    {
        printf("succeeded\n");
    }
    else
    {
        printf("Error %d\n", GetLastError());
    }

    SHChangeNotify(SHCNE_ALLEVENTS, SHCNF_NOTIFYRECURSIVE, NULL, NULL);
    SHChangeNotify(SHCNE_ALLEVENTS, SHCNF_NOTIFYRECURSIVE, folderpath, NULL);
}

SHChangeNotify() (works, but not immidiately)

Inspired by: How to refresh the folder icon instantly in Windows

I tried running this C++ code (after compiling using npm command g++ -o refresh-folder-icons refresh-folder-icons.cpp) and it worked! but I the changes take place between 1 to 5 minutes and if you're lucky, then you see it instantly. This is not reliable. I tested this on two computers (both Windows 10).

#include <windows.h>
#include <ShlObj.h>

const char folderPath[] = "path/to/folder";

int main()
{
    SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, folderPath, NULL);
    SHChangeNotify(SHCNE_ALLEVENTS, SHCNF_NOTIFYRECURSIVE, "path/to/folder/desktop.ini", NULL);
}

"Use a shell function that will notify all running Explorer windows to use the updated desktop.ini" using VBScript (Did not work for me)

I've also tried creating a .bat file followed by this: Changing desktop.ini doesn't update folder icon automatically in Windows but my folder still shows the previous icon.

Refresh Icons Using IE4UINIT

Following this

Run the following command (for Windows 10) and hit Enter: ie4uinit.exe -show For Windows 8 and earlier, run this command instead: ie4uinit.exe -ClearIconCache

Refresh Icons Using NirCmd

Following this tutorial (step 3), I tried installing NirCmd and running all three, none of them refreshed my folder icon:

nircmd.exe sysrefresh 

nircmd.exe sysrefresh environment 

nircmd.exe sysrefresh policy

SHGetSetFolderCustomSettings() (My main hope!!!)

Someone wrote here that the SHGetSetFolderCustomSettings "function sets an icon and sends all necessary notification by itself", and that really have me hope that this is the right path.

Also, in Peter Tseng's answer here and here he shows a sample code that worked, but when I try to complie this C++ code into .exe, I get errors. This is the code:

#include <windows.h>
#include <shlobj.h>

int main()
{
    SHFOLDERCUSTOMSETTINGS pfcs;
    pfcs.dwMask = "C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon\\folderico-1629960936.ico";
    pfcs.pszIconFile = "C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon\\folderico-1629960936.ico";
    pfcs.cchIconFile = 0;
    pfcs.iIconIndex = 0;
    PCWSTR pszPath = L"C:\\Users\\elron\\Elron Apps C\\005 Folder Icon\\paste-folder-icon\\test\\hey";
    SHGetSetFolderCustomSettings(pfcs, pszPath, FCS_FORCEWRITE);
}

but I get three errors, all indicating to the same problem:

  • 'SHFOLDERCUSTOMSETTINGS' was not declared in this scope (gcc)
  • 'pfcs' was not declared in this scope (gcc)
  • 'SHGetSetFolderCustomSettings' was not declared in this scope (gcc)

Why am I getting this error? Is there's a dependency I'm missing? I'm using windows 10.


Elron
  • 1,235
  • 1
  • 13
  • 26
  • `ShlObj_core.h` -- Your `SHFOLDERCUSTOMSETTINGS` values are almost all wrong. - Shouldn't you set the size of the struct? – Jimi Aug 26 '21 at 15:28
  • I'm new to C++, so I might have got it all wrong. What do you mean by the size of the struct? In lines 1359-1387 you can see the struct. https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/um/ShlObj.h#L1359 – Elron Aug 26 '21 at 15:36
  • `DWORD dwSize` is set to the size of `SHFOLDERCUSTOMSETTINGS` (you'll find it quite often :) - `dwMask` (as `dw` implies ) is a `DWORD`: see what you set. – Jimi Aug 26 '21 at 15:39
  • I see. Is that mandatory? Are all the values mandatory? I see `dwSize`, `dwMask`,`pvid`,`pszWebViewTemplate`,`cchWebViewTemplate`,`pszWebViewTemplateVersion` and many more. I don't know what to fill them. – Elron Aug 26 '21 at 15:43
  • 1
    SHChangeNotify is the way to go, but use Unicode paths and function (SHChangeNotifyW(SHCNE_UPDATEITEM, SHCNF_PATHW, ...), or if you have special characters it won't work), also use proper Windows path, with \ not / directory separators. – Simon Mourier Aug 26 '21 at 15:43
  • Check the Docs then: [SHFOLDERCUSTOMSETTINGS](https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-shfoldercustomsettings) – Jimi Aug 26 '21 at 15:45
  • @Jimi How do I know the "The size of the structure, in bytes"? – Elron Aug 26 '21 at 15:45
  • 1
    `pfcs.dwSize = sizeof(SHFOLDERCUSTOMSETTINGS);` – Jimi Aug 26 '21 at 15:46
  • Cool, I'll try that. But I still encounter this error: `'SHFOLDERCUSTOMSETTINGS' was not declared in this scope (gcc)` and it prevents me from compiling the file. Am I missing something? – Elron Aug 26 '21 at 15:47

1 Answers1

1

I managed to get working the last approach (using SHGetSetFolderCustomSettings())

#include <windows.h>
#include <shlobj.h>

int main()
{
    /*
    convert the icon's path to LPWSTR, as required by field pszIconFile
    method taken from https://stackoverflow.com/questions/29847036/convert-char-to-wchar-t-using-mbstowcs-s
    */
    const char* iconPath = "C:\\Users\\YourUsername\\PathTo\\YourIcon\\file.ico";
    size_t size = strlen(iconPath) + 1;
    wchar_t* wtext = new wchar_t[size];

    size_t outSize;
    mbstowcs_s(&outSize, wtext, size, iconPath, size - 1);
    LPWSTR ptr = wtext;

    // (fixed the initialization of some fields)
    SHFOLDERCUSTOMSETTINGS pfcs{};
    pfcs.dwSize = sizeof(SHFOLDERCUSTOMSETTINGS);
    pfcs.dwMask = FCSM_ICONFILE;
    pfcs.pszIconFile = ptr;
    pfcs.cchIconFile = 0;
    pfcs.iIconIndex = 0;
    PCWSTR pszPath = L"C:\\Users\\YourUsername\\PathTo\\YourFolder\\FolderToCustomize";
    SHGetSetFolderCustomSettings(&pfcs, pszPath, FCS_FORCEWRITE);
}

I think the problems you mentioned:

but I get three errors, all indicating to the same problem:

  • 'SHFOLDERCUSTOMSETTINGS' was not declared in this scope (gcc)

might be caused because you weren't using the appropriate compiler. I got similar compile errors when using g++ directly from the console/on Visual Studio Code, but the same code worked when using Visual Studio 2022 (Visual C++ compiler). I'm also new to C++, so feel free to correct me if this is not the source of the problem.

Diego BM
  • 409
  • 4
  • 5
  • SHFOLDERCUSTOMSETTINGS requires at least Windows Vista as Target, if MinGW doesn't provide it for GCC, probably Windows XP was targeted. – Benjamin Buch Dec 22 '22 at 01:01