TC rearranges my desktop!

May 20, 2008
11,048
90
Syracuse, NY, USA
My guess is that the refresh is hardcoded in TCMD (and I would love to know how it's done; For some scripted deployment I need exactly that: a command-line tool to refresh Explorer Desktop+filemanager ..)

Just a guess ... maybe by letting the system know that a change has been made ... something similar to what's done when TCC modifies the system or user environment, perhaps with a different string as LPARAM:

Code:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) L"Environment", SMTO_ABORTIFHUNG, 5000, &dwReturn);

As for simply refreshing the desktop, this is crude, but it works.
Code:
    HWND hWndProgman, hWndShell, hWndFolder;
   if   (
           (hWndProgman = FindWindowEx(GetDesktopWindow(), 0, L"Progman", NULL))   != NULL
       &&   (hWndShell   = FindWindowEx(hWndProgman, 0, L"SHELLDLL_DefView", NULL)) != NULL
       &&   (hWndFolder  = FindWindowEx(hWndShell, 0, L"SysListView32", NULL))      != NULL
       )
   {
       PostMessage(hWndFolder, WM_KEYDOWN, VK_F5, 0);
       PostMessage(hWndFolder, WM_KEYUP, VK_F5, 0);
   }

I also Googled up this. I don't know if it works.
Code:
SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);

The same for this one:
Code:
// Refresh desktop
SHGetSpecialFolderLocation
(hwnd, CSIDL_DESKTOPDIRECTORY, &lpil);
SHChangeNotify( SHCNE_ALLEVENTS, SHCNF_IDLIST, lpil, NULL);
 
  • Like
Reactions: MaartenG
Aug 3, 2016
376
9
Netherlands
Just a guess ... maybe by letting the system know that a change has been made ... something similar to what's done when TCC modifies the system or user environment, perhaps with a different string as LPARAM:

Code:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) L"Environment", SMTO_ABORTIFHUNG, 5000, &dwReturn);

As for simply refreshing the desktop, this is crude, but it works.
Code:
    HWND hWndProgman, hWndShell, hWndFolder;
   if   (
           (hWndProgman = FindWindowEx(GetDesktopWindow(), 0, L"Progman", NULL))   != NULL
       &&   (hWndShell   = FindWindowEx(hWndProgman, 0, L"SHELLDLL_DefView", NULL)) != NULL
       &&   (hWndFolder  = FindWindowEx(hWndShell, 0, L"SysListView32", NULL))      != NULL
       )
   {
       PostMessage(hWndFolder, WM_KEYDOWN, VK_F5, 0);
       PostMessage(hWndFolder, WM_KEYUP, VK_F5, 0);
   }

I also Googled up this. I don't know if it works.
Code:
SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);

The same for this one:
Code:
// Refresh desktop
SHGetSpecialFolderLocation
(hwnd, CSIDL_DESKTOPDIRECTORY, &lpil);
SHChangeNotify( SHCNE_ALLEVENTS, SHCNF_IDLIST, lpil, NULL);

Vince, thank you VERY much!! This is awesome!
(Sorry for going off-topic ....)
I went with SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero), as I'm no programmer and have no way to compile the code (or the knowledge to know how ...)
Tried to test-run it with RunDll32.exe and %@WINAPI[] without any luck (I know I shouldn't abuse those for this).
Eventually I found a small (<10KB) executable: WinApiExec.exe ([title]) to run this:
Code:
winapiexec64.exe shell32.dll@SHChangeNotify 0x8000000 0x1000 0 0

Works like a charm. I'm happy :-)

P.S Your second piece of code make me smile: Find the window and press F5. That IS quick and dirty :-)
 
May 20, 2008
11,048
90
Syracuse, NY, USA
This seems to work.
Code:
echo %@winapi[shell32.dll,SHChangeNotify,%@eval[0x8000000],%@eval[0x1000],0,0] > nul
 
May 20, 2008
11,048
90
Syracuse, NY, USA
@CONVERT doesn't like the "0x" prefix. These seem to work.
Code:
v:\> echo %@winapi[shell32.dll,SHChangeNotify,134217728,4096,0,0] > nul

v:\> echo %@winapi[shell32.dll,SHChangeNotify,%@convert[16,10,8000000],%@convert[16,10,1000],0,0] > nul
 
May 20, 2008
11,048
90
Syracuse, NY, USA
I had it in a plugin quite a while ago, and before that in an EXE. I dug it up, changed it to use SHChangeNotify, and rebuilt it. The 32 bit version works here. Check out ftp://lucky.syr.edu/refreshdt64.zip
 

rconn

Administrator
Staff member
May 14, 2008
12,183
141
Even so, why is TCMD *still* doing a full Explorer refresh when I set DontPrettyPath = 1. At that point all Explorer file view options are the same, yet build 25 still insists on doing the full refresh on startup.

TCMD isn't doing a refresh, Windows is (when calling the SHGetSetSettings API). You'll have to ask Microsoft why they chose to do that.
 
May 26, 2008
537
4
TCMD isn't doing a refresh, Windows is (when calling the SHGetSetSettings API). You'll have to ask Microsoft why they chose to do that.
But none of these Explorer settings are different. TCMD shouldn't be trying to adjust them.
 
Aug 3, 2016
376
9
Netherlands
Thanks Vince! Works perfect! Amazing what can be done with an executable with a size of 3.5 KB :-) Thanks for all the effort!

For others reading this: I needed this to refresh fileassociations. It does not refresh Desktop with new Explorer registry settings like mentioned above ( SuperHidden, DontPrettyPath).
If you want that, the SHGetSetSettings API will do (thanks Rex, for mentioning!).
I managed to get it working with WinApiExec (link is a few posts above):

Code:
start /wait winapiexec64.exe shell32.dll@SHGetSetSettings (shell32.dll@SHGetSetSettings $b:4 0xFFFFFFFF 0) 0xFFFFFFFF 1

It reads the Explorer view-options in some sort of a bit-array and then writes these same settings out again. This triggers a refresh.
(Tested on Win10 x64)

But none of these Explorer settings are different. TCMD shouldn't be trying to adjust them.
That's how ShGetSetSettings work, apparently. An extra check (like: if current settings <> new settings do SHGetSet...) is needed to prevent this.
 
May 26, 2008
537
4
That's how ShGetSetSettings work, apparently. An extra check (like: if current settings <> new settings do SHGetSet...) is needed to prevent this.
From what I understand TCMD is supposed to be doing a check for any differences before attempting to set new values. Past posts by Rex have stated that if you have no differences with ShowHidden, ShowExtensions, etc., then TCMD won't try to update those settings.

Also this seemed to only start with Build 25 for me. Previous builds did not flash my desktop.
 
Aug 3, 2016
376
9
Netherlands
How I understood the discussion so far: TCMD lets SHGetSetSettings write the registry settings (without checking for the current value) and SHGetSetSetting triggers a refresh afterwards, even if the settings are the same.
But it's all just guessing. I don't have access to the code (and would not understand a single bit of it, anyhow), so things might be completely different.

I agree that it would be better to do a difference-check before updating the settings.
 
May 26, 2008
537
4
What it seems like TCMD build 25 is doing is not checking for differences on startup, but it is on exit. If i have differences in ShowHidden/ShowExt/etc it will flash on BOTH startup and exit. But if there are no differences it flashes only on startup.
 
Aug 3, 2016
376
9
Netherlands
It probably is: I can see that on closing TCMD build 25 it is still writing out the regkeys:

Capture.JPG
 
May 18, 2018
31
2
TCMD isn't doing a refresh, Windows is (when calling the SHGetSetSettings API). You'll have to ask Microsoft why they chose to do that.

No, we are asking you why you are choosing to call the SHGetSetSettings API when there is no good reason to do so.
 
  • Like
Reactions: drwtsn32

rconn

Administrator
Staff member
May 14, 2008
12,183
141
No, we are asking you why you are choosing to call the SHGetSetSettings API when there is no good reason to do so.

TCMD does *not* call SHGetSetSettings unless your settings in Explorer are different from your settings in TCMD. Specifically:

fShowSysFiles
fShowSuperHidden
fShowAllObjects
fShowExtensions
 
May 20, 2008
11,048
90
Syracuse, NY, USA
While there's no desktop refresh here (Win7), it's being called at start-up, getting and then setting the values below. I'm not sure why since (I think) I have both Explorer and TCMD showing me as much as possible. And I'm not sure what Explorer or TCMD options they all correspond to.

1527480099506.png


The flags (0x4082b) are

00040000 = SSF_SHOWSUPERHIDDEN
00000800 = SSF_DONTPRETTYPATH
00000020 = SSF_SHOWSYSFILES
00000008 = SSF_SHOWCOMPCOLOR
00000002 = SSF_SHOWEXTENSIONS
00000001 = SSF_SHOWALLOBJECTS
 
May 20, 2008
11,048
90
Syracuse, NY, USA
If SSF_SHOWCOMPCOLOR means show compressed file names in color, that TCMD forces it (even if it's off in Explorer). I couldn't find a TCMD option to control that.
 
May 18, 2018
31
2
TCMD does *not* call SHGetSetSettings unless your settings in Explorer are different from your settings in TCMD. Specifically:

fShowSysFiles
fShowSuperHidden
fShowAllObjects
fShowExtensions

Screenshots of the two sets of settings are attached. As near as I can tell, I have identical settings in TCmd and Explorer, and I still get the flicker in Windows 10.

As a suggestion, why not have a TCmd option that specifies to simply use the existing Explorer settings? As I have argued before, it is improper for an application to be modifying these settings session-wide anyway.
 

Attachments

  • Untitled2.png
    Untitled2.png
    68.2 KB · Views: 120
  • Untitled.png
    Untitled.png
    64.4 KB · Views: 117
  • Like
Reactions: drwtsn32
May 20, 2008
11,048
90
Syracuse, NY, USA
There is something a little odd going on. According to MSDN,
SSF_SHOWALLOBJECTS (0x00000001)
The state of the Hidden files and folders option.

SSF_SHOWSYSFILES (0x00000020)
The state of the Hidden files and folders option. In Windows Vista and later, this is equivalent to SSF_SHOWALLOBJECTS. In versions of Windows before Windows Vista, this value referred to the state of the Do not show hidden files and folders option.

Apparently contradicting that, on my Windows 7 computer, the two are opposites (I seem to have the pre-Vista behavior). This test code produces the output below it. The output is exactly the same whether or not TCMD is running. On my system, I do see system files. I put a 64-bit version of this test (test.exe) in ftp://vefatica.net/shelltest.zip
Code:
int wmain( INT argc, WCHAR **argv )
{
    SHELLSTATE ss = {0};
    DWORD dwMask = SSF_SHOWSUPERHIDDEN | SSF_DONTPRETTYPATH | SSF_SHOWSYSFILES
                    | SSF_SHOWCOMPCOLOR | SSF_SHOWEXTENSIONS | SSF_SHOWALLOBJECTS;
    wprintf(L"dwMask = 0x%8.8x\n", dwMask);
    SHGetSetSettings(&ss, dwMask, FALSE);
    wprintf(L"SHOWSUPERHIDDEN = %d\nDONTPRETTYPATH = %d\nSHOWSYSFILES = %d\n"
            L"SHOWCOMPCOLOR = %d\nSHOWEXTENSIONS = %d\nSHOWALLOBJECTS = %d\n",
            ss.fShowSuperHidden    ? 1 : 0,    ss.fDontPrettyPath    ? 1 : 0,    ss.fShowSysFiles    ? 1 : 0,
            ss.fShowCompColor    ? 1 : 0,    ss.fShowExtensions    ? 1 : 0,    ss.fShowAllObjects    ? 1 : 0);

    return 0;
}
Code:
dwMask = 0x0004082b
SHOWSUPERHIDDEN = 1
DONTPRETTYPATH = 1
SHOWSYSFILES = 0
SHOWCOMPCOLOR = 1
SHOWEXTENSIONS = 1
SHOWALLOBJECTS = 1

But TCMD is trying to make a change with SHGetSetSettings. The first byte of SHGetSetSettings's SHELLSTATE structure is a bitfield:
Code:
BOOL  fShowAllObjects  :1;
BOOL  fShowExtensions  :1;
BOOL  fNoConfirmRecycle  :1;
BOOL  fShowSysFiles  :1;
BOOL  fShowCompColor  :1;
BOOL  fDoubleClickInWebView  :1;
BOOL  fDesktopHTML  :1;
BOOL  fWin95Classic  :1;

TCMD tries to change that byte from 0x13 to 0x1b. Given the MSDN remarks, perhaps TCMD might simply ignore SSF_SHOWSYSFILES.
 
May 20, 2008
11,048
90
Syracuse, NY, USA
SHOWALLOBJECTS seems to be the one that rules. For any of the four combinations of values for SHELLSTATE::fShowSysFiles and SHELLSTATE::fShowAllObjects, what you see is determined by the value of SHELLSTATE::fShowAllObjects.
 
May 20, 2008
11,048
90
Syracuse, NY, USA
It gets even more interesting (bizarre) than that. Below is a code snippet. In it, for each possible value of fShowSysFiles and fShowAllObjects, I
1. set those settings
2. get and print those settings (they are as I set them)
3. sleep 1 second
4. get and print those settinge again

In the two cases where fShowSysFiles and fShowAllObjects were given the same value, after the delay, Windows (7) has changed fShowSysFiles to be the opposite of fShowAllObjects. Here's the code.
Code:
    SHELLSTATE ss;
    DWORD dwMask = SSF_SHOWSYSFILES | SSF_SHOWALLOBJECTS;

    for ( BOOL i=0; i<2; i++ )
    {
        for ( BOOL j=0; j<2; j++ )
        {
            // set them both
            RtlZeroMemory(&ss, sizeof(SHELLSTATE));
            ss.fShowSysFiles = i;
            ss.fShowAllObjects = j;
            SHGetSetSettings(&ss, dwMask, TRUE);

            for ( INT k=0; k<2; k++ )
            {
                // get them both
                RtlZeroMemory(&ss, sizeof(SHELLSTATE));
                SHGetSetSettings(&ss, dwMask, FALSE);
    
                // see them
                wprintf(L"SHOWSYSFILES = %s\nSHOWALLOBJECTS = %s\n\n",
                    ss.fShowSysFiles    ? L"TRUE" : L"FALSE", ss.fShowAllObjects ? L"TRUE" : L"FALSE");

                // wait a bit
                Sleep(1000);
            }

            wprintf(L"-------------------------------------------\n\n");
        }
    }

And here's the output.
Code:
SHOWSYSFILES = FALSE
SHOWALLOBJECTS = FALSE

SHOWSYSFILES = TRUE
SHOWALLOBJECTS = FALSE

-------------------------------------------

SHOWSYSFILES = FALSE
SHOWALLOBJECTS = TRUE

SHOWSYSFILES = FALSE
SHOWALLOBJECTS = TRUE

-------------------------------------------

SHOWSYSFILES = TRUE
SHOWALLOBJECTS = FALSE

SHOWSYSFILES = TRUE
SHOWALLOBJECTS = FALSE

-------------------------------------------

SHOWSYSFILES = TRUE
SHOWALLOBJECTS = TRUE

SHOWSYSFILES = FALSE
SHOWALLOBJECTS = TRUE