This PC Modifications

This PC Modifications

Recently one of our guests commented on the Navigation Pane in regards to removing the 3D Objects sub-directory, or renaming it for the purpose of storing personal files in.

For those of you familiar with Windows over the years, you will realize that what once was named My Computer was changed to be named Computer then in Windows 8 we saw a new name, that being This PC which Windows 10 also has.

The default sub-directories for This PC are:
  • Desktop
  • Documents
  • Downloads
  • Music
  • Pictures
  • Videos
Also, in a recent Feature update Microsoft added one additional sub-directory named 3D Objects.

At the time of writing this Article, there is no simple way to add your own sub-directories to This PC's list, as Microsoft had no intention of allowing this. Modifications will be made if a New Feature is added that requires an anchored shortcut, but other than that you are not supposed to play with this section of the Navigation Pane in File Explorer.

With regards to the Guest who wondered could they rename the 3D Objects sub-directory and use that folder to store their files. The answer is yes/no/not really.
  • Yes, you could modify and rename the folder
  • Yes you could place files into the folder
  • However, this folder is here for Windows 10 3D applications like 3D Paint to store saved work etcetera, so its not really a good idea to try to use this sub-directory for anything else.

That brings us to the question, 'Can we add our own sub-directory to the This PC list'?
  • Yes we can, but the process if done manually is rather lengthy and for some Users may be too complicated to bother trying as they will need to get information from their Registry, modify that information, then merge it into their Registry, then having done all that, create a folder, and finally, update the registry entry so as to point to their new sub-directory folder.

One of our regular members, Norton, responded to this Guests question and provided a link to an interesting article by a former MVP for Microsoft Desktop environments, Ramesh Srinivasan. You can see that article here:

Having taken a look at the article I came to the conclusion it would be far easier for the average User to have a simple application to run, which does all the Registry and Directory creation for the User after they enter three pieces of information, then click a button.

Below is the PowerShell Script I've written, with full credits to those others out there for their ideas which has helped in creating this application.

Note: This is Version 1.0 [If you decide to try it, please post feedback for improvements or problems you have using the application].

The Code is commented for anyone interested in learning how it works.

Code:
<#
    __          ___           _                  __  ___  ______                                    
    \ \        / (_)         | |                /_ |/ _ \|  ____|                                    
     \ \  /\  / / _ _ __   __| | _____      _____| | | | | |__ ___  _ __ _   _ _ __ ___  ___   ___ ___  _ __ ___
      \ \/  \/ / | | '_ \ / _` |/ _ \ \ /\ / / __| | | | |  __/ _ \| '__| | | | '_ ` _ \/ __| / __/ _ \| '_ ` _ \
       \  /\  /  | | | | | (_| | (_) \ V  V /\__ \ | |_| | | | (_) | |  | |_| | | | | | \__ \| (_| (_) | | | | | |
        \/  \/   |_|_| |_|\__,_|\___/ \_/\_/ |___/_|\___/|_|  \___/|_|   \__,_|_| |_| |_|___(_)___\___/|_| |_| |_|

   PowerShell Script Repository: https://www.windows10forums.com/articles/this-pc-modifications.82/
   Author: Regedit32
   Date: 15 December 2017
   Script: ThisPCModifier — Version 1.0

   CREDIT:  Chrissy LeMaire, MCC, MVP — https://gallery.technet.microsoft.com/scriptcenter/Export-Icon-from-DLL-and-9d309047
   CREDIT:  Thomas Levesque and darkfall
   CREDIT:  Ramesh Srinivasan — http://www.winhelponline.com/blog/add-custom-folder-this-pc-navigation-pane-windows/
   CREDIT:  Norton for pointing out Ramesh's Article
   CREDIT:  David Corrale — https://info.sapien.com/index.php/guis/gui-controls/spotlight-on-the-imagelist-control
#>

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.IO

Function ConfirmUserIsAdministrator {
  [CmdletBinding()]
  Param()
  ${CmdletName} = $Pscmdlet.MyInvocation.MyCommand.Name
  ${Identity} = [System.Security.Principal.WindowsIdentity]::GetCurrent()
  ${Principal} = new-object System.Security.Principal.WindowsPrincipal(${Identity})
  ${IsAdmin} = $Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)

  if (-not ${IsAdmin}) {
    Write-Error -Message "${CmdletName}: User is not an administrator. To continue run application as administrator." `
                -RecommendedAction "Run application as administrator"
    Break      
  }
}
ConfirmUserIsAdministrator

Function FormOpened {
  Return $true
}

Function ThisPCSubdirectory {
  New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT

  # Variables to store User input
  [String] $newSubdirectoryName = $txtBox1.Text
  [String] $description = $txtBox2.Text
  [String] $iconValue = $iconsView.SelectedIndices

  # Obtain and store a unique guid
  [String] $uniqueGUID = [guid]::NewGuid().ToString()

  <# ### 1 ### #>
  New-Item -Path "HKCR:\CLSID\" -Name "{$uniqueGUID}" -ItemType Directory -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}" -Name "(Default)" -PropertyType String -Value "(value not set)" -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}" -Name "DescriptionID" -PropertyType DWord -Value 0x00000003 -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}" -Name "Infotip" -PropertyType ExpandString -Value "@%SystemRoot%\system32\shell32.dll,-12690" -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}" -Name "System.IsPinnedToNameSpaceTree" -PropertyType DWord -Value 0x00000001 -Force | Out-Null

  New-Item -Path "HKCR:\CLSID\{$uniqueGUID}\" -Name "DefaultIcon" -ItemType Directory -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\DefaultIcon" -Name "(Default)" -PropertyType ExpandString -Value "%SystemRoot%\System32\imageres.dll,-189" -Force | Out-Null


  New-Item -Path "HKCR:\CLSID\{$uniqueGUID}\" -Name "InProcServer32" -ItemType Directory -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\InProcServer32" -Name "(Default)" -PropertyType ExpandString -Value "%SystemRoot%\System32\shell32.dll" -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\InProcServer32" -Name "ThreadingModel" -PropertyType String -Value "Both" -Force | Out-Null

  New-Item -Path "HKCR:\CLSID\{$uniqueGUID}\" -Name "Instance" -ItemType Directory -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\Instance" -Name "CLSID" -PropertyType String -Value "{0E5AAE11-A475-4c5b-AB00-C66DE400274E}" -Force | Out-Null

  New-Item -Path "HKCR:\CLSID\{$uniqueGUID}\Instance\" -Name "InitPropertyBag" -ItemType Directory -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\Instance\InitPropertyBag" -Name "Attributes" -PropertyType DWord -Value 0x00000011 -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\Instance\InitPropertyBag" -Name "TargetKnownFolder" -PropertyType String -Value "{35286a68-3c57-41a1-bbb1-0eae73d76c95}" -Force | Out-Null

  New-Item -Path "HKCR:\CLSID\{$uniqueGUID}\" -Name "ShellFolder" -ItemType Directory -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\ShellFolder" -Name "Attributes" -PropertyType DWord -Value 0xf080004d -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\ShellFolder" -Name "FolderValueFlags" -PropertyType DWord -Value 0x00000029 -Force | Out-Null
  New-ItemProperty -Path "HKCR:\CLSID\{$uniqueGUID}\ShellFolder" -Name "SortOrderIndex" -PropertyType DWord -Value 0x00000000 -Force | Out-Null

  New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\" -Name "{$uniqueGUID}" -ItemType Directory -Force | Out-Null

  # Create directory to point to if one does not already exist
  if ($storageDir.length -eq 0) {
      $storageDir = "$env:HomeDrive\$newSubdirectoryName"
  }
  # Check whether directory exists.  If not create it.
  if ((Test-Path $storageDir) -eq $false) {
    try {
      New-Item -Type Directory $storageDir | Out-Null
    }
    catch {
      throw "Can't create $storageDir"
    }
  }
  UpdateRegistry
}

Function UpdateRegistry {
  if ((Test-Path "HKCR:\CLSID\{$uniqueGUID}") -eq $true) {
    try {
      # Update Registry with User defined parameters
      $regkey = "HKCR:\CLSID\{$uniqueGUID}"
      Set-ItemProperty -Path $regkey -Name "(Default)" -Value $newSubdirectoryName -Force | Out-Null
      Set-ItemProperty -Path $regkey -Name "Infotip" -Value "$description" -Force | Out-Null
      Set-ItemProperty -Path "$regkey\DefaultIcon" -Name "(Default)" -Value "%SystemRoot%\System32\imageres.dll,$iconValue" -Force | Out-Null
      Remove-ItemProperty -Path "$regkey\Instance\InitPropertyBag" -Name "TargetKnownFolder" -Force | Out-Null
      New-ItemProperty -Path "$regkey\Instance\InitPropertyBag" -Name "TargetFolderPath" -PropertyType String -Value "$env:HomeDrive\$newSubdirectoryName" -Force | Out-Null
    }
    catch {
      throw "HKCR:\CLSID\{$uniqueGUID} does not exist!"
    }
  }
  Cleanup
}

# Clean up drive of temporary files and exit application
Function Cleanup {
  # Close PSDrive
  Remove-PSDrive -Name HKCR

  # Remove temporary directory of extracted icons
  Remove-Item -Path "$env:UserProfile\AppData\Local\Temp\Icons" -ErrorAction SilentlyContinue -Recurse

  # Close Form
  $form.Close()
}

# Cleanup if User closes Form
Function UserCancelCleanup {
  # Remove temporary directory of extracted icons
  rmdir -Path "$env:UserProfile\AppData\Local\Temp\Icons" -Recurse
  $form.Close()
}

# Extract icons from imageres.dll
Function GetIcons {
  [CmdletBinding()]
  Param(
    [Parameter(Mandatory=$true)]
    [string]$Path,
    [string]$Directory,
    [ValidateSet(32)]
    [int]$Size = 32,
    [ValidateSet("ico","bmp","png")]
    [string]$Type = "ico"
  )

  Begin {
    $code = '
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.IO;

    namespace System {
      public class IconExtractor {
        public static Icon Extract(string file, int number, bool largeIcon) {
          IntPtr large;
          IntPtr small;
          ExtractIconEx(file, number, out large, out small, 1);
          try  { return Icon.FromHandle(largeIcon ? large : small); }
          catch  { return null; }
        }
        [DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
      }
    }

    public class PngIconConverter {
      public static bool Convert(System.IO.Stream input_stream, System.IO.Stream output_stream, int size, bool keep_aspect_ratio = false) {
        System.Drawing.Bitmap input_bit = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(input_stream);
          if (input_bit != null) {
            int width, height;
            if (keep_aspect_ratio) {
              width = size;
              height = input_bit.Height / input_bit.Width * size;
            }
            else {
              width = height = size;
            }
            System.Drawing.Bitmap new_bit = new System.Drawing.Bitmap(input_bit, new System.Drawing.Size(width, height));
            if (new_bit != null) {
              System.IO.MemoryStream mem_data = new System.IO.MemoryStream();
              new_bit.Save(mem_data, System.Drawing.Imaging.ImageFormat.Png);

              System.IO.BinaryWriter icon_writer = new System.IO.BinaryWriter(output_stream);
              if (output_stream != null && icon_writer != null) {
                icon_writer.Write((byte)0);
                icon_writer.Write((byte)0);
                icon_writer.Write((short)1);
                icon_writer.Write((short)1);
                icon_writer.Write((byte)width);
                icon_writer.Write((byte)height);
                icon_writer.Write((byte)0);
                icon_writer.Write((byte)0);
                icon_writer.Write((short)0);
                icon_writer.Write((short)32);
                icon_writer.Write((int)mem_data.Length);
                icon_writer.Write((int)(6 + 16));
                icon_writer.Write(mem_data.ToArray());
                icon_writer.Flush();
                return true;
              }
            }
            return false;
          }
          return false;
        }

        public static bool Convert(string input_image, string output_icon, int size, bool keep_aspect_ratio = false) {
          System.IO.FileStream input_stream = new System.IO.FileStream(input_image, System.IO.FileMode.Open);
          System.IO.FileStream output_stream = new System.IO.FileStream(output_icon, System.IO.FileMode.OpenOrCreate);

          bool result = Convert(input_stream, output_stream, size, keep_aspect_ratio);

          input_stream.Close();
          output_stream.Close();

          return result;
        }
      }
    '
    Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing, System.IO -ErrorAction SilentlyContinue
  }

  Process {
    switch ($type) {
      "jpg" { $type = "jpeg" }
      "icon" { $type = "ico" }
    }

    # Ensure file exists
    $path = Resolve-Path $path
    if ((Test-Path $path) -eq $false) {
      throw "$path does not exist."
    }

    # Ensure directory exists if one was specified. Otherwise, create icon directory in TEMP
    if ($directory.length -eq 0) {
      $directory = "$env:Temp\Icons"
    }
    if ((Test-Path $directory) -eq $false) {
      try {
        New-Item -Type Directory $directory | Out-Null
      }
      catch {
        throw "Can't create $directory"
      }
    }

    # Extract
    $index = 0
    $tempfile = "$directory\tempicon.png"
    $basename = [io.path]::GetFileNameWithoutExtension($path)

    do {
      try {
        $icon = [System.IconExtractor]::Extract($path, $index, $true)
      }
      catch {
        throw "Could not extract icon. Do you have the proper permissions?"
      }
      if ($icon -ne $null) {
        $filepath = "$directory\$basename-$index.$type"
  
        # Convert to bitmap, otherwise it's ugly
        $bmp = $icon.ToBitmap()
      
        try {
          if ($type -eq "ico") {
            $bmp.Save($tempfile,"png")
            [PngIconConverter]::Convert($tempfile,$filepath,$size,$true) | Out-Null
      
            # Keep remove-item from complaining about weird directories
            cmd /c del $tempfile
          }
          else {          
            if ($bmp.Width -ne $size) {
        
              # Needs to be resized
              $newbmp = New-Object System.Drawing.Bitmap($size, $size)
              $graph = [System.Drawing.Graphics]::FromImage($newbmp)
              
              # Make it transparent
              $graph.clear([System.Drawing.Color]::Transparent)
              $graph.DrawImage($bmp,0,0,$size,$size)
              
              #save to file
              $newbmp.Save($filepath,$type)
              $newbmp.Dispose()
            }
            else {
              $bmp.Save($filepath,$type)
            }
            $bmp.Dispose()
          }
          $icon.Dispose()      
          $index++
        }
        catch {
          throw "Could not convert icon!"
        }
      }
    } while ($icon -ne $null)

    # Strip file name down to Value only
    Dir $directory | Rename-Item -NewName { $_.Name -replace "imageres-","" }
  }
}

# Create Form
Function GenerateForm {
  # Echo message
  Write-Host
  Write-Host "Extracting icons . . ."
  Write-Host
  Write-Host ". . . Preparing Form . . ."
  Write-Host
  Write-Host "      . . . This will take a moment . . ."
  Write-Host
  Write-Host "            . . . Please wait . . ."
  Write-Host

  $form = New-Object System.Windows.Forms.Form
  $form.Size = New-Object System.Drawing.Size(900,780)
  $form.BackColor = 'CornflowerBlue'
  $form.StartPosition = 'CenterParent'

  $font = New-Object System.Drawing.Font("Sitka Small",18,[System.Drawing.FontStyle]::Regular)

  # Get name for new subdirectory from User
  $label = New-Object System.Windows.Forms.Label
  $label.AutoSize = $true
  $label.Location = New-Object System.Drawing.Size(15,15)
  $label.Font = $font
  $label.Text = "Enter name for new subdirectory:"

  # Obtain New Subdirectory Name
  $txtBox1 = New-Object System.Windows.Forms.TextBox
  $txtBox1.Size = New-Object System.Drawing.Size(150,20)
  $txtBox1.Location = New-Object System.Drawing.Size(450,15)

  # Get description of new subdirectory from User
  $label2 = New-Object System.Windows.Forms.Label
  $label2.AutoSize = $true
  $label2.Location = New-Object System.Drawing.Size(15,55)
  $label2.Font = $font
  $label2.Text = "Type a description of subdirectories content:"

  $txtBox2 = New-Object System.Windows.Forms.TextBox
  $txtBox2.Size = New-Object System.Drawing.Size(200,20)
  $txtBox2.Location = New-Object System.Drawing.Size(590,55)


  # Instruct User to select icon
  $label3 = New-Object System.Windows.Forms.Label
  $label3.AutoSize = $true
  $label3.Location = New-Object System.Drawing.Size(25,120)
  $label3.Font = $font
  $label3.Text = "Select the icon you would like to use by left-clicking with mouse:"
  $label3.TextAlign = 'BottomCenter'

  # Generate Unique GUID and new Subdirectory
  $button = New-Object System.Windows.Forms.Button
  $button.AutoSize = $true
  $button.Font = $font
  $button.Text = "Click to create unique guid and generate new Subdirectory"
  $button.TextAlign = "BottomCenter"
  $button.Enabled = $true
  $button.FlatStyle = "Flat"
  $button.Location = New-Object System.Drawing.Size(63,600)
  $button.BackColor = 'Black'
  $button.ForeColor = 'Gold'

  # Action on Click
  $button.Add_Click({ ThisPCSubdirectory })

  # Cancel / Close Form
  $button2 = New-Object System.Windows.Forms.Button
  $button2.AutoSize = $true
  $button2.Font = $font
  $button2.Text = "Cancel / Close"
  $button2.TextAlign = "BottomCenter"
  $button2.Enabled = $true
  $button2.FlatStyle = "Flat"
  $button2.Location = New-Object System.Drawing.Size(63,680)
  $button2.BackColor = 'Black'
  $button2.ForeColor = 'Gold'

  #Action on Click
  $button2.Add_Click({ UserCancelCleanup })

  # ListView for Icons Choice
  $iconsView = New-Object System.Windows.Forms.ListView
  $iconsView.Size = New-Object System.Drawing.Size(855,400)
  $iconsView.Location = New-Object System.Drawing.Size(15,160)
  $iconsView.BackColor = 'Sienna'

  # Extract Icons from imageres.dll
  GetIcons "$env:WinDir\System32\imageres.dll"

  # Create ImageList
  $iconsList = New-Object System.Windows.Forms.ImageList

  # Add icons to ImageList
  [int] $i = 0
  do {
    $iconsList.Images.Add([System.Drawing.Image]::FromFile("$env:UserProfile\AppData\Local\Temp\Icons\$i.ico"))
    $i++
  } while ($i -lt 412)

  # Associated iconsList with ListView
  $iconsView.LargeImageList = $iconsList
  $iconsView.LargeImageList.ImageSize = [System.Drawing.Size] '32,32'
  $iconsView.LargeImageList.ColorDepth = '32'

  # Generate ListView text of imageres.dll icon Values
  [int] $j = 0
  do {
    $iconsView.Items.Add("$j") | Out-Null
    $j++
  } while ($j -lt 412)

  # Generate imageres.dll icons correlating to icon Value
  [int] $k = 0
  do {
    $iconsView.Items[$k].ImageIndex = $k
    $k++
  } while ($k -lt 412)

  # Set Form's icon and title
  $form.Icon = "$env:UserProfile\AppData\Local\Temp\Icons\311.ico"
  $form.Text = "ThisPC Subdirectory Generator — Regedit32 @ https://www.windows10forums.com/"

  $form.Controls.AddRange(
    ($label, $label2, $label3, $txtBox1, $txtBox2,
     $txtBox3, $txtBox4, $iconsView, $button, $button2))

  # Prevent User being able to close Form
  $form.ControlBox = $false

  Return $form.ShowDialog()
}
if (FormOpened -eq $true) {
  # Generate the Form
  GenerateForm | Out-Null
}

So you do not have to muck around opening your PowerShell console, you will find attached a zipped folder.
  • Download ThisPCModifier.zip
  • Right-click on it and select Extract all
  • You'll now see the executable file: ThisPCModifier.exe
    • Right-click on this exe file and select Run as administrator
      • You'll now see a elevated command console
      • After 10 seconds the form will open and you can create your own sub-directories and add [ if you wish ] a description to appear in the information bubble that pops up when you hover your mouse over the folder.
      • When all is done, the program will clean up the temporary files created, then close the Form.
Regards,

Regedit32
Author
Regedit32
Downloads
1,966
First release
Last update

More resources from Regedit32

Top