Русский
Русский
English
Статистика
Реклама

Закрепляем ярлыки на начальном экране в Windows 10 у текущего пользователя

Эта статья своего рода proof of concept, как можно закрепить программно (открепить) ярлык на начальном экране для текущего пользователя без перезапуска или выхода из учетной записи. Как вы знаете, с выходом Windows 10 October 2018 Microsoft без шума закрыл доступ к API открепления (закрепления) ярлыков от начального экрана и панели задач: отныне это можно сделать лишь вручную.

Ниже приведен пример кода для закрепления (открепления) ярлыка на начальный экран, который когда-то работал. Как можете видеть, в коде используется метод получения локализорованной строки, и для этого нам необходимо знать код строки, чтобы вызвать соответствующий пункт контекстного меню. В данном пример, чтобы закрепить ярлык командной строки, мы вызываем строку с кодом 51201, Закрепить на начальном экране, из библиотеки %SystemRoot%\system32\shell32.dll.

Получить список всех локализованных строк удобнее всего через стороннюю утилиту ResourcesExtract.

# Extract a localized string from shell32.dll$Signature = @{Namespace = "WinAPI"Name = "GetStr"Language = "CSharp"MemberDefinition = @"[DllImport("kernel32.dll", CharSet = CharSet.Auto)]public static extern IntPtr GetModuleHandle(string lpModuleName);[DllImport("user32.dll", CharSet = CharSet.Auto)]internal static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);public static string GetString(uint strId){IntPtr intPtr = GetModuleHandle("shell32.dll");StringBuilder sb = new StringBuilder(255);LoadString(intPtr, strId, sb, sb.Capacity);return sb.ToString();}"@}if (-not ("WinAPI.GetStr" -as [type])){Add-Type @Signature -Using System.Text}# Pin to Start: 51201# Unpin from Start: 51394$LocalizedString = [WinAPI.GetStr]::GetString(51201)# Trying to pin the Command Prompt shortcut to Start$Target = Get-Item -Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\System Tools\Command Prompt.lnk"$Shell = New-Object -ComObject Shell.Application$Folder = $Shell.NameSpace($Target.DirectoryName)$file = $Folder.ParseName($Target.Name)$Verb = $File.Verbs() | Where-Object -FilterScript {$_.Name -eq $LocalizedString}$Verb.DoIt()

Сейчас консоль вываливается с ошибкой Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

Хотя, как можно заметить, API, конечно, отдает глагол контекстного меню Закрепить на начальном &экране, но не может его выполнить.

Где-то читал, что, возможно, Microsoft заблокировал доступ в целях недопущения закрепления ярлыков bloatware. Звучит странно, но ладно

Я уже много лет поддерживаю крупнейший PowerShell-модуль для тонкой настройки Windows 10 и автоматизации рутинных задач. Подробнее можно почитать здесь. И встала задача из спортивного интереса обойти это ограничение и попробовать закрепить нужные мне ярлыки. Речь, конечно, идет по большей части о домашних пользователях, ведь в энтерпрайзе используется GPO для импорта предзаготовленного макета начального экрана и панели задач.

Мы знаем, что текущий макет начального экрана можно выгрузить в формате XML. Но даже, если его настроить должным образом, импортировать макет в профиль текущего пользователя не получится: Import-StartLayout -LayoutPath "D:\Layout.xml импортирует макеты начального экрана и панели задач только для новых пользователей.

Идея заключается в том, чтобы использовать политику Макет начального экрана (Prevent users from customizing their Start Screen), отвечающую за подгрузку предзаготовленного макета в формате XML из определенного места. Соответственно, наш хак будет состоять из следующих пунктов:

  • Выгружаем текущий макет начального экрана;

  • Парсим XML, добавляя необходимые нам ярлыки (ссылки должны вести на реально существующие ярлыки) и сохраняем;

  • С помощью политики временно выключаем возможность редактировать макет начального экрана;

  • Перезапускаем меню Пуск;

  • Программно открываем меню Пуск, чтобы в реестре сохранился его макет;

  • Выключаем политику, чтобы можно было редактировать макет начального экрана;

  • И открываем меню Пуск опять.

Вуаля! В данном примере мы настроили начальный экран на лету, закрепив на него три ярлыка: Панель управления, устройства и принтеры и PowerShell.

<#.SYNOPSISConfigure the Start tiles.PARAMETER ControlPanelPin the "Control Panel" shortcut to Start.PARAMETER DevicesPrintersPin the "Devices & Printers" shortcut to Start.PARAMETER PowerShellPin the "Windows PowerShell" shortcut to Start.PARAMETER UnpinAllUnpin all the Start tiles.EXAMPLE.\Pin.ps1 -Tiles ControlPanel, DevicesPrinters, PowerShell.EXAMPLE.\Pin.ps1 -UnpinAll.EXAMPLE.\Pin.ps1 -UnpinAll -Tiles ControlPanel, DevicesPrinters, PowerShell.EXAMPLE.\Pin.ps1 -UnpinAll -Tiles ControlPanel.EXAMPLE.\Pin.ps1 -Tiles ControlPanel -UnpinAll.LINKhttps://github.com/farag2/Windows-10-Sophia-Script.NOTESSeparate arguments with commaCurrent user#>[CmdletBinding()]param([Parameter(Mandatory = $false,Position = 0)][switch]$UnpinAll,[Parameter(Mandatory = $false,Position = 1)][ValidateSet("ControlPanel", "DevicesPrinters", "PowerShell")][string[]]$Tiles,[string]$StartLayout = "$PSScriptRoot\StartLayout.xml")begin{# Unpin all the Start tilesif ($UnpinAll){Export-StartLayout -Path $StartLayout -UseDesktopApplicationID[xml]$XML = Get-Content -Path $StartLayout -Encoding UTF8 -Force$Groups = $XML.LayoutModificationTemplate.DefaultLayoutOverride.StartLayoutCollection.StartLayout.Groupforeach ($Group in $Groups){# Removing all groups inside XML$Group.ParentNode.RemoveChild($Group) | Out-Null}$XML.Save($StartLayout)}}process{# Extract strings from shell32.dll using its' number$Signature = @{Namespace = "WinAPI"Name = "GetStr"Language = "CSharp"MemberDefinition = @"[DllImport("kernel32.dll", CharSet = CharSet.Auto)]public static extern IntPtr GetModuleHandle(string lpModuleName);[DllImport("user32.dll", CharSet = CharSet.Auto)]internal static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);public static string GetString(uint strId){IntPtr intPtr = GetModuleHandle("shell32.dll");StringBuilder sb = new StringBuilder(255);LoadString(intPtr, strId, sb, sb.Capacity);return sb.ToString();}"@}if (-not ("WinAPI.GetStr" -as [type])){Add-Type @Signature -Using System.Text}# Extract the localized "Devices and Printers" string from shell32.dll$DevicesPrinters = [WinAPI.GetStr]::GetString(30493)# We need to get the AppID because it's auto generated$Script:DevicesPrintersAppID = (Get-StartApps | Where-Object -FilterScript {$_.Name -eq $DevicesPrinters}).AppID$Parameters = @(# Control Panel hash table@{# Special name for Control PanelName = "ControlPanel"Size = "2x2"Column = 0Row = 0AppID = "Microsoft.Windows.ControlPanel"},# "Devices & Printers" hash table@{# Special name for "Devices & Printers"Name = "DevicesPrinters"Size   = "2x2"Column = 2Row    = 0AppID  = $Script:DevicesPrintersAppID},# Windows PowerShell hash table@{# Special name for Windows PowerShellName = "PowerShell"Size = "2x2"Column = 4Row = 0AppID = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"})# Valid columns to place tiles in$ValidColumns = @(0, 2, 4)[string]$StartLayoutNS = "http://schemas.microsoft.com/Start/2014/StartLayout"# Add pre-configured hastable to XMLfunction Add-Tile{param([string]$Size,[int]$Column,[int]$Row,[string]$AppID)[string]$elementName = "start:DesktopApplicationTile"[Xml.XmlElement]$Table = $xml.CreateElement($elementName, $StartLayoutNS)$Table.SetAttribute("Size", $Size)$Table.SetAttribute("Column", $Column)$Table.SetAttribute("Row", $Row)$Table.SetAttribute("DesktopApplicationID", $AppID)$Table}if (-not (Test-Path -Path $StartLayout)){# Export the current Start layoutExport-StartLayout -Path $StartLayout -UseDesktopApplicationID}[xml]$XML = Get-Content -Path $StartLayout -Encoding UTF8 -Forceforeach ($Tile in $Tiles){switch ($Tile){ControlPanel{$ControlPanel = [WinAPI.GetStr]::GetString(12712)Write-Verbose -Message ("The `"{0}`" shortcut is being pinned to Start" -f $ControlPanel) -Verbose}DevicesPrinters{$DevicesPrinters = [WinAPI.GetStr]::GetString(30493)Write-Verbose -Message ("The `"{0}`" shortcut is being pinned to Start" -f $DevicesPrinters) -Verbose# Create the old-style "Devices and Printers" shortcut in the Start menu$Shell = New-Object -ComObject Wscript.Shell$Shortcut = $Shell.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start menu\Programs\System Tools\$DevicesPrinters.lnk")$Shortcut.TargetPath = "control"$Shortcut.Arguments = "printers"$Shortcut.IconLocation = "$env:SystemRoot\system32\DeviceCenter.dll"$Shortcut.Save()Start-Sleep -Seconds 3}PowerShell{Write-Verbose -Message ("The `"{0}`" shortcut is being pinned to Start" -f "Windows PowerShell") -Verbose}}$Parameter = $Parameters | Where-Object -FilterScript {$_.Name -eq $Tile}$Group = $XML.LayoutModificationTemplate.DefaultLayoutOverride.StartLayoutCollection.StartLayout.Group | Where-Object -FilterScript {$_.Name -eq "Sophia Script"}# If the "Sophia Script" group exists in Startif ($Group){$DesktopApplicationID = ($Parameters | Where-Object -FilterScript {$_.Name -eq $Tile}).AppIDif (-not ($Group.DesktopApplicationTile | Where-Object -FilterScript {$_.DesktopApplicationID -eq $DesktopApplicationID})){# Calculate current filled columns$CurrentColumns = @($Group.DesktopApplicationTile.Column)# Calculate current free columns and take the first one$Column = (Compare-Object -ReferenceObject $ValidColumns -DifferenceObject $CurrentColumns).InputObject | Select-Object -First 1# If filled cells contain desired ones assign the first free columnif ($CurrentColumns -contains $Parameter.Column){$Parameter.Column = $Column}$Group.AppendChild((Add-Tile @Parameter)) | Out-Null}}else{# Create the "Sophia Script" group[Xml.XmlElement]$Group = $XML.CreateElement("start:Group", $StartLayoutNS)$Group.SetAttribute("Name","Sophia Script")$Group.AppendChild((Add-Tile @Parameter)) | Out-Null$XML.LayoutModificationTemplate.DefaultLayoutOverride.StartLayoutCollection.StartLayout.AppendChild($Group) | Out-Null}}$XML.Save($StartLayout)}end{# Temporarily disable changing the Start menu layoutif (-not (Test-Path -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer)){New-Item -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Force}New-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name LockedStartLayout -Value 1 -ForceNew-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name StartLayoutFile -Value $StartLayout -ForceStart-Sleep -Seconds 3# Restart the Start menuStop-Process -Name StartMenuExperienceHost -Force -ErrorAction IgnoreStart-Sleep -Seconds 3# Open the Start menu to load the new layout$wshell = New-Object -ComObject WScript.Shell$wshell.SendKeys("^{ESC}")Start-Sleep -Seconds 3# Enable changing the Start menu layoutRemove-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name LockedStartLayout -Force -ErrorAction IgnoreRemove-ItemProperty -Path HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer -Name StartLayoutFile -Force -ErrorAction IgnoreRemove-Item -Path $StartLayout -ForceStop-Process -Name StartMenuExperienceHost -Force -ErrorAction IgnoreStart-Sleep -Seconds 3# Open the Start menu to load the new layout$wshell = New-Object -ComObject WScript.Shell$wshell.SendKeys("^{ESC}")}

Страница GitHub Windows 10 Sophia Script, где в том числе используется данный метод.

Огромное спасибо iNNOKENTIY21 за помощь в реализации метода.

Источник: habr.com
К списку статей
Опубликовано: 06.04.2021 10:06:21
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Системное администрирование

Powershell

Windows

Windows 10

Пуск

Начальный экран

Pin to start

Sophia script

Windows 10 sophia script

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru