TC rearranges my desktop!

#31
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);
 
Likes: MaartenG
#34
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 [email protected] 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 :-)
 
#37
@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
 
#39
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
10,614
97
#40
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.
 
#42
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 [email protected] ([email protected] $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
495
3
#43
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.
 
#44
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
495
3
#45
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.
 
#53
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 18, 2018
29
2
#55
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

Likes: Rod Savard
#56
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.
 
#57
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.
 
#58
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