· 4 years ago · Apr 05, 2021, 06:48 AM
1<#
2 .SYNOPSIS
3 Configure tiles on Star
4
5 .PARAMETER ControlPanel
6 Pin the "Control Panel" shortcut to Star
7
8 .PARAMETER DevicesPrinters
9 Pin the "Devices & Printers" shortcut to Start
10
11 .PARAMETER PowerShell
12 Pin the "Windows PowerShell" shortcut to Start
13
14 .PARAMETER UnpinAll
15 Unpin all the Start tiles
16
17 .EXAMPLE
18 .\Pin.ps1 -Tiles ControlPanel, DevicesPrinters, PowerShell
19
20 .EXAMPLE
21 .\Pin.ps1 -UnpinAll
22
23 .EXAMPLE
24 .\Pin.ps1 -UnpinAll -Tiles ControlPanel, DevicesPrinters, PowerShell
25
26 .EXAMPLE
27 .\Pin.ps1 -UnpinAll -Tiles ControlPanel
28
29 .EXAMPLE
30 .\Pin.ps1 -Tiles ControlPanel -UnpinAll
31
32 .LINK
33 https://github.com/farag2/Windows-10-Sophia-Script
34
35 .NOTES
36 Separate arguments with comma
37 Current user
38#>
39[CmdletBinding()]
40param
41(
42 [Parameter(
43 Mandatory = $false,
44 Position = 0
45 )]
46 [switch]
47 $UnpinAll,
48
49 [Parameter(
50 Mandatory = $false,
51 Position = 1
52 )]
53 [ValidateSet("ControlPanel", "DevicesPrinters", "PowerShell")]
54 [string[]]
55 $Tiles,
56
57 [string]
58 $StartLayout = "$PSScriptRoot\StartLayout.xml"
59)
60
61begin
62{
63 # Unpin all the Start tiles
64 if ($UnpinAll)
65 {
66 Export-StartLayout -Path $StartLayout -UseDesktopApplicationID
67
68 [xml]$XML = Get-Content -Path $StartLayout -Encoding UTF8 -Force
69 $Groups = $XML.LayoutModificationTemplate.DefaultLayoutOverride.StartLayoutCollection.StartLayout.Group
70
71 foreach ($Group in $Groups)
72 {
73 # Removing all groups inside XML
74 $Group.ParentNode.RemoveChild($Group) | Out-Null
75 }
76
77 $XML.Save($StartLayout)
78 }
79}
80
81process
82{
83 # Extract strings from shell32.dll using its' number
84 $Signature = @{
85 Namespace = "WinAPI"
86 Name = "GetStr"
87 Language = "CSharp"
88 MemberDefinition = @"
89[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
90public static extern IntPtr GetModuleHandle(string lpModuleName);
91[DllImport("user32.dll", CharSet = CharSet.Auto)]
92internal static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);
93public static string GetString(uint strId)
94{
95 IntPtr intPtr = GetModuleHandle("shell32.dll");
96 StringBuilder sb = new StringBuilder(255);
97 LoadString(intPtr, strId, sb, sb.Capacity);
98 return sb.ToString();
99}
100"@
101 }
102
103 if (-not ("WinAPI.GetStr" -as [type]))
104 {
105 Add-Type @Signature -Using System.Text
106 }
107
108 # Extract the localized "Devices and Printers" string from shell32.dll
109 $DevicesPrinters = [WinAPI.GetStr]::GetString(30493)
110
111 # We need to get the AppID because it's auto generated
112 $Script:DevicesPrintersAppID = (Get-StartApps | Where-Object -FilterScript {$_.Name -eq $DevicesPrinters}).AppID
113
114 $Parameters = @(
115 # Control Panel hash table
116 @{
117 # Special name for Control Panel
118 Name = "ControlPanel"
119 Size = "2x2"
120 Column = 0
121 Row = 0
122 AppID = "Microsoft.Windows.ControlPanel"
123 },
124 # "Devices & Printers" hash table
125 @{
126 # Special name for "Devices & Printers"
127 Name = "DevicesPrinters"
128 Size = "2x2"
129 Column = 2
130 Row = 0
131 AppID = $Script:DevicesPrintersAppID
132 },
133 # Windows PowerShell hash table
134 @{
135 # Special name for Windows PowerShell
136 Name = "PowerShell"
137 Size = "2x2"
138 Column = 4
139 Row = 0
140 AppID = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"
141 }
142 )
143
144 # Valid columns to place tiles in
145 $ValidColumns = @(0, 2, 4)
146 [string]$StartLayoutNS = "http://schemas.microsoft.com/Start/2014/StartLayout"
147
148 # Add pre-configured hastable to XML
149 function Add-Tile
150 {
151 param
152 (
153 [string]
154 $Size,
155
156 [int]
157 $Column,
158
159 [int]
160 $Row,
161
162 [string]
163 $AppID
164 )
165
166 [string]$elementName = "start:DesktopApplicationTile"
167 [Xml.XmlElement]$Table = $xml.CreateElement($elementName, $StartLayoutNS)
168 $Table.SetAttribute("Size", $Size)
169 $Table.SetAttribute("Column", $Column)
170 $Table.SetAttribute("Row", $Row)
171 $Table.SetAttribute("DesktopApplicationID", $AppID)
172
173 $Table
174 }
175
176 if (-not (Test-Path -Path $StartLayout))
177 {
178 # Export the current Start layout
179 Export-StartLayout -Path $StartLayout -UseDesktopApplicationID
180 }
181
182 [xml]$XML = Get-Content -Path $StartLayout -Encoding UTF8 -Force
183
184 foreach ($Tile in $Tiles)
185 {
186 switch ($Tile)
187 {
188 ControlPanel
189 {
190 $ControlPanel = [WinAPI.GetStr]::GetString(12712)
191 Write-Verbose -Message ("The `"{0}`" shortcut is being pinned to Start" -f $ControlPanel) -Verbose
192 }
193 DevicesPrinters
194 {
195 $DevicesPrinters = [WinAPI.GetStr]::GetString(30493)
196 Write-Verbose -Message ("The `"{0}`" shortcut is being pinned to Start" -f $DevicesPrinters) -Verbose
197
198 # Create the old-style "Devices and Printers" shortcut in the Start menu
199 $Shell = New-Object -ComObject Wscript.Shell
200 $Shortcut = $Shell.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start menu\Programs\System Tools\$DevicesPrinters.lnk")
201 $Shortcut.TargetPath = "control"
202 $Shortcut.Arguments = "printers"
203 $Shortcut.IconLocation = "$env:SystemRoot\system32\DeviceCenter.dll"
204 $Shortcut.Save()
205
206 Start-Sleep -Seconds 3
207 }
208 PowerShell
209 {
210 Write-Verbose -Message ("The `"{0}`" shortcut is being pinned to Start" -f "Windows PowerShell") -Verbose
211 }
212 }
213
214 $Parameter = $Parameters | Where-Object -FilterScript {$_.Name -eq $Tile}
215 $Group = $XML.LayoutModificationTemplate.DefaultLayoutOverride.StartLayoutCollection.StartLayout.Group | Where-Object -FilterScript {$_.Name -eq "Sophia Script"}
216
217 # If the "Sophia Script" group exists in Start
218 if ($Group)
219 {
220 $DesktopApplicationID = ($Parameters | Where-Object -FilterScript {$_.Name -eq $Tile}).AppID
221
222 if (-not ($Group.DesktopApplicationTile | Where-Object -FilterScript {$_.DesktopApplicationID -eq $DesktopApplicationID}))
223 {
224 # Calculate current filled columns
225 $CurrentColumns = @($Group.DesktopApplicationTile.Column)
226 # Calculate current free columns and take the first one
227 $Column = (Compare-Object -ReferenceObject $ValidColumns -DifferenceObject $CurrentColumns).InputObject | Select-Object -First 1
228 # If filled cells contain desired ones assign the first free column
229 if ($CurrentColumns -contains $Parameter.Column)
230 {
231 $Parameter.Column = $Column
232 }
233 $Group.AppendChild((Add-Tile @Parameter)) | Out-Null
234 }
235 }
236 else
237 {
238 # Create the "Sophia Script" group
239 [Xml.XmlElement]$Group = $XML.CreateElement("start:Group", $StartLayoutNS)
240 $Group.SetAttribute("Name","Sophia Script")
241 $Group.AppendChild((Add-Tile @Parameter)) | Out-Null
242 $XML.LayoutModificationTemplate.DefaultLayoutOverride.StartLayoutCollection.StartLayout.AppendChild($Group) | Out-Null
243 }
244 }
245
246 $XML.Save($StartLayout)
247}
248
249end
250{
251 # Temporarily disable changing the Start menu layout
252 if (-not (Test-Path -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer))
253 {
254 New-Item -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Force
255 }
256 New-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name LockedStartLayout -Value 1 -Force
257 New-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name StartLayoutFile -Value $StartLayout -Force
258
259 Start-Sleep -Seconds 3
260
261 # Restart the Start menu
262 Stop-Process -Name StartMenuExperienceHost -Force -ErrorAction Ignore
263
264 Start-Sleep -Seconds 3
265
266 # Open the Start menu to load the new layout
267 $wshell = New-Object -ComObject WScript.Shell
268 $wshell.SendKeys("^{ESC}")
269
270 Start-Sleep -Seconds 3
271
272 # Enable changing the Start menu layout
273 Remove-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name LockedStartLayout -Force -ErrorAction Ignore
274 Remove-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name StartLayoutFile -Force -ErrorAction Ignore
275
276 Remove-Item -Path $StartLayout -Force
277
278 Stop-Process -Name StartMenuExperienceHost -Force -ErrorAction Ignore
279
280 Start-Sleep -Seconds 3
281
282 # Open the Start menu to load the new layout
283 $wshell = New-Object -ComObject WScript.Shell
284 $wshell.SendKeys("^{ESC}")
285}