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)
        {
            # For security reason, check if the namespace exists
            if ($null -eq (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, '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
            }
        }
    }
}

<#
    .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 -eq (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
}