Click here to Skip to main content
14,334,145 members

No More desktop.ini Icons on the Desktop

Rate this:
3.19 (11 votes)
Please Sign up or sign in to vote.
3.19 (11 votes)
19 Sep 2019CPOL
A small Windows Service that provides permanent monitoring against those little pesky icons that few people enjoy and love

Introduction

Desktop.ini are hidden files that store information about the view settings of a Windows folder, including but not limited to the display icon whereabouts, infotip when the cursor hovers the folder and localization information.

Although desktop.ini files have the hidden attribute set, users will be able to view them by changing a couple of Folder Options settings, namely by checking "Show hidden files, folders and drives" and unchecking "Hide protected operating system files".

Many users do that because they want to see with their own eyes what they got, rather than delegate on Windows the production of a selection what they should see.

However, by doing that, two desktop.ini icons will appear on the desktop screen. This happens because the desktop screen shows both the contents of the Current User Desktop folder and the Public Desktop folder. Since each one of these folders has its own desktop.ini icon, voilà the explanation for the two icons!

It is safe and easy to delete the desktop.ini icons from the desktop screen. The problem is that they are automatically regenerated by the system when the first opportunity arises, namely on reboot.

We have seen a few solutions on the internet to fix this, but all of them do not appear to work anymore.

So, I introduce here NoDskIni, a solution that should work on all version of Windows from Vista till the latest Windows 10.

How Does NoDskIni Work?

NoDskIni is a small Windows Service that once installed, will monitor your desktop screen 24/7, like a faithful watchdog.

It will not consume any CPU cycles unless there is work to do, that is:

  • When it is alerted that some file was created in either the User Desktop folder or the Public Desktop folder, it will rush to see if it a desktop.ini and if true, will promptly delete it.
  • When there is a Session Change with a possible change of logged on user account, it will re access the User Desktop folder path and change if needed.

How to Launch NoDskIni?

NoDskIni doubles both as a console application and a Windows Service. This is an old trick, sported in various instances, namely in some Windows Platform SDKs, where the same application that installs the Service is the Service itself.

So, to launch the Service, simply open an elevated command prompt and run:

NoDskIni -install

To remove the Service, proceed the same way and run:

NoDskIni -remove

Using the Code

Once the Service is installed, it launches a separate thread with the following function which is the heart of everything:

void WatchDesktopFoldersThread(LPVOID param)
{
#define TIMEOUT 20000 
    DWORD dwWaitStatus;
    HANDLE dwChangeHandles[3] = { 0 };
    WCHAR InteractiveUserDesktopFolder[MAX_PATH] = { 0 };
    WCHAR InteractivePublicDesktopFolder[MAX_PATH] = { 0 };

    AdjustDesktopFolders(InteractiveUserDesktopFolder, InteractivePublicDesktopFolder);
    if (!SetChangeNotification
    (dwChangeHandles, InteractiveUserDesktopFolder, InteractivePublicDesktopFolder))
        goto Exit;
    WatchTriggerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (WatchTriggerEvent == NULL)
    {
        OutputDebugString(L"Create WatchTriggerEvent error");
        goto Cleanup;
    }
    dwChangeHandles[2] = WatchTriggerEvent;

    while (TRUE)
    {
        dwWaitStatus = WaitForMultipleObjects(3, dwChangeHandles,
            FALSE, timeoutValue);
        Sleep(1000);
        switch (dwWaitStatus)
        {
        case WAIT_OBJECT_0:
        case WAIT_OBJECT_0 + 1:
            if (FindNextChangeNotification(dwChangeHandles[dwWaitStatus]) == FALSE)
            {
                goto Cleanup;
            }
            if (dwWaitStatus == WAIT_OBJECT_0)
            {
                OutputDebugString(L"Removing UserDesktopFolder desktop.ini");
                removeDesktopIni(InteractiveUserDesktopFolder);
            }
            else
            {
                OutputDebugString(L"Removing PublicDesktopFolder desktop.ini");
                removeDesktopIni(InteractivePublicDesktopFolder);
            }
            break;
        case WAIT_OBJECT_0 + 2:
            ResetEvent(WatchTriggerEvent);
            timeoutValue = TIMEOUT;
            break;
        case WAIT_TIMEOUT:
            // The interactive user and desktop folders may change. 
            // This will run depending on session change notifications
            AdjustDesktopFolders(InteractiveUserDesktopFolder, InteractivePublicDesktopFolder);
            if (!SetChangeNotification
            (dwChangeHandles, InteractiveUserDesktopFolder, InteractivePublicDesktopFolder))
                goto Cleanup;
            removeDesktopIni(InteractiveUserDesktopFolder);
            removeDesktopIni(InteractivePublicDesktopFolder);
            timeoutValue = INFINITE;
            break;
        default:
        {
            goto Cleanup;
        }
        }
    }
Cleanup:
    FindCloseChangeNotification(dwChangeHandles[0]);
    FindCloseChangeNotification(dwChangeHandles[1]);
    if (WatchTriggerEvent)
        CloseHandle(WatchTriggerEvent);
Exit:
    ExitThread(0);
}

I have been using some goto and I am aware that goto should not be used in most instances, but it is part of the syntax repertoire and looked handy here. I apologize to the purists.

The above function calls AdjustDesktopFolders, shown below, to adjust the Desktop Folder paths according to the logged on interactive user.

Since the Service runs in the System Account, it will need to impersonate the logged on user, otherwise the retrieved Desktop Folders would be from the System account (an error will be produced in some Windows versions).

void AdjustDesktopFolders(WCHAR* PassedUserDesktopFolder, WCHAR* PassedPublicDesktopFolder)
{
    DWORD dwSessionId = 0;
    HANDLE hImprToken = INVALID_HANDLE_VALUE;

    dwSessionId = WTSGetActiveConsoleSessionId();
    if (!WTSQueryUserToken(dwSessionId, &hImprToken))
        return;
    if (!ImpersonateLoggedOnUser(hImprToken))
        return;
    wmemset(PassedUserDesktopFolder, 0, MAX_PATH);
    wmemset(PassedPublicDesktopFolder, 0, MAX_PATH);
    if (!getDesktopFolders(hImprToken, PassedUserDesktopFolder, PassedPublicDesktopFolder))
        return;

    RevertToSelf();
    CloseHandle(hImprToken);
}

Another function from WatchDesktopFoldersThread worth mentioning here is SetChangeNotification.

Our Windows Service does not poll for changes, it waits to be notified of changes. SetChangeNotification instructs Windows about what changes we want to be notified.

int SetChangeNotification(HANDLE* dwChangeHandles, 
    WCHAR* PassedUserDesktopFolder, WCHAR* PassedPublicDesktopFolder)
{
    dwChangeHandles[0] = FindFirstChangeNotification(PassedUserDesktopFolder, 
                         FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
    if ((dwChangeHandles[0] == INVALID_HANDLE_VALUE) || (dwChangeHandles[0] == NULL))
    {
        OutputDebugString(L"WatchDesktopFolders/PassedUserDesktopFolder Error");
        return 0;
    }
    dwChangeHandles[1] = FindFirstChangeNotification(PassedPublicDesktopFolder, 
                         FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
    if ((dwChangeHandles[1] == INVALID_HANDLE_VALUE) || (dwChangeHandles[1] == NULL))
    {
        OutputDebugString(L"WatchDesktopFolders/PublicDesktopFolder Error");
        return 0;
    }
    return 1;
}

Finally, when the Windows Service receives a notification about a Session Change, it will trigger the WatchTriggerEvent event which will wake up our thread.

An indeterminate amount of time will elapse between a Session Change notification and the completion of the Session Change itself, so there is no guarantee we would find a different logged on user by the time we receive the Session Change notification.

So our thread will sleep for some additional time (arbitrated in 20 seconds) after notification of the Session Change before adjusting the User Desktop Folder path and checking for the presence of desktop.ini icons on the Desktop folders.

Once these operations have been completed, it will sleep again for an INFINITE time, that is, it will wake only when externally notified.

A final note, I have used Visual Studio 2019 to build the C/C++ project, but the code should work without modifications with earlier versions.

History

  • 20th September, 2019 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jose A Pascoa
AtelierWeb Software
Portugal Portugal
Jose Pascoa is the owner of AtelierWeb Software (http://www.atelierweb.com). We produce security and network software and mixed utilities since 1999. The first program I published (in a BBS) was a MS-DOS utility, had the size of 21 KB and was done in Assembly Language. Nowadays, my low level languages are more likely to be "C", "C++" and "Delphi" rather than Assembly Language but I still like it. I have nothing against more fashionable languages like C# and technologies like WPF, actually I have played with them and published software with them.

Comments and Discussions

 
SuggestionIdea can be expanded Pin
docNyie24-Sep-19 22:09
memberdocNyie24-Sep-19 22:09 
PraiseRe: Idea can be expanded Pin
Jose A Pascoa25-Sep-19 5:44
memberJose A Pascoa25-Sep-19 5:44 
GeneralMy vote of 3 Pin
Member 1236439023-Sep-19 21:27
memberMember 1236439023-Sep-19 21:27 
PraiseRe: My vote of 3 Pin
Jose A Pascoa23-Sep-19 21:53
memberJose A Pascoa23-Sep-19 21:53 
QuestionNicely done Pin
DaveBoltman23-Sep-19 5:56
memberDaveBoltman23-Sep-19 5:56 
PraiseRe: Nicely done Pin
Jose A Pascoa23-Sep-19 21:40
memberJose A Pascoa23-Sep-19 21:40 
PraiseWorking! Thanks Pin
Ashok Kumar RV20-Sep-19 19:54
memberAshok Kumar RV20-Sep-19 19:54 
GeneralRe: Working! Thanks Pin
Jose A Pascoa20-Sep-19 23:23
memberJose A Pascoa20-Sep-19 23:23 
QuestionHarsh... Pin
dandy7220-Sep-19 4:33
memberdandy7220-Sep-19 4:33 
PraiseRe: Harsh... Pin
Jose A Pascoa20-Sep-19 6:10
memberJose A Pascoa20-Sep-19 6:10 
GeneralRe: Harsh... Pin
dandy7221-Sep-19 8:10
memberdandy7221-Sep-19 8:10 
PraiseRe: Harsh... Pin
Jose A Pascoa22-Sep-19 2:42
memberJose A Pascoa22-Sep-19 2:42 
GeneralRe: Harsh... Pin
dandy7222-Sep-19 4:48
memberdandy7222-Sep-19 4:48 
PraiseRe: Harsh... Pin
Jose A Pascoa22-Sep-19 21:26
memberJose A Pascoa22-Sep-19 21:26 
GeneralRe: Harsh... Pin
dandy7223-Sep-19 4:59
memberdandy7223-Sep-19 4:59 
PraiseRe: Harsh... Pin
Jose A Pascoa23-Sep-19 21:51
memberJose A Pascoa23-Sep-19 21:51 
GeneralRe: Harsh... Pin
dandy7224-Sep-19 3:39
memberdandy7224-Sep-19 3:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 19 Sep 2019

Stats

4K views
161 downloads
8 bookmarked