0

The answers I've found link to fHideIcon, but I get a 404 error on Microsoft's page for those links.

I've also tried:

SHELLSTATE ss;
SecureZeroMemory(&ss, sizeof(ss));
SHGetSetSettings(&ss, SSF_HIDEICONS, TRUE);

But that didn't work.
Does anyone know how to do this?

zx485
  • 28,498
  • 28
  • 50
  • 59
Adam
  • 63
  • 8
  • MS sure doesn't maintain its winapi C++ documentation like it used to. – Christopher Pisz Nov 14 '18 at 23:38
  • @Christopher Was it good before? – Adam Nov 14 '18 at 23:43
  • It was 'better' from 1998-2003. At least the links worked. – Christopher Pisz Nov 14 '18 at 23:49
  • MSDN have been migrating its documentation from `msdn.microsoft.com` to a new format on `learn.microsoft.com` for some time now, and a lot of things are falling through the cracks (broken links, incomplete text, etc). It is horrible. IMHO, there was nothing wrong with the old documentation, they should have just left it alone. – Remy Lebeau Nov 15 '18 at 01:58
  • @rem: While I agree that the current state of the documentation is a pretty significant regression, there *is* actually a reason for moving it. Once complete, we will finally have a way to submit and *publicly track* documentation defects. – IInspectable Nov 15 '18 at 09:27
  • @IInspectable Yes, and that system works (for topics that have been migrated). Something I flagged a wile back turned up on Github. – Paul Sanders Nov 15 '18 at 17:00

3 Answers3

5

The following approach which uses the official shell API is a little bit involved, but works even under Windows 10.

Steps:

  1. Get the IFolderView2 interface of the desktop (supported since Windows Vista).
  2. Call IFolderView2::SetCurrentFolderFlags() with FWF_NOICONS for both the dwMask and dwFlags parameters.

The effect of the flag is visible immediately. There is no need to restart the computer nor "explorer.exe". The flag also persists after logoff or reboot.

The tricky thing is step 1). Raymond Chen shows C++ code for that in his article "Manipulating the positions of desktop icons", specifically in his FindDesktopFolderView() function.

Here is a full example in form of a console application. It is based on Raymond Chen's code. The program toggles the visibility of the desktop icons each time it is run.

The code has been tested under Windows 10 Version 1803.

"Library" code:

#include <ShlObj.h>     // Shell API
#include <atlcomcli.h>  // CComPtr & Co.
#include <string> 
#include <iostream> 
#include <system_error>

// Throw a std::system_error if the HRESULT indicates failure.
template< typename T >
void ThrowIfFailed( HRESULT hr, T&& msg )
{
    if( FAILED( hr ) )
        throw std::system_error{ hr, std::system_category(), std::forward<T>( msg ) };
}

// RAII wrapper to initialize/uninitialize COM
struct CComInit
{
    CComInit() { ThrowIfFailed( ::CoInitialize( nullptr ), "CoInitialize failed" ); }
    ~CComInit() { ::CoUninitialize(); }
    CComInit( CComInit const& ) = delete;
    CComInit& operator=( CComInit const& ) = delete;
};

// Query an interface from the desktop shell view.
void FindDesktopFolderView( REFIID riid, void **ppv, std::string const& interfaceName )
{
    CComPtr<IShellWindows> spShellWindows;
    ThrowIfFailed( 
        spShellWindows.CoCreateInstance( CLSID_ShellWindows ),
        "Failed to create IShellWindows instance" );

    CComVariant vtLoc( CSIDL_DESKTOP );
    CComVariant vtEmpty;
    long lhwnd;
    CComPtr<IDispatch> spdisp;
    ThrowIfFailed( 
        spShellWindows->FindWindowSW(
            &vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp ),
        "Failed to find desktop window" );

    CComQIPtr<IServiceProvider> spProv( spdisp );
    if( ! spProv )
        ThrowIfFailed( E_NOINTERFACE, "Failed to get IServiceProvider interface for desktop" );

    CComPtr<IShellBrowser> spBrowser;
    ThrowIfFailed( 
        spProv->QueryService( SID_STopLevelBrowser, IID_PPV_ARGS( &spBrowser ) ),
        "Failed to get IShellBrowser for desktop" );

    CComPtr<IShellView> spView;
    ThrowIfFailed( 
        spBrowser->QueryActiveShellView( &spView ),
        "Failed to query IShellView for desktop" );

    ThrowIfFailed( 
        spView->QueryInterface( riid, ppv ),
        "Could not query desktop IShellView for interface " + interfaceName );
}

Example to toggle desktop icons using the above code:

void ToggleDesktopIcons()
{
    CComPtr<IFolderView2> spView;
    FindDesktopFolderView( IID_PPV_ARGS(&spView), "IFolderView2" );

    DWORD flags = 0;
    ThrowIfFailed( 
        spView->GetCurrentFolderFlags( &flags ), 
        "GetCurrentFolderFlags failed" );
    ThrowIfFailed( 
        spView->SetCurrentFolderFlags( FWF_NOICONS, flags ^ FWF_NOICONS ),
        "SetCurrentFolderFlags failed" );
}

int wmain(int argc, wchar_t **argv)
{
    try
    {
        CComInit init;

        ToggleDesktopIcons();

        std::cout << "Desktop icons have been toggled.\n";
    }
    catch( std::system_error const& e )
    {
        std::cout << "ERROR: " << e.what() << ", error code: " << e.code() << "\n";
        return 1;
    }

    return 0;
}
zett42
  • 25,437
  • 3
  • 35
  • 72
2

The third parameter isn't about changing the setting, it's to select the SHGetSetSettings() behavior.

FALSE will get the value of the current setting and store it in ss, TRUE will set the value of the setting to what is in ss.

So basically you have to do ss.fHideIcons = TRUE and then call SHGetSetSettings(&ss, SSF_HIDEICONS, TRUE) to set it.

I know, it's weird, but on the other hand it allows you to change multiple settings simultaneously because SSF_* is a bitmask.

Havenard
  • 27,022
  • 5
  • 36
  • 62
  • Except that doesn't work... I think `ss.fHideIcons` has a different meaning to what the OP is hoping for – Paul Sanders Nov 14 '18 at 23:50
  • @PaulSanders Well hopefully you're doing `ss.fHideIcons = TRUE` *after* zeroing the memory, otherwise it's work undone. – Havenard Nov 14 '18 at 23:52
  • @PaulSanders Well... yeah, I just tested here and, my icons blink but that's about it. Funny thing they only blink when you actually change the setting to a value different to what it was before, regardless if I'm trying to hide or show the icons. It won't show icons either if I manually hide them on the Windows Explorer context menu. I don't know, maybe it's a Windows 10 bug? What Windows version are you using? – Havenard Nov 15 '18 at 00:09
  • I tried this and unfortunately, it doesn't work. @peter's answer worked though. – Adam Nov 15 '18 at 00:12
  • There are other ways of hiding desktop icons though, by hiding objects within the Windows Explorer's Window Object. You can actually customize a lot of things doing this, without actually changing any setting or modifying any file which is pretty cool too. – Havenard Nov 15 '18 at 00:12
  • @Havenard I am using Windows 10. Having run a few tests, `ss.fHideIcons` has no discernable effect. Maybe it used to but not now. I have posted some code that I dug up from elsehere on SO that works and seems pretty clean. – Paul Sanders Nov 15 '18 at 00:16
  • @PaulSanders Yeah that method is pretty old school, I'm surprised it still works. – Havenard Nov 15 '18 at 00:19
  • 1
    @Havenard Oh well, you know MS: backwards compatibility is king. – Paul Sanders Nov 15 '18 at 00:20
  • @Havenard Tried this on Windows 7. The `fHideIcons` value is preserved across multiple calls, as expected, but there is simply no visual effect on the display of the desktop icons. What is odd is that when right-clicking on the Desktop, the `View > Show desktop icons` menu item IS being toggled in accordance with `fHideIcons` (the menu item itself works fine, it actually toggles the display of icons when clicked). So the issue is `SHGetSetSettings()` is apparantly not doing everything the menu item does. – Remy Lebeau Nov 15 '18 at 02:27
  • @RemyLebeau True, it does change the setting, but it doesn't take effect immediately. I guess it's a bug after all. Verified on Windows 10. – Havenard Nov 15 '18 at 02:37
  • @Havenard Searching online, I see this is a very well-known issue going back several years. Seems `fHideIcons` merely toggles the `HideIcons` value in the `HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced` Registry key (verified on my Win7), nothing more. Hiding the `Progman` window is the most common workaround (though it has a side effect that you can no longer right-click on the desktop). – Remy Lebeau Nov 15 '18 at 02:47
  • *"I'm surprised it still works"* - It doesn't, and the [documentation](https://learn.microsoft.com/en-us/windows/desktop/api/shlobj_core/nf-shlobj_core-shgetsetsettings) says as much: *"`SHGetSetSettings` is available for use in the operating systems specified in the Requirements section. **It may be altered or unavailable in subsequent versions.**"* This API is supported in Windows XP/Server 2003 only. – IInspectable Nov 15 '18 at 09:44
  • @IInspectable I meant the `FindWindow` method. And no, the API is verified to be supported in Windows 7 and Windows 10 too. – Havenard Nov 15 '18 at 15:39
  • You cannot verify whether any given API is supported. That's something you can only determine by looking at the documentation. – IInspectable Nov 15 '18 at 15:59
  • @IInspectable The API exists in the system and you could say it does what it says it does, it just takes a reload of the Windows Explorer to take effect. In addition the docs say the *minimum* supported system is Windows XP/2003, and that it is supported in User32.dll 5.0 **and later** (5.0 was the one at Windows XP/2003). There is no mention about it being discontinued. – Havenard Nov 15 '18 at 16:32
  • In other words: The API doesn't do what the contract promises. That's what an unsupported API is. The documentation reads *"Minimum supported client/server"*, because there's only a single template for the entire documentation. – IInspectable Nov 15 '18 at 20:55
-1

The following seems to work (adapted from https://stackoverflow.com/a/6403014/5743288):

#include <windows.h>

int main ()
{
    HWND hProgman = FindWindowW (L"Progman", L"Program Manager");
    HWND hChild = GetWindow (hProgman, GW_CHILD);
    ShowWindow (hChild, SW_HIDE);
    Sleep (2000);
    ShowWindow (hChild, SW_SHOW);
}

Please note: this approach is not supported by Microsoft and disables right-clicking on ths desktop.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • @peter Thanks! Yeah I found that link, but I couldn't figure out how to make it work in C++. Can you possibly explain how this works? I don't understand it. – Adam Nov 15 '18 at 00:12
  • @Adam Yeah that's precisely the method I was talking about, I was just unsure if it worked across different versions of Windows. Geez I remember doing this on Windows 98... – Havenard Nov 15 '18 at 00:13
  • @adam As the post I linked to explains, it hides the parent window that hosts all the desktop icons. Hopefully, MS won't break this. – Paul Sanders Nov 15 '18 at 00:18
  • 1
    The Shell has a public API. Don't go about handing out solutions based on implementation details, without **clearly** marking them as unsupported. – IInspectable Nov 15 '18 at 09:22
  • @IInspectable Neither I nor the OP can find a public API to do what the OP wants. Please feel free to correct me if you know better. Added a note to my post as per your request. – Paul Sanders Nov 15 '18 at 16:05
  • @PaulSanders `SHGetSetSettings(SSF_HIDEICONS)` is the *public* API for this. It. Is just a *broken* API at this point. – Remy Lebeau Nov 15 '18 at 16:44
  • @RemyLebeau Hmmm. Well, given that it is deprecated and that the documentation is 90% missing anyway, it seems that is unlikely to change. – Paul Sanders Nov 15 '18 at 16:52
  • 1
    May also want to note, that this loses the ability to right-click on the desktop. – IInspectable Nov 15 '18 at 20:58
  • @IInspectable Done – Paul Sanders Nov 15 '18 at 21:11