WindowsFever.psm1
<# .SYNOPSIS Create a new file explorer namespace in Windows 10. .DESCRIPTION Create a new file explorer namespace in Windows 10. It uses the following registry paths to register the namespace properly: - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel - HKCU:\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000} - HKCU:\SOFTWARE\Classes\Wow6432Node\CLSID\{00000000-0000-0000-0000-000000000000} You can find the reference for this implementation on MSDN. Even if it's intended for a Cloud Storage Provider, it will work for every local folder: - https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934 .PARAMETER Id The GUID of the new file explorer namespace. A random id will be generated, if this parameter is not specified. .PARAMETER Name The name of the new file explorer namespace. This name will be visible inside the file explorer. .PARAMETER Icon The icon of the new file explorer namespace. Please specify a target dll or exe with the id of the icon. By default, the following icon is used: %SystemRoot%\System32\imageres.dll,156 .PARAMETER Order The order of the new file explorer namespace. The order defines, where in the file explorer the new namespace will be visible. The default order is 66. .PARAMETER TargetKnownFolder You can specify a GUID for a known folder, e.g. for OneDrive. .PARAMETER TargetFolderPath You can specify a physical path to the target folder path. .INPUTS None. .OUTPUTS WindowsFever.FileExplorerNamespace. .EXAMPLE C:\> Add-FileExplorerNamespace -Name 'Demo' -TargetFolderPath 'C:\Path\To\Demo' Create a simple file explorer namespace with the name Test which points to the given path. .EXAMPLE C:\> Add-FileExplorerNamespace -Name 'Workspace' -Icon '%SystemRoot%\System32\imageres.dll,156' -TargetFolderPath "$HOME\Workspace" -Order 67 Create a complex Workspace file explorer namespace by specified every parameter including a specific icon. .EXAMPLE C:\> Add-FileExplorerNamespace -Name 'PowerShell' -Icon '%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe,0' -TargetFolderPath "$HOME\Dropbox\PowerShell" -Order 65 Create a complex PowerShell file explorer namespace by specified every parameter including a specific icon. .EXAMPLE C:\> Add-FileExplorerNamespace -Id '57C16D98-4CA5-4AAA-8118-48C28D8C50BC' -Name 'OneDrive (Copy)' -Icon 'C:\WINDOWS\system32\imageres.dll,-1043' -TargetKnownFolder 'a52bba46-e9e1-435f-b3d9-28daa648c0f6' Duplicate the OneDrive namespace inside the file explorer by using the OneDrive known folder class id. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/WindowsFever #> function Add-FileExplorerNamespace { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [System.Guid] $Id = [Guid]::NewGuid(), [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $false)] [System.String] $Icon = '%SystemRoot%\System32\imageres.dll,156', [Parameter(Mandatory = $false)] [System.Int32] $Order = 66, [Parameter(Mandatory = $true, ParameterSetName = 'KnownFolder')] [System.Guid] $TargetKnownFolder, [Parameter(Mandatory = $true, ParameterSetName = 'FolderPath')] [ValidateScript({ Test-Path -Path $_ })] [System.String] $TargetFolderPath ) # For security reason, check if the namespace already exists if ($null -ne (Get-FileExplorerNamespace -Id $Id)) { throw "The file explorer namespace with Id '$Id' already exists!" } # Use the default and WOW64 node to place the class foreach ($Key in 'HKCU:\SOFTWARE\Classes\CLSID', 'HKCU:\SOFTWARE\Classes\Wow6432Node\CLSID') { if ((Test-Path -Path $Key)) { # Step 1: Add your CLSID and name your extension New-Item -Path $Key -Name "{$Id}" -Value $Name -ItemType String -Force | Out-Null # Step 2: Set the image for your icon New-Item -Path "$Key\{$Id}" -Name 'DefaultIcon' -Value $Icon -ItemType String -Force | Out-Null # Step 3: Add your extension to the Navigation Pane and make it visible New-ItemProperty -Path "$Key\{$Id}" -Name 'System.IsPinnedToNameSpaceTree' -Value 1 -PropertyType DWord -Force | Out-Null # Step 4: Set the location for your extension in the Navigation Pane New-ItemProperty -Path "$Key\{$Id}" -Name 'SortOrderIndex' -Value $Order -PropertyType DWord -Force | Out-Null # Step 5: Provide the dll that hosts your extension New-Item -Path "$Key\{$Id}" -Name 'InProcServer32' -Value "%SystemRoot%\System32\shell32.dll" -ItemType String -Force | Out-Null # Step 6: Define the instance object New-Item -Path "$Key\{$Id}" -Name 'Instance' -ItemType String -Force | Out-Null New-ItemProperty -Path "$Key\{$Id}\Instance" -Name 'CLSID' -Value '{0E5AAE11-A475-4c5b-AB00-C66DE400274E}' -PropertyType String -Force | Out-Null # Step 7: Provide the file system attributes of the target folder New-Item -Path "$Key\{$Id}\Instance" -Name 'InitPropertyBag' -ItemType String -Force | Out-Null New-ItemProperty -Path "$Key\{$Id}\Instance\InitPropertyBag" -Name 'Attributes' -Value 17 -PropertyType DWord -Force | Out-Null # Step 8: Set the path for the target (depending of the type) switch ($PSCmdlet.ParameterSetName) { 'KnownFolder' { New-ItemProperty -Path "$Key\{$Id}\Instance\InitPropertyBag" -Name 'TargetKnownFolder' -Value "{$TargetKnownFolder}" -PropertyType ExpandString -Force | Out-Null } 'FolderPath' { New-ItemProperty -Path "$Key\{$Id}\Instance\InitPropertyBag" -Name 'TargetFolderPath' -Value $TargetFolderPath -PropertyType ExpandString -Force | Out-Null } } # Step 9: Set appropriate shell flags New-Item -Path "$Key\{$Id}" -Name 'ShellFolder' -ItemType String -Force | Out-Null New-ItemProperty -Path "$Key\{$Id}\ShellFolder" -Name 'FolderValueFlags' -Value 40 -PropertyType DWord -Force | Out-Null # Step 10: Set the appropriate flags to control your shell behavior New-ItemProperty -Path "$Key\{$Id}\ShellFolder" -Name 'Attributes' -Value 4034920525 -PropertyType DWord -Force | Out-Null } } # Step 11: Register your extension in the namespace root New-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace" -Name "{$Id}" -Value $Name -ItemType String -Force | Out-Null # Step 12: Hide your extension from the Desktop New-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" -Name "{$Id}" -Value 1 -PropertyType DWord -Force | Out-Null # Return the newly create file explorer namespace Get-FileExplorerNamespace -Id $Id } <# .SYNOPSIS List all file explorer namespaces of the current user. .DESCRIPTION List all file explorer namespaces of the current user. It uses the registry with the following paths to enumerate the file explorer namespaces: - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace - HKCU:\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000} You can find the reference for this implementation on MSDN. Even if it's intended for a Cloud Storage Provider, it will work for every local folder: - https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934 .PARAMETER Id Parameter to filter for the id (GUID) of the file explorer namespace. .PARAMETER Name Parameter to filter for the name of the file explorer namespace, optionally with wildcard characters. .INPUTS None. .OUTPUTS WindowsFever.FileExplorerNamespace. .EXAMPLE C:\> Get-FileExplorerNamespace Get all file explorer namespaces for the current user. .EXAMPLE C:\> Get-FileExplorerNamespace -Id '018d5c66-4533-4307-9b53-224de2ed1fe6' Get the file explorer namespaces with the provided GUID, which in this case is OneDrive. .EXAMPLE C:\> Get-FileExplorerNamespace -Name 'OneDrive' Get the file explorer namespaces with the name OneDrive. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/WindowsFever #> function Get-FileExplorerNamespace { [CmdletBinding(DefaultParameterSetName = 'All')] param ( [Parameter(Mandatory = $false, Position = 0, ParameterSetName = 'Id')] [System.Guid] $Id = [Guid]::Empty, [Parameter(Mandatory = $false, Position = 0, ParameterSetName = 'Name')] [AllowEmptyString()] [System.String] $Name = [String]::Empty ) $namespaceItems = Get-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\*' foreach ($namespaceItem in $namespaceItems) { $namespaceId = [Guid] $namespaceItem.PSChildName $namespaceName = $namespaceItem.'(default)' if (($Id -eq [Guid]::Empty -or $Id -eq $namespaceId) -and ($Name -eq [String]::Empty -or $namespaceName -like $Name)) { $initPropertyBag = Get-ItemProperty "HKCU:\SOFTWARE\Classes\CLSID\{$namespaceId}\Instance\initPropertyBag" # Based on the property bag definition, use the corresponding type if ($null -ne $initPropertyBag.TargetKnownFolder) { $targetType = 'KnownFolder' $targetValue = $initPropertyBag.TargetKnownFolder } elseif ($null -ne $initPropertyBag.TargetFolderPath) { $targetType = 'FolderPath' $targetValue = $initPropertyBag.TargetFolderPath } else { $targetType = 'Unknown' $targetValue = '' } # Create a correctly typed output object $namespace = New-Object -TypeName PSObject -Property @{ Id = $namespaceId Name = $namespaceName Icon = $(try { Get-ItemProperty -Path "HKCU:\SOFTWARE\Classes\CLSID\{$namespaceId}\DefaultIcon" -ErrorAction Stop | Select-Object -ExpandProperty '(default)' } catch { 'Unknown' }) Order = $(try { Get-ItemProperty -Path "HKCU:\SOFTWARE\Classes\CLSID\{$namespaceId}" -ErrorAction Stop | Select-Object -ExpandProperty 'SortOrderIndex' } catch { 'Unknown' }) targetType = $targetType targetValue = $targetValue } $namespace.PSTypeNames.Insert(0, 'WindowsFever.FileExplorerNamespace') Write-Output $namespace } } } <# .SYNOPSIS Remove an existing file explorer namespace. .DESCRIPTION Remove an existing file explorer namespace. It removes all entries from the following registry keys. Take care, you can also remove an existing built-in file explorer namespace like OneDrive: - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel - HKCU:\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000} - HKCU:\SOFTWARE\Classes\Wow6432Node\CLSID\{00000000-0000-0000-0000-000000000000} You can find the reference for this implementation on MSDN. Even if it's intended for a Cloud Storage Provider, it will work for every local folder: - https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934 .PARAMETER Id The id of the file explorer namespace to delete. .INPUTS WindowsFever.FileExplorerNamespace. .OUTPUTS None. .EXAMPLE C:\> Remove-FileExplorerNamespace -Id 'e9ec969f-3e60-4be3-b2bb-1a5d04beacc1' Remove the file explorer namespace with the given id. .EXAMPLE C:\> Get-FileExplorerNamespace -Name 'Test' | Remove-FileExplorerNamespace Remove the file explorer namespace with the name 'Test'. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/WindowsFever #> function Remove-FileExplorerNamespace { [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [System.Guid[]] $Id ) process { foreach ($currentId in $Id) { if ($null -ne (Get-FileExplorerNamespace -Id $currentId)) { # The method ShouldProcess asks the user for confirmation or display just # the action we perform inside this if when the users uses -WhatIf if ($PSCmdlet.ShouldProcess($currentId, 'Remove')) { # Step 1: Remove CLSID class implementation foreach ($key in 'HKCU:\SOFTWARE\Classes\CLSID', 'HKCU:\SOFTWARE\Classes\Wow6432Node\CLSID') { if ((Test-Path -Path $key)) { Remove-Item -Path "$key\{$currentId}" -Recurse -Force } } # Step 2: Remove namespace extension from root Remove-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{$currentId}" -Recurse -Force # Step 3: Remove desktop hide feature Remove-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" -Name "{$currentId}" -Force } } else { Write-Warning -Message "The file explorer namespace with Id '$currentId' does not exists!" } } } } <# .SYNOPSIS Update properties of an existing file explorer namespace. .DESCRIPTION Update properties of an existing file explorer namespace in Windows 10. It updates the following registry paths to change the namespace, if required: - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace - HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel - HKCU:\SOFTWARE\Classes\CLSID\{00000000-0000-0000-0000-000000000000} - HKCU:\SOFTWARE\Classes\Wow6432Node\CLSID\{00000000-0000-0000-0000-000000000000} You can find the reference for this implementation on MSDN. Even if it's intended for a Cloud Storage Provider, it will work for every local folder: - https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934 .PARAMETER Id The GUID of the existing file explorer namespace. .PARAMETER Name The new name of the new file explorer namespace. .PARAMETER Icon The new icon of the new file explorer namespace. Please specify a target dll or exe with the id of the icon. .PARAMETER Order The new order of the new file explorer namespace. The order defines, where in the file explorer the new namespace will be visible. .INPUTS WindowsFever.FileExplorerNamespace. .OUTPUTS WindowsFever.FileExplorerNamespace. .EXAMPLE C:\> Get-FileExplorerNamespace -Name 'PowerShell' | Set-FileExplorerNamespace -Order 66 Update the order property of the existing PowerShell file explorer namespace to number 66. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/WindowsFever #> function Set-FileExplorerNamespace { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [System.Guid[]] $Id, [Parameter(Mandatory = $false)] [System.String] $Name, [Parameter(Mandatory = $false)] [System.String] $Icon, [Parameter(Mandatory = $false)] [System.Int32] $Order ) process { foreach ($currentId in $Id) { # For security reason, check if the namespace exists if ($null -ne (Get-FileExplorerNamespace -Id $currentId)) { throw "The file explorer namespace with Id '$currentId' does not exists!" } # The method ShouldProcess asks the user for confirmation or display # just the action we perform inside this if when the users uses # -WhatIf if ($PSCmdlet.ShouldProcess($currentId, 'Set')) { # Use the default and WOW64 node to place the class foreach ($Key in 'HKCU:\SOFTWARE\Classes\CLSID', 'HKCU:\SOFTWARE\Classes\Wow6432Node\CLSID') { if ((Test-Path -Path $Key)) { # Update the name if ($PSBoundParameters.Keys -contains 'Name') { Set-Item -Path "$Key\{$currentId}" -Value $Name -Force | Out-Null } # Update the icon if ($PSBoundParameters.Keys -contains 'Icon') { Set-Item -Path "$Key\{$currentId}\DefaultIcon" -Value $Icon -Force | Out-Null } # Update the order if ($PSBoundParameters.Keys -contains 'Order') { Set-ItemProperty -Path "$Key\{$currentId}" -Name 'SortOrderIndex' -Value $Order -Force | Out-Null } } } # Update the name if ($PSBoundParameters.Keys -contains 'Name') { Set-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{$currentId}" -Value $Name -Force | Out-Null } # Return the newly create file explorer namespace Get-FileExplorerNamespace -Id $currentId } } } } <# .SYNOPSIS Register an event watcher for file system change events. .DESCRIPTION Use the System.IO.FileSystemWatcher .NET class to watch for file system events like create, change, rename and delete. Thanks to the PowerShell cmdlet Register-ObjectEvent, you can provide script blocks to process the events. .PARAMETER Path Path to watch for changes. .PARAMETER Filter The file filter, be default any file (wildcard). .PARAMETER Recurse Optionally watch all subfolders and files. .PARAMETER NotifyFilter Change the events to listen on. By default for file name and last write. .PARAMETER CreatedAction Script block to execute, when the create-event happens. The variable $Event will contain all arguments and properties of the event. .PARAMETER ChangedAction Script block to execute, when the change-event happens. The variable $Event will contain all arguments and properties of the event. .PARAMETER RenamedAction Script block to execute, when the rename-event happens. The variable $Event will contain all arguments and properties of the event. .PARAMETER DeletedAction Script block to execute, when the delete-event happens. The variable $Event will contain all arguments and properties of the event. .INPUTS None. .OUTPUTS None .EXAMPLE C:\> Start-WatchPath -Path 'C:\Demo' -Filter 'file.txt' -ChangedAction { Write-Host "File changed: $($Event.SourceArgs[1].FullPath)" } Watch the C:\Demo\file.txt for changes and write them to the host. .EXAMPLE C:\> Start-WatchPath -Path 'C:\Windows' -Recurse -CreatedAction { Write-Host "File changed: $($Event.SourceArgs[1].FullPath)" } Watch for new files in the C:\Windows directory. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/WindowsFever #> function Start-WatchPath { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ })] [System.String] $Path, [Parameter(Mandatory = $false)] [System.String] $Filter = '*', [Parameter(Mandatory = $false)] [System.Management.Automation.SwitchParameter] $Recurse, [Parameter(Mandatory = $false)] [System.IO.NotifyFilters] $NotifyFilter = 'FileName, LastWrite', [Parameter(Mandatory = $false)] [System.Management.Automation.ScriptBlock] $CreatedAction, [Parameter(Mandatory = $false)] [System.Management.Automation.ScriptBlock] $ChangedAction, [Parameter(Mandatory = $false)] [System.Management.Automation.ScriptBlock] $RenamedAction, [Parameter(Mandatory = $false)] [System.Management.Automation.ScriptBlock] $DeletedAction ) $ErrorActionPreference = 'Stop' $Path = $Path.TrimEnd('\') if ($null -ne (Get-EventSubscriber -SourceIdentifier "PSWatchPath|$Path|*")) { throw "File System Watcher for $Path does already exist." } # Create the file system watcher and add all required properties $watcher = New-Object -TypeName 'System.IO.FileSystemWatcher' -ArgumentList $Path, $Filter $watcher.IncludeSubdirectories = $Recurse.IsPresent $watcher.NotifyFilter = $NotifyFilter # Register object events with provided script blocks if ($PSBoundParameters.Keys.Contains('CreatedAction') -and $PSCmdlet.ShouldProcess("Created event on $Path", 'Register')) { Register-ObjectEvent -InputObject $watcher -EventName 'Created' -SourceIdentifier "PSWatchPath|$Path|Created" -Action $CreatedAction } if ($PSBoundParameters.Keys.Contains('ChangedAction') -and $PSCmdlet.ShouldProcess("Changed event on $Path", 'Register')) { Register-ObjectEvent -InputObject $watcher -EventName 'Changed' -SourceIdentifier "PSWatchPath|$Path|Changed" -Action $ChangedAction } if ($PSBoundParameters.Keys.Contains('RenamedAction') -and $PSCmdlet.ShouldProcess("Renamed event on $Path", 'Register')) { Register-ObjectEvent -InputObject $watcher -EventName 'Renamed' -SourceIdentifier "PSWatchPath|$Path|Renamed" -Action $RenamedAction } if ($PSBoundParameters.Keys.Contains('DeletedAction') -and $PSCmdlet.ShouldProcess("Deleted event on $Path", 'Register')) { Register-ObjectEvent -InputObject $watcher -EventName 'Deleted' -SourceIdentifier "PSWatchPath|$Path|Deleted" -Action $DeletedAction } } <# .SYNOPSIS Stop registered watchers for file system change events. .DESCRIPTION Remove event subscribers for the existing path by getting all event subscribers and unregister all with the Unregister-Event command. .PARAMETER Path Path to stop watching changes. .INPUTS None. .OUTPUTS None .EXAMPLE C:\> Stop-WatchPath -Path 'C:\Demo' Stop watching the path C:\Demo for all events. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/WindowsFever #> function Stop-WatchPath { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ })] [System.String] $Path ) $ErrorActionPreference = 'Stop' $Path = $Path.TrimEnd('\') # Unregister all event subscribers for the path. Get-EventSubscriber -SourceIdentifier "PSWatchPath|$Path|*" -ErrorAction SilentlyContinue | Unregister-Event } |