WindowsSearchHelper.psm1

# WindowsSearchHelper

function Get-WindowsSearchPersistentHandler {
    # .SYNOPSIS
    # Queries all file types with associated filter handlers and lists their respective filter handlers.
    # .DESCRIPTION
    # Queries all file types with associated filter handlers and lists their respective filter handlers.
    # .OUTPUTS
    # List of PSObject.
    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[PSObject]])]
    param()

    $Records = [System.Collections.Generic.List[PSObject]]::new()

    try {
        $ClassesKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('Software\Classes', $False)
        $Extensions = $ClassesKey.GetSubKeyNames()
        foreach ($extension in $Extensions) {
            if (-not $extension.StartsWith('.')) { continue }
            Write-Verbose "Extension $extension"

            try {
                $extensionKey = $ClassesKey.OpenSubKey($extension, $False)
                if ($Null -eq $extensionKey) { continue }
                $progId = $extensionKey.GetValue('')

                try {
                    $phKey = $extensionKey.OpenSubKey('PersistentHandler', $False)
                    if ($Null -ne $phKey) {
                        $Records.Add([PSCustomObject]@{
                                Extension                 = $extension
                                ProgID                    = $Null
                                ProgIDCLSID               = $Null
                                DefaultPersistentHandler  = $phKey.GetValue('')
                                OriginalPersistentHandler = $phKey.GetValue('OriginalPersistentHandler')
                            })
                    }
                }
                finally { if ($Null -ne $phKey) { $phKey.Close() } }
            }
            finally { if ($Null -ne $extensionKey) { $extensionKey.Close() } }

            if (-not [string]::IsNullOrWhiteSpace($progId)) {
                try {
                    $progIdClsidKey = $ClassesKey.OpenSubKey("$progId\CLSID", $False)
                    if ($Null -ne $progIdClsidKey) {
                        $clsid = $progIdClsidKey.GetValue('')

                        try {
                            $phKey = $ClassesKey.OpenSubKey("CLSID\$clsid\PersistentHandler", $False)
                            if ($Null -ne $phKey) {
                                $Records.Add([PSCustomObject]@{
                                        Extension                 = $extension
                                        ProgID                    = $progId
                                        ProgIDCLSID               = $clsid
                                        DefaultPersistentHandler  = $phKey.GetValue('')
                                        OriginalPersistentHandler = $phKey.GetValue('OriginalPersistentHandler')
                                    })
                            }
                        }
                        finally { if ($Null -ne $phKey) { $phKey.Close() } }
                    }
                }
                finally { if ($Null -ne $progIdClsidKey) { $progIdClsidKey.Close() } }
            }
        }
    }
    finally { if ($Null -ne $ClassesKey) { $ClassesKey.Close() } }
    return $Records
}

function Disable-WindowsSearchContentIndexing {
    # .SYNOPSIS
    # Associates all file types that have associated filter handlers with the Null filter handler.
    # .DESCRIPTION
    # Associates all file types that have associated filter handlers with the Null filter handler.
    [CmdletBinding(SupportsShouldProcess = $true)]
    param()

    $EmptyHandler = [Guid]::Empty.ToString('B')
    $NullHandler = '{098f2470-bae0-11cd-b579-08002b30bfeb}'

    try {
        $ClassesKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('Software\Classes', -not $WhatIfPreference)
        $Extensions = $ClassesKey.GetSubKeyNames()
        foreach ($extension in $Extensions) {
            if (-not $extension.StartsWith('.')) { continue }
            Write-Verbose "Extension $extension"
            $phKeyExist = $False
            $default = $Null
            $defaultExist = $False
            $original = $Null

            try {
                $extensionKey = $ClassesKey.OpenSubKey($extension, $False)
                if ($Null -eq $extensionKey) { continue }
                $progId = $extensionKey.GetValue('')

                try {
                    $phKey = $extensionKey.OpenSubKey('PersistentHandler', $False)
                    if ($Null -ne $phKey) {
                        $phKeyExist = $True
                        $default = $phKey.GetValue('')
                        if ($default -notin $Null, $EmptyHandler) {
                            $defaultExist = $True
                        }
                        $original = $phKey.GetValue('OriginalPersistentHandler')
                    }
                }
                finally { if ($Null -ne $phKey) { $phKey.Close() } }
            }
            finally { if ($Null -ne $extensionKey) { $extensionKey.Close() } }

            if ((-not $defaultExist) -and (-not [string]::IsNullOrWhiteSpace($progId))) {
                try {
                    $progIdClsidKey = $ClassesKey.OpenSubKey("$progId\CLSID", $False)
                    if ($Null -ne $progIdClsidKey) {
                        $clsid = $progIdClsidKey.GetValue('')

                        try {
                            $phKey = $ClassesKey.OpenSubKey("CLSID\$clsid\PersistentHandler", $False)
                            if ($Null -ne $phKey) {
                                $default = $phKey.GetValue('')
                            }
                        }
                        finally { if ($Null -ne $phKey) { $phKey.Close() } }
                    }
                }
                finally { if ($Null -ne $progIdClsidKey) { $progIdClsidKey.Close() } }
            }

            if ($default -notin $Null, $EmptyHandler, $NullHandler) {
                $name = $extension.PadRight(10)
                try {
                    if ($phKeyExist) {
                        $phKey = $ClassesKey.OpenSubKey("$extension\PersistentHandler", -not $WhatIfPreference)
                    }
                    else {
                        Write-Host "Extension $name Create PersistentHandler" -ForegroundColor Cyan
                        if ($PSCmdlet.ShouldProcess("HKLM:\Software\Classes\$extension\PersistentHandler", 'CreateSubKey')) {
                            $phKey = $ClassesKey.CreateSubKey("$extension\PersistentHandler")
                        }
                    }
                    Write-Host "Extension $name Set PersistentHandler to $NullHandler" -ForegroundColor Cyan
                    if ($PSCmdlet.ShouldProcess("HKLM:\Software\Classes\$extension\PersistentHandler\(Default)", 'SetValue')) {
                        $phKey.SetValue('', $NullHandler)
                    }
                    if ($Null -eq $original) {
                        $original = if ($defaultExist) { $default } else { $EmptyHandler }
                        Write-Host "Extension $name Set OriginalPersistentHandler to $original" -ForegroundColor Cyan
                        if ($PSCmdlet.ShouldProcess("HKLM:\Software\Classes\$extension\PersistentHandler\OriginalPersistentHandler", 'SetValue')) {
                            $phKey.SetValue('OriginalPersistentHandler', $original)
                        }
                    }
                }
                finally { if ($Null -ne $phKey) { $phKey.Close() } }
            }
        }
    }
    finally { if ($Null -ne $ClassesKey) { $ClassesKey.Close() } }
}

function Enable-WindowsSearchContentIndexing {
    # .SYNOPSIS
    # Re-associates all file types previously associated with a non-Null filter handler back to their original filter handlers.
    # .DESCRIPTION
    # Re-associates all file types previously associated with a non-Null filter handler back to their original filter handlers.
    [CmdletBinding(SupportsShouldProcess = $true)]
    param()

    $EmptyHandler = [Guid]::Empty.ToString('B')
    $NullHandler = '{098f2470-bae0-11cd-b579-08002b30bfeb}'

    try {
        $ClassesKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('Software\Classes', $False)
        $Extensions = $ClassesKey.GetSubKeyNames()
        foreach ($extension in $Extensions) {
            if (-not $extension.StartsWith('.')) { continue }
            Write-Verbose "Extension $extension"

            try {
                $phKey = $ClassesKey.OpenSubKey("$extension\PersistentHandler", -not $WhatIfPreference)
                if ($Null -eq $phKey) { continue }
                $default = $phKey.GetValue('')
                $original = $phKey.GetValue('OriginalPersistentHandler')

                if (($NullHandler -eq $default) -and ($original -notin $Null, $NullHandler)) {
                    $name = $extension.PadRight(10)
                    if ($EmptyHandler -ne $original) {
                        Write-Host "Extension $name Set PersistentHandler to $original" -ForegroundColor Cyan
                        if ($PSCmdlet.ShouldProcess("HKLM:\Software\Classes\$extension\PersistentHandler\(Default)", 'SetValue')) {
                            $phKey.SetValue('', $original)
                        }
                    }
                    else {
                        Write-Host "Extension $name Delete PersistentHandler eq $default" -ForegroundColor Cyan
                        if ($PSCmdlet.ShouldProcess(
                                "HKLM:\Software\Classes\$extension\PersistentHandler\(Default)", 'DeleteValue')) {
                            $phKey.DeleteValue('')
                        }
                    }
                    Write-Host "Extension $name Delete OriginalPersistentHandler eq $original" -ForegroundColor Cyan
                    if ($PSCmdlet.ShouldProcess(
                            "HKLM:\Software\Classes\$extension\PersistentHandler\OriginalPersistentHandler", 'DeleteValue')) {
                        $phKey.DeleteValue('OriginalPersistentHandler')
                    }
                }
            }
            finally { if ($Null -ne $phKey) { $phKey.Close() } }
        }
    }
    finally { if ($Null -ne $ClassesKey) { $ClassesKey.Close() } }
}

function Register-WindowsSearchContentIndexingDisablementScheduledTask {
    # .SYNOPSIS
    # Registers a scheduled task that runs Disable-WindowsSearchContentIndexing.
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [String]$TaskName = 'Windows Search Disable Content Indexing',
        [String]$TaskPath = '\',
        [TimeSpan]$RepetitionInterval = (New-TimeSpan -Hours 24),
        [TimeSpan]$StartupDelay = (New-TimeSpan -Hours 1)
    )

    $Principal = New-ScheduledTaskPrincipal -RunLevel Highest -UserId 'NT AUTHORITY\SYSTEM' -LogonType ServiceAccount

    $Action = New-ScheduledTaskAction -Execute 'powershell.exe' `
        -Argument '-NoProfile -WindowStyle Hidden -Command "Disable-WindowsSearchContentIndexing"'

    $RepetitionClass = Get-CimClass -Namespace Root/Microsoft/Windows/TaskScheduler -ClassName MSFT_TaskRepetitionPattern
    $Repetition = New-CimInstance -CimClass $RepetitionClass -ClientOnly -Property @{
        Interval          = [System.Xml.XmlConvert]::ToString($RepetitionInterval)
        StopAtDurationEnd = $False
    }

    $TriggerClass = Get-CimClass -Namespace Root/Microsoft/Windows/TaskScheduler -ClassName MSFT_TaskBootTrigger
    $Trigger = New-CimInstance -CimClass $TriggerClass -ClientOnly -Property @{
        Delay      = [System.Xml.XmlConvert]::ToString($StartupDelay)
        Repetition = $Repetition
    }

    $Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -StartWhenAvailable

    if ($PSCmdlet.ShouldProcess("$TaskPath$TaskName", 'Register-ScheduledTask')) {
        Register-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath `
            -Principal $Principal -Action $Action -Settings $Settings -Trigger $Trigger -Force | Out-Null
    }
}

Export-ModuleMember -Function `
    Get-WindowsSearchPersistentHandler, `
    Disable-WindowsSearchContentIndexing, `
    Enable-WindowsSearchContentIndexing, `
    Register-WindowsSearchContentIndexingDisablementScheduledTask