0

I want to create an application that allows users to have different icons on different Windows 10 desktops. I can deal with the switching of virtual desktops using the Window Station and Desktop functions and the Virtual Desktop Shell Interface. So now I know how to detect that a desktop has been switched and I need to change the location of the Desktop User Folder as fast as possible.

I know there are two ways of doing this in the User Interface:

A) Via User Folders' properties

  1. Open %HomePath%/Desktop in Explorer
  2. Right click on background, open Properties
  3. In tab Location type in the new path
  4. Hit OK and then No (as you don't want to move the files)
  5. Sometimes needed: click on desktop, press F2

.

B) Via Registry

  1. Change Desktop in HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders to the new address
  2. Relog

.

The B option would be ideal for a programmatical solution if it wasn't for that relog part. Which is, as you can certainly understand, a bit of a dealbreaker.

m93a
  • 8,866
  • 9
  • 40
  • 58
  • 1
    Color me finicky, but the **real** deal breaker with your second option is, that it relies on undocumented implementation details, that can break at any time, without prior notice. Make sure you read [the long and sad story of the Shell Folders key](https://blogs.msdn.microsoft.com/oldnewthing/20031103-00/?p=41973). – IInspectable Dec 10 '16 at 16:14
  • 2
    [SHSetKnownFolderPath](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762249.aspx) is the official interface to redirect a known folder (like the Desktop) to a new location. @CodyGray: If more than a decade of [The Old New Thing](https://blogs.msdn.microsoft.com/oldnewthing/) didn't succeed in explaining the difference between contracts and implementation details (and why relying on the latter is dangerous), I don't know what possibly can. – IInspectable Dec 11 '16 at 18:51

1 Answers1

0

The VB.Net code below will save all open desktop folder locations and sizes in the Windows registry and restore them on demand. Run from the command line or a batch file. To save folder attributes add a parameter of 'Set' (no quotes), to restore, no parameter.

Option Explicit On
Imports System.Text
Public Class FixFolders
   Structure RECT
      Dim Left As Integer
      Dim Top As Integer
      Dim Right As Integer
      Dim Bottom As Integer
   End Structure
   Structure POINTAPI
      Dim x As Integer
      Dim y As Integer
   End Structure
   Structure WINDOWPLACEMENT
      Dim length As Integer
      Dim flags As Integer
      Dim showCmd As Integer
      Dim ptMinPosition As POINTAPI
      Dim ptMaxPosition As POINTAPI
      Dim rcNormalPosition As RECT
   End Structure
   Private Structure TDeskTopWindow
      Dim lhwnd As Integer
      Dim WinTitle As String
      Dim WinRect As RECT
   End Structure

   Private Declare Function GetWindowRect Lib "user32" Alias "GetWindowRect" (ByVal hwnd As Integer, ByRef lpRect As RECT) As Integer
   Declare Function MoveWindow Lib "user32" (ByVal hwnd As Int32, ByVal x As Int32, ByVal y As Int32, ByVal nWidth As Int32, ByVal nHeight As Int32, ByVal bRepaint As Int32) As Int32
   Declare Function GetDesktopWindow Lib "user32" () As Int32
   Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Int32, ByVal lpClassName As String, ByVal nMaxCount As Int32) As Int32
   Private Delegate Function EnumChildWindowsCallback(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Boolean
   Private Declare Function EnumChildWindows Lib "user32" (ByVal hWnd As IntPtr, ByVal lpEnumFunc As EnumChildWindowsCallback, ByVal lParam As IntPtr) As Boolean
   Private Delegate Function EnumWindowsProcDelegate(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Boolean
   Private Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As EnumWindowsProcDelegate, ByVal lParam As IntPtr) As Boolean
   Private Declare Auto Function GetWindowText Lib "user32" (ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
   Private Declare Function SetFocusAPI Lib "user32" Alias "SetFocus" (ByVal hwnd As Int32) As Int32
   Private Declare Function SetWindowPlacement Lib "user32" (ByVal hwnd As Integer, ByRef lpwndpl As WINDOWPLACEMENT) As Integer
   Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwbd As Int32) As Int32
   'Private Declare Function FlashWindow Lib "User32" (ByVal hWnd As Int32, ByVal Invert As Int32) As Int32
   Private Declare Function SetActiveWindow Lib "User32" (ByVal hWnd As Int32) As Int32

   Private Shared lpwndplNew As WINDOWPLACEMENT
   Private Const SW_SHOWMINIMIZED As Short = 2
   Private Const SW_SHOWMAXIMIZED As Short = 3
   Private Const SW_SHOWNORMAL As Short = 1

   Private Shared Mode As String

   Private WindowArray() As String
   Private Shared DeskTopWindows() As TDeskTopWindow, DTWinIndex As Integer

   Public Shared Sub Main()
      Dim left, Right, Top, Bottom As Integer
      Dim DTWinIndex As Integer
      Dim RetVal1, RetVal2, RetVal3, TimeOut As Integer

      Mode = Command()
      GetDeskTopFolderWindows()

      For DTWinIndex = 0 To DeskTopWindows.GetUpperBound(0)
         Select Case Mode
            Case ""
               With DeskTopWindows(DTWinIndex)
                  'If it' s not there, put it in
                  If GetSetting("FixFolders", .WinTitle, "Left") = "" Then
                     SaveSetting("FixFolders", .WinTitle, "Left", .WinRect.Left.ToString)
                     SaveSetting("FixFolders", .WinTitle, "Right", .WinRect.Right.ToString)
                     SaveSetting("FixFolders", .WinTitle, "Top", .WinRect.Top.ToString)
                     SaveSetting("FixFolders", .WinTitle, "Bottom", .WinRect.Bottom.ToString)
                  End If

                  left = Val(GetSetting("FixFolders", .WinTitle, "Left"))
                  Right = Val(GetSetting("FixFolders", .WinTitle, "Right"))
                  Top = Val(GetSetting("FixFolders", .WinTitle, "Top"))
                  Bottom = Val(GetSetting("FixFolders", .WinTitle, "Bottom"))
                  While .WinRect.Bottom <> Bottom Or .WinRect.Left <> left Or .WinRect.Right <> Right Or .WinRect.Top <> Top
                     'RetVal1 = SetForegroundWindow(lhWnd)
                     RetVal2 = SetWindowPlacement(.lhwnd, lpwndplNew)          'This 'restores' the window if minimized
                     RetVal3 = MoveWindow(.lhwnd, left, Top, Right - left, Bottom - Top, True)
                     ' Log.WriteLine(Now.TimeOfDay.ToString.Substring(0, 8) & " Set " & .WinTitle)
                     RetVal1 = GetWindowRect(.lhwnd, .WinRect)  ' get current size
                     TimeOut += 1
                     If TimeOut > 1 And TimeOut < 10 Then Threading.Thread.Sleep(1000)
                  End While
               End With
            Case "Set"
               With DeskTopWindows(DTWinIndex)
                  SaveSetting("FixFolders", .WinTitle, "Left", .WinRect.Left.ToString)
                  SaveSetting("FixFolders", .WinTitle, "Right", .WinRect.Right.ToString)
                  SaveSetting("FixFolders", .WinTitle, "Top", .WinRect.Top.ToString)
                  SaveSetting("FixFolders", .WinTitle, "Bottom", .WinRect.Bottom.ToString)
               End With
         End Select
      Next
   End Sub

   Private Shared Sub GetDeskTopFolderWindows()
      Dim lhwnd As Integer, lParam As IntPtr
      DTWinIndex = -1
      lhwnd = GetDesktopWindow()  ' Find the Desktop's Child Windows
      EnumChildWindows(lhwnd, AddressOf EnumChildProc, lParam)
   End Sub
   Shared Function EnumChildProc(ByVal lhWnd As IntPtr, ByVal lParam As IntPtr) As Boolean
      Dim RetVal1 As Int32
      Dim WinClassBuf As String
      Dim WinTitleBuf As New StringBuilder(256)
      Dim WinClass As String
      Dim WinRect As RECT

      WinClassBuf = New String(Chr(0), 256)
      WinTitleBuf.Append(Chr(0), 256)
      RetVal1 = GetClassName(lhWnd, WinClassBuf, WinClassBuf.Length)
      WinClass = WinClassBuf.ToString
      WinClass = StripNulls(WinClassBuf)  ' remove extra Nulls & spaces
      If WinClass = "CabinetWClass" Or WinClass = "ExploreWClass" Then   ' TextBox Window
         DTWinIndex += 1
         ReDim Preserve DeskTopWindows(DTWinIndex)
         DeskTopWindows(DTWinIndex).lhwnd = lhWnd
         GetWindowText(lhWnd, WinTitleBuf, WinTitleBuf.Capacity)
         DeskTopWindows(DTWinIndex).WinTitle = WinTitleBuf.ToString
         RetVal1 = GetWindowRect(lhWnd, WinRect)  ' get current size
         DeskTopWindows(DTWinIndex).WinRect = WinRect
      End If
      EnumChildProc = True
   End Function

   Public Shared Function StripNulls(ByVal OriginalStr As String) As String
      ' This removes the extra Nulls so String comparisons will work
      If (InStr(OriginalStr, Chr(0)) > 0) Then
         'OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
         OriginalStr = OriginalStr.Substring(0, OriginalStr.IndexOf(Chr(0)))
      End If
      StripNulls = OriginalStr
   End Function
End Class
  • **Welcome to StackOverflow!** Could you please explain more thoroughly what does the code do? Does it save information about the *open windows*? Although this is definitely cool, it's not what I need – in the question I'm talking about the "desktop folder" (the one where the icons on your desktop live) and changing its destination (so that different icons appear on your desktop). – m93a Jun 23 '17 at 08:20