在不先运行 VB 脚本的情况下运行 Powershell 脚本时出现问题



我一直在寻找一种解决方案,将快捷方式或程序固定到 带有 PS 的 win 10 中的任务。 我发现在 Windows 10 中使用 PS 将程序固定到任务栏。 VB脚本工作,

If WScript.Arguments.Count < 1 Then WScript.Quit
'----------------------------------------------------------------------
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFile    = WScript.Arguments.Item(0)
sKey1      = "HKCUSoftwareClasses*shell{:}\"
sKey2      = Replace(sKey1, "\", "ExplorerCommandHandler")
'----------------------------------------------------------------------
With WScript.CreateObject("WScript.Shell")
KeyValue = .RegRead("HKLMSOFTWAREMicrosoftWindowsCurrentVersionExplorer" & _
"CommandStoreshellWindows.taskbarpinExplorerCommandHandler")
.RegWrite sKey2, KeyValue, "REG_SZ"
With WScript.CreateObject("Shell.Application")
With .Namespace(objFSO.GetParentFolderName(objFile))
With .ParseName(objFSO.GetFileName(objFile))
.InvokeVerb("{:}")
End With
End With
End With
.Run("Reg.exe delete """ & Replace(sKey1, "\", "") & """ /F"), 0, True
End With
'----------------------------------------------------------------------

我可以从 PS 调用 VB 脚本,但一个乐于助人的人将脚本转换为 PS

Param($Target)
$KeyPath1  = "HKCU:SOFTWAREClasses"
$KeyPath2  = "*"
$KeyPath3  = "shell"
$KeyPath4  = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
(Get-ItemProperty `
("HKLM:SOFTWAREMicrosoftWindowsCurrentVersionExplorer" + `
"CommandStoreshellWindows.taskbarpin")
).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0) {
$Key2.DeleteSubKey($KeyPath3)
}

但是,除非 VB 脚本至少运行过一次,否则此 PS 脚本将不会运行。 有没有办法使PS脚本在不必运行VB脚本的情况下工作?

尝试运行 PS 脚本而不运行 VB 脚本之前至少运行一次时出现的错误:

You cannot call a method on a null-valued expression.
At \serverUtilitiesTaskbarPin.ps1:41 char:5
+     $Key3 = $Key2.CreateSubKey($KeyPath3, $true)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \serverUtilitiesTaskbarPin.ps1:42 char:5
+     $Key4 = $Key3.CreateSubKey($KeyPath4, $true)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \serverUtilitiesTaskbarPin.ps1:43 char:5
+     $Key4.SetValue($KeyValue, $ValueData)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \serverUtilitiesTaskbarPin.ps1:50 char:5
+     $Key3.DeleteSubKey($KeyPath4)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

使用VB脚本执行任务一次后,我没有收到错误。

你不应该受到这种方式的影响。 代码按设计工作,但您必须完全调用 exe 的路径。

我刚刚将其转换为函数,并且没有其他依赖项即可成功。

Function Add-AppToTaskbar
{
[cmdletbinding()]
Param
(
[string]$Target
)
$KeyPath1  = "HKCU:SOFTWAREClasses"
$KeyPath2  = "*"
$KeyPath3  = "shell"
$KeyPath4  = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
(Get-ItemProperty `
("HKLM:SOFTWAREMicrosoftWindowsCurrentVersionExplorer" + `
"CommandStoreshellWindows.taskbarpin")
).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0) 
{$Key2.DeleteSubKey($KeyPath3)}
}
Add-AppToTaskbar -Target 'C:Program Files (x86)GoogleChromeApplicationchrome.exe'

顺便说一句,这些固定的东西位于系统上的两个位置:

这里:

$env:USERPROFILEAppDataRoamingMicrosoftInternet ExplorerQuick LaunchUser PinnedTaskBar

注册表:

HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorerTaskband

两者都是必需的。

根据OP的评论进行更新

我只是在本地和远程运行了这个,两者都成功了。请参阅下面的结果。 我正在使用的本地主机 - WS2012R2设置为工作站角色 我的实验室里没有任何 W10 系统。较早的测试是在本地 W10 主机上进行的。

在控制台主机 ISE 和 VSCode 中执行。

PS C:Windowssystem32> $env:COMPUTERNAME
LabWS01
# PS Version
PS C:Windowssystem32> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.42000
BuildVersion                   6.3.9600.18968
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2

# the current user profile pinned location filtered for notepad*
PS C:Windowssystem32> Get-ChildItem -Path "$env:USERPROFILEAppDataRoamingMicrosoftInternet ExplorerQuick LaunchUser PinnedTaskBarNotepad*"
# Tested path to remote share
PS C:Windowssystem32> Test-path -Path '\ServerShareNameAdd-AppToTaskbar.ps1'
True
# Ran the script from that remote share
PS C:Windowssystem32> \ServerShareNameAdd-AppToTaskbar.ps1 'c:windowsnotepad.exe'

或者这样...

Start-process -FilePath Powershell -ArgumentList '\ServerShareNameAdd-AppToTaskbar.ps1 -Target C:Windowsnotepad.exe'
# Review pinned item location, filtered for notepad*
PS C:Windowssystem32> Get-ChildItem -Path "$env:USERPROFILEAppDataRoamingMicrosoftInternet ExplorerQuick LaunchUser PinnedTaskBarNotepad*"

Directory: C:UsersLabuser001AppDataRoamingMicrosoftInternet ExplorerQuick LaunchUser PinnedTaskBar

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          8/9/2018   8:48 PM        791 Notepad.lnk

快捷方式显示固定到任务栏。

所以,这听起来很环保。现在,如果此问题仍然存在,可以使用 GPO 固定应用。

我已经修改了您的函数,以便有选择地将项目固定或取消固定到任务栏。以前,问题是 pin 命令不是独占的,如果应用程序已经固定,它将取消固定应用程序。通过进一步检测二进制注册表值中固定的内容,可以确定该项是否已固定,并且不会尝试两次固定该项。

Set-AppPinTaskbarCsv 是为我们的环境定制的函数,我只将其作为示例包含在内,如果有人想在登录脚本中推出它以确保用户固定他们需要的所有应用程序,则需要大量的修改和简化。它有一些不包括在内的功能,这些函数检查组成员资格并重新格式化字符串以扩展变量,这不是必需的。固定应用程序后,如果资源管理器重新启动,它会更可靠地显示,如果固定了任何项目,Csv 函数将重新启动资源管理器。

Function Set-PinTaskbar {
Param (
[parameter(Mandatory=$True, HelpMessage="Target item to pin")]
[ValidateNotNullOrEmpty()]
[string] $Target
,
[Parameter(Mandatory=$False, HelpMessage="Target item to unpin")]
[switch]$Unpin
)
If (!(Test-Path $Target)) {
Write-Warning "$Target does not exist"
Break
}
$Reg = @{}
$Reg.Key1 = "*"
$Reg.Key2 = "shell"
$Reg.Key3 = "{:}"
$Reg.Value = "ExplorerCommandHandler"
$Reg.Data = (Get-ItemProperty ("HKLM:SOFTWAREMicrosoftWindowsCurrentVersionExplorerCommandStoreshellWindows.taskbarpin")).ExplorerCommandHandler
$Reg.Path1 = "HKCU:SOFTWAREClasses"
$Reg.Path2 = Join-Path $Reg.Path1 $Reg.Key1
$Reg.Path3 = Join-Path $Reg.Path2 $Reg.Key2
$Reg.Path4 = Join-Path $Reg.Path3 $Reg.Key3
If (!(Test-Path -LiteralPath $Reg.Path2)) {
New-Item -ItemType Directory -Path $Reg.Path1 -Name [System.Management.Automation.WildcardPattern]::Escape($Reg.Key1)
}
If (!(Test-Path -LiteralPath $Reg.Path3)) {
New-Item -ItemType Directory -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path2)) -Name $Reg.Key2
}
If (!(Test-Path -LiteralPath $Reg.Path4)) {
New-Item -ItemType Directory -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path3)) -Name $Reg.Key3
}
Set-ItemProperty -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path4)) -Name $Reg.Value -Value $Reg.Data
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
# Registry key where the pinned items are located
$RegistryKey = "HKCU:SoftwareMicrosoftWindowsCurrentVersionExplorerTaskband"
# Binary registry value where the pinned items are located
$RegistryValue = "FavoritesResolve"
# Gets the contents into an ASCII format
$CurrentPinsProperty = ([system.text.encoding]::ASCII.GetString((Get-ItemProperty -Path $RegistryKey -Name $RegistryValue | Select-Object -ExpandProperty $RegistryValue)))
# Filters the results for only the characters that we are looking for, so that the search will function
[string]$CurrentPinsResults = $CurrentPinsProperty -Replace '[^x20-x2f^x30-x3ax41-x5cx61-x7F]+', ''
# Globally Unique Identifiers for common system folders, to replace in the pin results
$Guid = @{}
$Guid.FOLDERID_ProgramFilesX86 = @{
"ID" = "{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}"
"Path" = ${env:ProgramFiles(x86)}
}
$Guid.FOLDERID_ProgramFilesX64 = @{
"ID" = "{6D809377-6AF0-444b-8957-A3773F02200E}"
"Path" = $env:ProgramFiles
}
$Guid.FOLDERID_ProgramFiles = @{
"ID" = "{905e63b6-c1bf-494e-b29c-65b732d3d21a}"
"Path" = $env:ProgramFiles
}
$Guid.FOLDERID_System = @{
"ID" = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}"
"Path" = Join-Path $env:WINDIR "System32"
}
$Guid.FOLDERID_Windows = @{
"ID" = "{F38BF404-1D43-42F2-9305-67DE0B28FC23}"
"Path" = $env:WINDIR
}
ForEach ($GuidEntry in $Guid.Keys) {
$CurrentPinsResults = $CurrentPinsResults -replace $Guid.$GuidEntry.ID,$Guid.$GuidEntry.Path
}
$Split = $CurrentPinsResults -split ('C:')
$SplitOutput = @()
# Process each path entry, remove invalid characters, test to determine if the path is valid
ForEach ($Entry in $Split) {
If ($Entry.Substring(0,1) -eq '') {
# Get a list of invalid path characters
$InvalidPathCharsRegEx = [IO.Path]::GetInvalidPathChars() -join ''
$InvalidPathChars = "[{0}]" -f [RegEx]::Escape($InvalidPathCharsRegEx)
$EntryProcessedPhase1 = "C:" + ($Entry -replace $InvalidPathChars)
$EntryProcessedPhase2 = $null
# Remove characters from the path until it is resolvable
ForEach ($Position in $EntryProcessedPhase1.Length .. 1) {
If (Test-Path $EntryProcessedPhase1.Substring(0,$Position)) {
$EntryProcessedPhase2 = $EntryProcessedPhase1.Substring(0,$Position)
Break
}
}
# If the path resolves, add it to the array of paths
If ($EntryProcessedPhase2) {
$SplitOutput += $EntryProcessedPhase2
}
}
}
$PinnedItems = @()
$Shell = New-Object -ComObject WScript.Shell
ForEach ($Path in $SplitOutput) {
# Determines if the entry in the registry is a link in the standard folder, if it is, resolve the path of the shortcut and add it to the array of pinnned items
If ((Split-Path $Path) -eq (Join-Path $env:USERPROFILE "AppDataRoamingMicrosoftInternet ExplorerQuick LaunchUser PinnedTaskBar")) {
$Shell.CreateShortcut($Path).TargetPath
$PinnedItems += $Shell.CreateShortcut($Path).TargetPath
}
Else {
# If the link or executable is not in the taskbar folder, add it directly
$PinnedItems += $Path
}
}
# Unpin if the application is pinned
If ($Unpin.IsPresent) {
If ($PinnedItems -contains $Target) {
$Item.InvokeVerb("{:}")
Write-Host "Unpinning application $Target"
}
}
Else {
# Only pin the application if it hasn't been pinned
If ($PinnedItems -notcontains $Target) {
$Item.InvokeVerb("{:}")
Write-Host "Pinning application $Target"
}
}
# Remove the registry key and subkeys required to pin the application
If (Test-Path $Reg.Path3) {
Remove-Item -LiteralPath $Reg.Path3 -Recurse
}

}

Function Set-PinTaskbarCsv {
Param (
[Parameter(Mandatory=$true)]
$PinHashTable
,
[Parameter(Mandatory=$true)]
$UnpinHashTable
)
$Organization = "LIHC"
$RootRegistry = "HKCU:Software" + $Organization
$RootRegistryPinned = Join-Path $RootRegistry "Pinned"
# Unpin applications from taskbar
ForEach ($Entry in $UnpinHashTable.Keys) {
$Location = Format-VariablesString -String $UnpinHashTable.$Entry.Location
Add-Log "Taskbar app unpinned" $Location
Set-PinTaskbar -Target $Location -Unpin
}
# Pin applications to taskbar
$Groups = @("Group1","Group2","Group3","Group4","Group5")
ForEach ($Entry in $PinHashTable.Keys) {
$Entry
$Location = Format-VariablesString -String $PinHashTable.$Entry.Location
$ToTaskbar = [string]::IsNullOrWhiteSpace($PinHashTable.$Entry.Group1)
ForEach ($Group in $Groups) {
If (!([string]::IsNullOrWhiteSpace($PinHashTable.$Entry.$Group))) {
$ToTaskbar = (Get-UserGroups -Username $env:USERNAME -Group $PinHashTable.$Entry.$Group) -or $ToTaskbar
}
}        
If (!([string]::IsNullOrWhiteSpace($PinHashTable.$Entry.TestPath))) {
$ToTaskbar = ((Test-Path $PinHashTable.$Entry.TestPath) -or (Test-Path $PinHashTable.$Entry.TestPath2)) -and $true
}
If ($ToTaskbar -and (Test-Path $Location) -and (!(Get-ItemProperty $RootRegistryPinned $Location -ErrorAction SilentlyContinue))) {
#Set-AppPinTaskbar -Application $Location
Set-PinTaskbar -Target $Location
Add-Log "Taskbar app Pinned" $Location
New-ItemProperty -Path $RootRegistryPinned -Name $Location 2>&1 > $null
$Status = $true
}
}
If ($Status) {
Get-Process -Name explorer | Stop-Process
Start-Process -FilePath explorer.exe
}

}

最新更新