在不首先运行VB脚本的情况下运行Powershell脚本的问题

时间:2018-08-09 19:15:09

标签: powershell vbscript

我一直在寻找一种解决方案,可以在PS 10中将快捷方式或程序固定到该任务中。我找到了Pin program to taskbar using PS in Windows 10。 VB脚本有效,

If WScript.Arguments.Count < 1 Then WScript.Quit
'----------------------------------------------------------------------
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFile    = WScript.Arguments.Item(0)
sKey1      = "HKCU\Software\Classes\*\shell\{:}\\"
sKey2      = Replace(sKey1, "\\", "\ExplorerCommandHandler")
'----------------------------------------------------------------------
With WScript.CreateObject("WScript.Shell")
    KeyValue = .RegRead("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" & _
    "\CommandStore\shell\Windows.taskbarpin\ExplorerCommandHandler")

   .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:\SOFTWARE\Classes"
$KeyPath2  = "*"
$KeyPath3  = "shell"
$KeyPath4  = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
    (Get-ItemProperty `
        ("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\" + `
            "CommandStore\shell\Windows.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 \\server\Utilities\TaskbarPin.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 \\server\Utilities\TaskbarPin.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 \\server\Utilities\TaskbarPin.ps1:43 char:5
+     $Key4.SetValue($KeyValue, $ValueData)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:50 char:5
+     $Key3.DeleteSubKey($KeyPath4)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

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

2 个答案:

答案 0 :(得分:0)

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

我只是将其转换为函数,并且没有其他依赖项就成功了。

Function Add-AppToTaskbar
{
    [cmdletbinding()]

    Param
    (
        [string]$Target
    )

    $KeyPath1  = "HKCU:\SOFTWARE\Classes"
    $KeyPath2  = "*"
    $KeyPath3  = "shell"
    $KeyPath4  = "{:}"
    $ValueName = "ExplorerCommandHandler"
    $ValueData =
        (Get-ItemProperty `
            ("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\" + `
                "CommandStore\shell\Windows.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)\Google\Chrome\Application\chrome.exe'

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

这里:

$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar

注册表:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband

都是必需的。

根据OP的评论进行更新

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

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

PS C:\Windows\system32> $env:COMPUTERNAME
LabWS01

# PS Version
PS C:\Windows\system32> $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:\Windows\system32> Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Notepad*"

# Tested path to remote share

PS C:\Windows\system32> Test-path -Path '\\Server\ShareName\Add-AppToTaskbar.ps1'
True

# Ran the script from that remote share

PS C:\Windows\system32> \\Server\ShareName\Add-AppToTaskbar.ps1 'c:\windows\notepad.exe'

或者这样...

Start-process -FilePath Powershell -ArgumentList '\\Server\ShareName\Add-AppToTaskbar.ps1 -Target C:\Windows\notepad.exe'

# Review pinned item location, filtered for notepad*

PS C:\Windows\system32> Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Notepad*"


    Directory: C:\Users\Labuser001\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar


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

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

因此,这听起来对您有利。现在,如果此问题仍然存在,您可以使用GPO固定应用程序。

答案 1 :(得分:0)

我已经修改了您的功能,以便有选择地将项目固定或取消固定到任务栏。以前,问题是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:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Windows.taskbarpin")).ExplorerCommandHandler
$Reg.Path1 = "HKCU:\SOFTWARE\Classes"
$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:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband"
# 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-\x3a\x41-\x5c\x61-\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 "AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar")) {
        $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
}

}