PSSpecialFolder.psm1

using namespace Microsoft.Win32
using namespace System.Diagnostics
using namespace System.Diagnostics.CodeAnalysis
using namespace System.Drawing
using namespace System.IO
using namespace System.Management.Automation
using namespace System.Windows
using namespace System.Windows.Controls
using namespace System.Windows.Input
using namespace System.Windows.Interop
using namespace System.Windows.Markup
using namespace Win32API

param()

Set-StrictMode -Version Latest

if ([Environment]::OSVersion.Platform -ne 'Win32NT') {
    throw [PlatformNotSupportedException]'The PSSpecialFolder module supports Windows only.'
    return
}

class OS {
    static [bool]$Win10 # Win10以降
    static [bool]$Win10_1607 # Win10 1607以降
    static [bool]$Win10_1803 # Win10 1803以降
    static [bool]$Win10_20h2 # Win10 20H2以降
    static [bool]$Win10_22h2_Only # Win10 22H2のみ
    static [bool]$Win11 # Win11以降
    static [bool]$Win11_21h2_Only # Win11 21H2のみ
    static [bool]$Win11_22h2 # Win11 22H2以降
    static [bool]$Win11_22h2_Only # Win11 22H2のみ
    static [bool]$Win11_22h2_moment4 # Win11 22H2 Moment4以降
    static [bool]$Win11_23h2 # Win11 23H2以降

    static OS() {
        $os = & $PSScriptRoot\Get-OSVersion.ps1
        $version = $os['Version']
        $verString = $os['VersionString']

        [OS]::Win10 = $version -gt '10.0.10240'
        [OS]::Win10_1607 = $version -gt '10.0.14393'
        [OS]::Win10_1803 = $version -gt '10.0.17134'
        [OS]::Win10_20h2 = $version -gt '10.0.19042'
        [OS]::Win10_22h2_Only = $verString -eq '10.0.19045'
        [OS]::Win11 = $version -gt '10.0.22000'
        [OS]::Win11_22h2 = $version -gt '10.0.22621'
        [OS]::Win11_22h2_Only = $version -eq '10.0.22621'
        [OS]::Win11_22h2_moment4 = $version -ge '10.0.22621.2361'
        [OS]::Win11_23h2 = $version -gt '10.0.22631'
    }
}

do {
    if ([OS]::Win11_23h2) { break }
    if ([OS]::Win11_22h2_Only) { break }
    if ([OS]::Win10_22h2_Only) { break }

    if ([OS]::Win10) {
        Write-Warning 'The PSSpecialFolder module supports Windows 10 Version 22H2+.'
    } else {
        Write-Warning 'The PSSpecialFolder module supports Windows 10 and 11.'
    }
} while ($false)


function getItem {
    [OutputType([Microsoft.Win32.RegistryKey])]
    param ([string]$Path)

    return Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue
}

function getItemValue {
    param ([RegistryKey]$Key, [string]$Name)

    if (!$Key) { return }
    return $Key.GetValue($Name)
}

# pwsh.exeがあるフォルダーにパスが通っていない場合もあるのでAPIから取得する
$powershellPath = [Process]::GetCurrentProcess().Path
# ISEなど場合もあるので名前を明示する
if ($powershellPath -notmatch '\\(?:powershell|pwsh)\.exe$') {
    $powershellPath = `
        if ($PSVersionTable['PSVersion'].Major -le 5) { 'powershell.exe' } `
        elseif (!(Get-Command pwsh.exe -ErrorAction SilentlyContinue)) { 'powershell.exe' } `
        else { 'pwsh.exe' }
}

$isWslEnabled = Test-Path 'HKLM:\SOFTWARE\Classes\Directory\shell\WSL\command'
$canFolderBeOpenedAsAdmin = `
    !(getItemValue (getItem 'HKLM:\SOFTWARE\Classes\AppID\{CDCBCFCA-3CDC-436f-A4E2-0E02075250C2}') RunAs)

enum PropertyTypes {
    None
    StartProcess
    Verb
    NotSupported
}

class SpecialFolder {
    [string]$Name
    [string]$Path

    hidden [string]$Dir
    hidden [__ComObject]$FolderItem
    hidden [PropertyTypes]$PropertyTypes
    hidden [__ComObject]$PropertiesVerb
    hidden [__ComObject]$FolderItemForProperties
    hidden [string]$Category
    # Save-List.ps1で使用
    hidden [string]$ClassName

    [string]ToString() {
        return "$($this.Name) [$($this.Path)]"
    }

    hidden [void]Open([string]$Verb) {
        Start-Process explorer.exe $(if ($this.Dir) { $this.Dir } else { $this.Path }) -Verb $Verb
    }
    hidden [void]SetProperties() {
        if ($this.PropertyTypes -ne 'None') { return }
        if ($this -is [FileFolder] -and !$this.FolderItemForProperties) {
            $this.PropertyTypes = 'StartProcess'
            return
        }

        $item = $this.FolderItemForProperties
        if ($null -eq $item) { $item = $this.FolderItem }

        $verbs = $item.Verbs()
        if ($verbs -and $verbs.Count) {
            $verb = $verbs.Item($verbs.Count - 1)
            if ($verb.Name -eq $script:propertiesName) {
                $this.PropertiesVerb = $verb
                $this.PropertyTypes = 'Verb'
                return
            }
        }

        $this.PropertyTypes = 'NotSupported'
    }
    hidden [bool]HasProperties() {
        $this.SetProperties()
        return $this.PropertyTypes -ne 'NotSupported'
    }
    hidden [void]Properties() {
        switch ($this.PropertyTypes) {
            StartProcess { Start-Process $this.Dir -Verb properties }
            Verb { $this.PropertiesVerb.DoIt() }
            NotSupported { throw [InvalidOperationException]'The properties of this folder can''t be shown.' }
            None {
                $this.SetProperties()
                $this.Properties()
            }
        }
    }
    hidden [void]Powershell([string]$Verb) {
        throw [InvalidOperationException]'This is not a directory.'
    }
    hidden [void]Cmd([string]$Verb) {
        throw [InvalidOperationException]'This is not a directory.'
    }
}

class FileFolder: SpecialFolder {
    hidden [void]Powershell([string]$Verb) {
        $startArgs = @{
            FilePath = $script:powershellPath
            ArgumentList = "-NoExit -Command `"Push-Location -LiteralPath '$($this.Path)'`""
            Verb = $Verb
        }
        Start-Process @startArgs
    }
    hidden [void]Cmd([string]$Verb) {
        Start-Process cmd.exe "/k pushd `"$($this.Path)`"" -Verb $Verb
    }
    hidden [void]Wsl([string]$Verb) {
        Start-Process wsl.exe "--cd `"$($this.Path)`"" -Verb $Verb
    }
}

Add-Type -ErrorAction Stop `
    -TypeDefinition (Get-Content -LiteralPath "$PSScriptRoot\KnownFolder.cs" -Raw -ErrorAction Stop)


$shell = New-Object -ComObject Shell.Application
$propertiesName = @($shell.NameSpace([Environment+SpecialFolder]::Desktop).Self.Verbs())[-1].Name

# PSSpecialFolder.Tests.ps1を実行するときに変数未定義エラーにならないようにするためのダミー
$categoryName = ''

function newSpecialFolder {
    [OutputType([SpecialFolder])]
    param ([string]$Dir, [string]$Name = '', [string]$Path = '', [__ComObject]$FolderItemForProperties = $null)

    if (!$Dir) { return }
    if ($Dir -match '^\\\\') { $Dir = 'file:' + $Dir }
    elseif ($Dir -notmatch '^(?:file|shell):') { $Dir = "file:\\\$Dir" }

    try { $folder = $shell.NameSpace($Dir) }
    catch { return }

    if (!$folder) { return }
    $folderItem = $shell.NameSpace($Dir).Self

    if (!$Path) { $Path = $folderItem.Path -replace '^::', 'shell:::' }

    $isFileFolder = Test-Path $path -PathType Container
    $className = if ($Dir -match '^shell:.*::(\{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\})$') {
        getItemValue (getItem "Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT\CLSID\$($Matches[1])")
    }

    $initializer = @{
        Name = $(
            if ($Name) { $Name }
            elseif ($Dir -match '^shell:((?:\w|\s)+)$') { $Matches[1] }
            elseif ($className) { $className }
            else { $Dir -replace '^.+\\(.+?)$', '$1' }
        )
        Path = $Path
        Dir = $Dir
        FolderItem = $folderItem
        FolderItemForProperties = $FolderItemForProperties
        Category = $categoryName
        ClassName = if ($Name) { $className }
    }
    return $(if ($isFileFolder) { [FileFolder]$initializer } else { [SpecialFolder]$initializer })
}

function newShellCommand {
    [OutputType([SpecialFolder])]
    param ([string]$Clsid, [string]$Name = '')

    if (!$Clsid) { return }

    $path = "Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT\CLSID\$Clsid"
    if (!(Test-Path $path)) { return }
    $className = getItemValue (getItem $path)

    return [SpecialFolder]@{
        Name = if ($Name) { $Name } else { $className }
        Path = "shell:::$Clsid"
        PropertyTypes = 'NotSupported'
        Category = $categoryName
        ClassName = if ($Name) { $className }
    }
}

function getDirectoryFolderItem {
    [OutputType([__ComObject])]
    param ([string]$path)

    return $shell.NameSpace((Split-Path $path)).Items().Item((Split-Path $path -Leaf))
}

$folderGuids = & $PSScriptRoot\FolderGuids.ps1

function getKnownFolderPath {
    [OutputType([string])]
    param ([string]$Name)

    $folder = [KnownFolder]::new($folderGuids[$Name], 0)
    if ($folder.Result -eq 'OK') { return $folder.Path }
}

function getSpecialFolder {
    # $categoryNameはnewSpecialFolderやnewShellCommand関数で参照する
    [SuppressMessage('PSUseDeclaredVarsMoreThanAssignments', 'categoryName')]

    [OutputType([SpecialFolder[]])]
    param ([bool]$IncludeShellCommand)

    $is64bitOS = [Environment]::Is64BitOperatingSystem
    $isWow64 = $is64bitOS -and ![Environment]::Is64BitProcess

    $currentVersionKey = getItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
    $appxKey = getItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx'
    $isShfusionRegistered = (Test-Path 'HKLM:\SOFTWARE\Classes\CLSID\{1D2680C9-0E2A-469d-B787-065558BC7D43}') `
        -and ($shell.NameSpace('shell:Windows\assembly').Items().Count() -eq 1)

    #region Category: User's Files
    $categoryName = 'User''s Files'

    # shell:Profile
    # shell:::{59031A47-3F72-44A7-89C5-5595FE6B30EE}
    # shell:ThisDeviceFolder / shell:::{F8278C54-A712-415B-B593-B77A2BE0DDA9} (Win10 1703から)
    # %USERPROFILE%
    # %HOMEDRIVE%%HOMEPATH%
    newSpecialFolder 'shell:UsersFilesFolder' -FolderItemForProperties $shell.NameSpace([Environment+SpecialFolder]::UserProfile).Self
    # shell:MyComputerFolder\::{B4BFCC3A-DB2C-424C-B029-7FE99A87C641} (Win11 23H2まで)
    # shell:::{B4BFCC3A-DB2C-424C-B029-7FE99A87C641} (Win11 24H2から)
    newSpecialFolder 'shell:ThisPCDesktopFolder' 'DesktopFolder'
    # shell:Local Documents / shell:MyComputerFolder\::{D3162B92-9365-467A-956B-92703ACA08AF} (Win11 23H2まで)
    # shell:::{D3162B92-9365-467A-956B-92703ACA08AF} (Win11 24H2から)
    # shell:::{450D8FBA-AD25-11D0-98A8-0800361B1103} ([My Documents])
    # shell:MyComputerFolder\::{A8CDFF1C-4878-43BE-B5FD-F8091C1C60D0} (Win11 23H2まで)
    # shell:::{A8CDFF1C-4878-43BE-B5FD-F8091C1C60D0} (Win11 24H2から)
    newSpecialFolder 'shell:Personal' 'My Documents'
    # shell:Local Downloads / shell:MyComputerFolder\::{088E3905-0323-4B02-9826-5D99428E115F} (Win11 23H2まで)
    # shell:::{088E3905-0323-4B02-9826-5D99428E115F} (Win11 24H2から)
    # shell:MyComputerFolder\::{374DE290-123F-4565-9164-39C4925E467B} (Win11 23H2まで)
    # shell:::{374DE290-123F-4565-9164-39C4925E467B} (Win11 24H2から)
    newSpecialFolder 'shell:Downloads'

    # shell:Local Music / shell:MyComputerFolder\::{3DFDF296-DBEC-4FB4-81D1-6A3438BCF4DE} (Win11 23H2まで)
    # shell:::{3DFDF296-DBEC-4FB4-81D1-6A3438BCF4DE} (Win11 24H2から)
    # shell:MyComputerFolder\::{1CF1260C-4DD0-4EBB-811F-33C572699FDE} (Win11 23H2まで)
    # shell:::{1CF1260C-4DD0-4EBB-811F-33C572699FDE} (Win11 24H2から)
    newSpecialFolder 'shell:My Music'
    # WMPやGroove ミュージックで再生リストを作成する時に自動生成される
    newSpecialFolder 'shell:Playlists'

    # shell:Local Pictures / shell:MyComputerFolder\::{24AD3AD4-A569-4530-98E1-AB02F9417AA8} (Win11 23H2まで)
    # shell:::{24AD3AD4-A569-4530-98E1-AB02F9417AA8} (Win11 24H2から)
    # shell:MyComputerFolder\::{3ADD1653-EB32-4CB0-BBD7-DFA0ABB5ACCA} (Win11 23H2まで)
    # shell:::{3ADD1653-EB32-4CB0-BBD7-DFA0ABB5ACCA} (Win11 24H2から)
    newSpecialFolder 'shell:My Pictures'
    # カメラアプリで写真や動画を撮影する時に自動生成される
    newSpecialFolder 'shell:Camera Roll'
    newSpecialFolder 'shell:SavedPictures'
    # Win+PrtScrでスクリーンショットを保存する時に自動生成される
    newSpecialFolder 'shell:Screenshots'
    newSpecialFolder 'shell:PhotoAlbums'

    # shell:Local Videos / shell:MyComputerFolder\::{F86FA3AB-70D2-4FC7-9C99-FCBF05467F3A} (Win11 23H2まで)
    # shell:::{F86FA3AB-70D2-4FC7-9C99-FCBF05467F3A} (Win11 24H2から)
    # shell:MyComputerFolder\::{A0953C92-50DC-43BF-BE83-3742FED03C9C} (Win11 23H2まで)
    # shell:::{A0953C92-50DC-43BF-BE83-3742FED03C9C} (Win11 24H2から)
    newSpecialFolder 'shell:My Video'
    # ゲームバーで動画やスクリーンショットを保存する時に自動生成される
    newSpecialFolder 'shell:Captures'

    # shell:::{0DB7E03F-FC29-4DC6-9020-FF41B59E513A} (Win10 1709から)
    # Win10 1703までは3D Builderを起動した時に自動生成される
    newSpecialFolder 'shell:3D Objects'
    # Win10 1703からサポート
    newSpecialFolder 'shell:AppMods'
    # shell:UsersFilesFolder\{56784854-C6CB-462B-8169-88E350ACB882}
    newSpecialFolder 'shell:Contacts'
    newSpecialFolder 'shell:Favorites'
    # shell:::{323CA680-C24D-4099-B94D-446DD2D7249E} ([Favorites])
    # shell:::{D34A6CA6-62C2-4C34-8A7C-14709C1AD938} ([Common Places FS Folder])
    # shell:UsersFilesFolder\{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}
    newSpecialFolder 'shell:Links'
    newSpecialFolder 'shell:Recorded Calls'
    # shell:UsersFilesFolder\{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}
    newSpecialFolder 'shell:SavedGames'
    # shell:UsersFilesFolder\{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}
    newSpecialFolder 'shell:Searches'
    #endregion

    #region Category: OneDrive
    $categoryName = 'OneDrive'

    # shell:::{59031A47-3F72-44A7-89C5-5595FE6B30EE}\::{018D5C66-4533-4307-9B53-224DE2ED1FE6}
    # %OneDrive% (Win10 1607から)
    newSpecialFolder 'shell:OneDrive'
    newSpecialFolder 'shell:OneDriveDocuments'
    newSpecialFolder 'shell:OneDriveMusic'
    newSpecialFolder 'shell:OneDrivePictures'
    newSpecialFolder 'shell:OneDriveCameraRoll'
    #endregion

    #region Category: AppData
    $categoryName = 'AppData'

    # %APPDATA%
    newSpecialFolder 'shell:AppData'
    newSpecialFolder 'shell:CredentialManager'
    newSpecialFolder 'shell:CryptoKeys'
    newSpecialFolder 'shell:DpapiKeys'
    newSpecialFolder 'shell:SystemCertificates'

    newSpecialFolder 'shell:Quick Launch'
    # shell:::{1F3427C8-5C10-4210-AA03-2EE45287D668}
    newSpecialFolder 'shell:User Pinned'
    newSpecialFolder 'shell:ImplicitAppShortcuts'

    newSpecialFolder 'shell:AccountPictures'
    # shell:::{B155BDF8-02F0-451E-9A26-AE317CFD7779} ([delegate folder that appears in Computer])
    newSpecialFolder 'shell:NetHood'
    # shell:::{ED50FC29-B964-48A9-AFB3-15EBB9B97F36} ([printhood delegate folder])
    newSpecialFolder 'shell:PrintHood'
    newSpecialFolder 'shell:Recent'
    newSpecialFolder 'shell:SendTo'
    newSpecialFolder 'shell:Templates'
    #endregion

    #region Category: Libraries
    $categoryName = 'Libraries'

    # shell:UsersLibrariesFolder
    # shell:::{031E4825-7B94-4DC3-B131-E946B44C8DD5}
    $librariesPath = getKnownFolderPath Libraries
    newSpecialFolder 'shell:Libraries' -Path $librariesPath -FolderItemForProperties (getDirectoryFolderItem $librariesPath)
    # shell:Libraries\{2B20DF75-1EDA-4039-8097-38798227D5B7}
    newSpecialFolder 'shell:CameraRollLibrary' -Path (getKnownFolderPath CameraRollLibrary)
    # shell:Libraries\{7B0DB17D-9CD2-4A93-9733-46CC89022E7C}
    newSpecialFolder 'shell:DocumentsLibrary' -Path (getKnownFolderPath DocumentsLibrary)
    # shell:Libraries\{2112AB0A-C86A-4FFE-A368-0DE96E47012E}
    newSpecialFolder 'shell:MusicLibrary' -Path (getKnownFolderPath MusicLibrary)
    # shell:Libraries\{A990AE9F-A03B-4E80-94BC-9912D7504104}
    newSpecialFolder 'shell:PicturesLibrary' -Path (getKnownFolderPath PicturesLibrary)
    # shell:Libraries\{E25B5812-BE88-4BD9-94B0-29233477B6C3}
    newSpecialFolder 'shell:SavedPicturesLibrary' -Path (getKnownFolderPath SavedPicturesLibrary)
    # shell:::{031E4825-7B94-4DC3-B131-E946B44C8DD5}\{491E922F-5643-4AF4-A7EB-4E7A138D8174}
    newSpecialFolder 'shell:VideosLibrary' -Path (getKnownFolderPath VideosLibrary)
    #endregion

    #region Category: StartMenu
    $categoryName = 'StartMenu'

    newSpecialFolder 'shell:Start Menu'
    newSpecialFolder 'shell:Programs'
    newSpecialFolder 'shell:Administrative Tools'
    newSpecialFolder 'shell:Startup'
    #endregion

    #region Category: LocalAppData
    $categoryName = 'LocalAppData'

    # %LOCALAPPDATA%
    newSpecialFolder 'shell:Local AppData'

    # Win10 1709からサポート
    newSpecialFolder 'shell:AppDataDesktop'
    newSpecialFolder 'shell:Development Files'
    # Win10 1709からサポート
    newSpecialFolder 'shell:AppDataDocuments'
    # Win10 1709からサポート
    newSpecialFolder 'shell:AppDataFavorites'
    # ストアアプリの設定
    newSpecialFolder 'shell:Local AppData\Packages' 'PackagedApps Data'
    # Win10 1709からサポート
    newSpecialFolder 'shell:AppDataProgramData'
    # %TEMP%
    # %TMP%
    newSpecialFolder ([Path]::GetTempPath()) 'Temporary Folder'
    newSpecialFolder 'shell:Local AppData\VirtualStore'

    newSpecialFolder 'shell:Application Shortcuts'
    newSpecialFolder 'shell:CD Burning'
    # Win10 1809からサポート
    # 標準ユーザー権限でフォントをインストールした時に自動生成される
    newSpecialFolder 'shell:Local AppData\Microsoft\Windows\Fonts' 'UserFonts'
    newSpecialFolder 'shell:GameTasks'
    newSpecialFolder 'shell:History'
    newSpecialFolder 'shell:Cache'
    newSpecialFolder 'shell:Cookies'
    newSpecialFolder 'shell:Ringtones'
    newSpecialFolder 'shell:Roamed Tile Images'
    newSpecialFolder 'shell:Roaming Tiles'
    newSpecialFolder 'shell:Local AppData\Microsoft\Windows\WinX'

    newSpecialFolder 'shell:SearchHistoryFolder'
    newSpecialFolder 'shell:SearchTemplatesFolder'

    newSpecialFolder 'shell:Local AppData\Microsoft\Windows Sidebar\Gadgets'
    # フォトギャラリーでファイルを編集する時に自動生成される
    newSpecialFolder 'shell:Original Images'

    newSpecialFolder 'shell:Local AppData\Microsoft\WindowsApps' 'AppExecutionAlias'

    newSpecialFolder 'shell:UserProgramFiles'
    newSpecialFolder 'shell:UserProgramFilesCommon'

    newSpecialFolder 'shell:LocalAppDataLow'
    #endregion

    #region Category: Public
    $categoryName = 'Public'

    # shell:::{4336A54D-038B-4685-AB02-99BB52D3FB8B}
    # shell:ThisDeviceFolder (Win10 1607まで)
    # shell:::{5B934B42-522B-4C34-BBFE-37A3EF7B9C90}
    # %PUBLIC%
    newSpecialFolder 'shell:Public'
    newSpecialFolder 'shell:PublicAccountPictures'
    newSpecialFolder 'shell:Common Desktop'
    newSpecialFolder 'shell:Common Documents'
    newSpecialFolder 'shell:CommonDownloads'
    newSpecialFolder 'shell:PublicLibraries'
    newSpecialFolder (getKnownFolderPath RecordedTVLibrary) 'RecordedTVLibrary'
    newSpecialFolder 'shell:CommonMusic'
    newSpecialFolder 'shell:SampleMusic'
    newSpecialFolder 'shell:CommonPictures'
    newSpecialFolder 'shell:SamplePictures'
    newSpecialFolder 'shell:CommonVideo'
    newSpecialFolder 'shell:SampleVideos'
    #endregion

    #region Category: ProgramData
    $categoryName = 'ProgramData'

    # %ALLUSERSPROFILE%
    # %ProgramData%
    newSpecialFolder 'shell:Common AppData'
    newSpecialFolder 'shell:OEM Links'

    newSpecialFolder (getItemValue $appxKey 'PackageRepositoryRoot') 'PackagedApps StateRepository'
    newSpecialFolder 'shell:Device Metadata Store'
    newSpecialFolder 'shell:PublicGameTasks'
    # 市販デモ モードで使用される
    newSpecialFolder 'shell:Retail Demo'
    newSpecialFolder 'shell:CommonRingtones'
    newSpecialFolder 'shell:Common Templates'
    #endregion

    #region Category: CommonStartMenu
    $categoryName = 'CommonStartMenu'

    newSpecialFolder 'shell:Common Start Menu'
    newSpecialFolder 'shell:Common Programs'
    # shell:ControlPanelFolder\::{D20EA4E1-3957-11D2-A40B-0C5020524153}
    newSpecialFolder 'shell:Common Administrative Tools'
    newSpecialFolder 'shell:Common Startup'
    newSpecialFolder 'shell:Common Start Menu Places'
    #endregion

    #region Category: Windows
    $categoryName = 'Windows'

    # %SystemRoot%
    # %windir%
    newSpecialFolder 'shell:Windows'
    # shell:::{1D2680C9-0E2A-469D-B787-065558BC7D43} ([Fusion Cache]) (.NET3.5まで)
    # %windir%\Microsoft.NET\Framework*\v2.0.50727\shfusion.dll を登録すると特殊フォルダー表示になる
    if ($isShfusionRegistered) { newSpecialFolder 'shell:Windows\assembly' '.NET Framework Assemblies' }
    newSpecialFolder (getItemValue (getItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings') 'ActiveXCache') 'ActiveX Cache Folder'
    # shell:ControlPanelFolder\::{BD84B380-8CA2-1069-AB1D-08000948F534}
    newSpecialFolder 'shell:Fonts'
    newSpecialFolder 'shell:Windows\Offline Web Pages' 'Subscription Folder'

    newSpecialFolder 'shell:ResourceDir'
    newSpecialFolder 'shell:LocalizedResourcesDir'

    newSpecialFolder $(if (!$isWow64) { 'shell:System' } else { 'shell:SystemX86' } )
    # Win10 1803からサポート
    newSpecialFolder "$Env:DriverData" 'DriverData'
    newSpecialFolder (getItemValue (getItem 'HKLM:\SOFTWARE\Microsoft\Wbem') 'Installation Directory') 'WMI'
    if ($is64bitOS) {
        newSpecialFolder $(if (!$isWow64) { 'shell:SystemX86' } else { 'shell:Windows\SysNative' } )
    }
    #endregion

    #region Category: UserProfiles
    $categoryName = 'UserProfiles'

    newSpecialFolder 'shell:UserProfiles'
    newSpecialFolder (getItemValue (getItem 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList') 'Default') 'DefaultUserProfile'
    #endregion

    #region Category: ProgramFiles
    $categoryName = 'ProgramFiles'

    # shell:ProgramFilesX64 (64ビットアプリのみ)
    # %ProgramFiles%
    newSpecialFolder 'shell:ProgramFiles'
    # shell:ProgramFilesCommonX64 (64ビットアプリのみ)
    # %CommonProgramFiles%
    newSpecialFolder 'shell:ProgramFilesCommon'
    newSpecialFolder (getItemValue $appxKey 'PackageRoot') 'PackagedApps'
    newSpecialFolder 'shell:ProgramFiles\Windows Sidebar\Gadgets' 'Default Gadgets'
    newSpecialFolder 'shell:ProgramFiles\Windows Sidebar\Shared Gadgets'

    if ($is64bitOS) {
        if (!$isWow64) { newSpecialFolder 'shell:ProgramFilesX86' }
        else { newSpecialFolder (getItemValue $currentVersionKey 'ProgramW6432Dir') 'ProgramFilesX64' }
    }
    if ($is64bitOS) {
        if (!$isWow64) { newSpecialFolder 'shell:ProgramFilesCommonX86' }
        else { newSpecialFolder (getItemValue $currentVersionKey 'CommonW6432Dir') 'ProgramFilesCommonX64' }
    }
    #endregion

    #region Category: Desktop / MyComputer
    $categoryName = 'Desktop / MyComputer'

    newSpecialFolder 'shell:Desktop'
    # shell:MyComputerFolderはWin10 1507/1511だとなぜかデスクトップになってしまう
    newSpecialFolder 'shell:MyComputerFolder'
    # Recent Places Folder
    newSpecialFolder 'shell:::{22877A6D-37A1-461A-91B0-DBDA5AAEBC99}'
    # shell:::{4564B25E-30CD-4787-82BA-39E73A750B14} ([Recent Items Instance Folder])
    newSpecialFolder 'shell:::{3134EF9C-6B18-4996-AD04-ED5912E00EB5}' 'Recent files'
    # Portable Devices
    newSpecialFolder 'shell:::{35786D3C-B075-49B9-88DD-029876E11C01}'
    # Frequent Places Folder
    newSpecialFolder 'shell:::{3936E9E4-D92C-4EEE-A85A-BC16D5EA0819}'
    newSpecialFolder 'shell:RecycleBinFolder'
    # (windows.storage.dll)
    # Win11 22H2からOtherDirs カテゴリに移動するので非表示に
    if (![OS]::Win11_22h2) { newSpecialFolder 'shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}' 'Quick access' }
    # Removable Storage Devices
    newSpecialFolder 'shell:::{A6482830-08EB-41E2-84C1-73920C2BADB9}'
    # Win10まで
    newSpecialFolder 'shell:HomeGroupFolder'
    # (shell32.dll)
    # Win11 23H2からサポート
    # Win11 22H2 Moment4ではKB5030509かKB5031455をインストールすると利用可
    newSpecialFolder 'shell:::{E88865EA-0E1C-4E20-9AA6-EDCD0212C87C}' 'Gallery'
    newSpecialFolder 'shell:NetworkPlacesFolder'
    # Removable Drives
    newSpecialFolder 'shell:::{F5FB2C77-0E2F-4A16-A381-3E560C68BC83}'
    # (windows.storage.dll)
    # Win11 22H2から
    if ([OS]::Win11_22h2) { newSpecialFolder 'shell:::{F874310E-B6B7-47DC-BC84-B9E6B38F5903}' 'Home' }
    #endregion

    #region Category: ControlPanel
    $categoryName = 'ControlPanel'

    # Control Panel
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}'
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\1' 'Appearance and Personalization'
    # shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\4
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\2' 'Hardware and Sound'
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\3' 'Network and Internet'
    # shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\10
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\5' 'System and Security'
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\6' $(if ([OS]::Win10_1803) { 'Clock and Region' } else { 'Clock, Language, and Region' })
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\7' 'Ease of Access'
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\8' 'Programs'
    newSpecialFolder 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\9' 'User Accounts'

    # shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}
    # shell:::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}
    # shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\11
    newSpecialFolder 'shell:ControlPanelFolder' 'All Control Panel Items'

    # コントロールパネル内の項目はCLSIDだけを指定してもアクセス可能
    # 例えば[電源オプション]なら shell:::{025A5937-A6BE-4686-A844-36FE4BEC8B6D}
    # ただしその場合はアドレスバーからコントロールパネルに移動できない

    # Power Options
    newSpecialFolder 'shell:ControlPanelFolder\::{025A5937-A6BE-4686-A844-36FE4BEC8B6D}'
    # Credential Manager
    newSpecialFolder 'shell:ControlPanelFolder\::{1206F5F1-0569-412C-8FEC-3204630DFB70}'
    newSpecialFolder 'shell:AddNewProgramsFolder'
    # Set User Defaults
    # shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{E44E5D18-0652-4508-A4E2-8A090067BCB0}
    # Win11 22H2でShellCommandsExceptFolders カテゴリに移動するので非表示に
    if (![OS]::Win11_22h2) { newSpecialFolder 'shell:ControlPanelFolder\::{17CD9488-1228-4B2F-88CE-4298E93E0966}' 'Default Programs' }
    # Workspaces Center
    newSpecialFolder 'shell:ControlPanelFolder\::{241D7C96-F8BF-4F85-B01F-E2B043341A4B}' 'RemoteApp and Desktop Connections'
    # Windows Firewall (Win10 1703まで)
    # Windows Defender Firewall (Win10 1709から)
    newSpecialFolder 'shell:ControlPanelFolder\::{4026492F-2F69-46B8-B9BF-5654FC07E423}'
    # Speech Recognition (Win11 23H2まで)
    newSpecialFolder 'shell:ControlPanelFolder\::{58E3C745-D971-4081-9034-86E34B30836A}'
    # User Accounts
    newSpecialFolder 'shell:ControlPanelFolder\::{60632754-C523-4B62-B45C-4172DA012619}'
    # HomeGroup Control Panel
    # Win10 1709までサポート
    newSpecialFolder 'shell:ControlPanelFolder\::{67CA7650-96E6-4FDD-BB43-A8E774F73A57}'
    # Network and Sharing Center
    newSpecialFolder 'shell:ControlPanelFolder\::{8E908FC9-BECC-40F6-915B-F4CA0E70D03D}'
    # AutoPlay
    newSpecialFolder 'shell:ControlPanelFolder\::{9C60DE1E-E5FC-40F4-A487-460851A8D915}'
    # System Recovery
    newSpecialFolder 'shell:ControlPanelFolder\::{9FE63AFD-59CF-4419-9775-ABCC3849F861}'
    # Device Center
    newSpecialFolder 'shell:ControlPanelFolder\::{A8A91A66-3A7D-4424-8D24-04E180695C7A}' 'Devices and Printers'
    # Windows 7 File Recovery
    newSpecialFolder 'shell:ControlPanelFolder\::{B98A2BEA-7D42-4558-8BD1-832F41BAC6FD}'
    # System
    # Win10 20H2から下部に移動するので非表示に
    if (![OS]::Win10_20h2) { newSpecialFolder 'shell:ControlPanelFolder\::{BB06C0E4-D293-4F75-8A90-CB05B6477EEE}' }
    # Security and Maintenance CPL
    newSpecialFolder 'shell:ControlPanelFolder\::{BB64F8A7-BEE7-4E1A-AB8D-7D8273F7FDB6}'
    # Microsoft Windows Font Folder
    # shell:Fonts
    newSpecialFolder 'shell:ControlPanelFolder\::{BD84B380-8CA2-1069-AB1D-08000948F534}' -Path 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\::{BD84B380-8CA2-1069-AB1D-08000948F534}'
    # Language Settings
    # Win10 1803までサポート
    newSpecialFolder 'shell:ControlPanelFolder\::{BF782CC9-5A52-4A17-806C-2A894FFEEAC5}'
    # Display
    # Win10 1607までサポート
    newSpecialFolder 'shell:ControlPanelFolder\::{C555438B-3C23-4769-A71F-B6D3D9B6053A}'
    # Troubleshooting
    # Win10 22H2 Moment4からShellCommandsExceptFoldersカテゴリに移動するので非表示に
    if (![OS]::Win11_22h2_moment4) { newSpecialFolder 'shell:ControlPanelFolder\::{C58C4893-3BE0-4B45-ABB5-A63E4B8C8651}' }
    # Administrative Tools (Win10まで)
    # Windows Tools (Win11 21H2から)
    # shell:Common Administrative Tools
    newSpecialFolder 'shell:ControlPanelFolder\::{D20EA4E1-3957-11D2-A40B-0C5020524153}' -Path 'shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\::{D20EA4E1-3957-11D2-A40B-0C5020524153}'
    # Ease of Access
    newSpecialFolder 'shell:ControlPanelFolder\::{D555645E-D4F8-4C29-A827-D93C859C4F2A}'
    # Secure Startup
    newSpecialFolder 'shell:ControlPanelFolder\::{D9EF8727-CAC2-4E60-809E-86F80A666C91}' 'BitLocker Drive Encryption'
    # ECS
    newSpecialFolder 'shell:ControlPanelFolder\::{ECDB0924-4208-451E-8EE0-373C0956DE16}' 'Work Folders'
    # Personalization Control Panel
    newSpecialFolder 'shell:ControlPanelFolder\::{ED834ED6-4B5A-4BFE-8F11-A626DCB6A921}'
    # History Vault
    newSpecialFolder 'shell:ControlPanelFolder\::{F6B6E965-E9B2-444B-9286-10C9152EDBC5}'
    # Storage Spaces
    newSpecialFolder 'shell:ControlPanelFolder\::{F942C606-0914-47AB-BE56-1321B8035096}'

    newSpecialFolder 'shell:ChangeRemoveProgramsFolder'
    # Win11 22H2でShellCommandsExceptFolders カテゴリに移動するので非表示に
    if (![OS]::Win11_22h2) { newSpecialFolder 'shell:AppUpdatesFolder' }

    newSpecialFolder 'shell:SyncCenterFolder'
    # shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{2E9E59C0-B437-4981-A647-9C34B9B90891} ([Sync Setup Folder])
    newSpecialFolder 'shell:SyncSetupFolder'
    newSpecialFolder 'shell:ConflictFolder'
    newSpecialFolder 'shell:SyncResultsFolder'

    # Taskbar
    newSpecialFolder 'shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{05D7B0F4-2121-4EFF-BF6B-ED3F69B894D9}' 'Notification Area Icons'
    # shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{863AA9FD-42DF-457B-8E4D-0DE1B8015C60}
    newSpecialFolder 'shell:PrintersFolder'
    # Bluetooth Devices
    newSpecialFolder 'shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{28803F59-3A75-4058-995F-4EE5503B023C}'
    # shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{992CFFA0-F557-101A-88EC-00DD010CCC48}
    newSpecialFolder 'shell:ConnectionsFolder'
    # Font Settings
    newSpecialFolder 'shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{93412589-74D4-4E4E-AD0E-E0CB621440FD}'
    # System
    # Win10 20H2からここに移動
    # Win11 21H2以前まで
    if ([OS]::Win10_20h2 -and ![OS]::Win11) { newSpecialFolder 'shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{BB06C0E4-D293-4F75-8A90-CB05B6477EEE}' }
    # All Tasks
    newSpecialFolder 'shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{ED7BA470-8E54-465E-825C-99712043E01C}'
    #endregion

    #region Category: OtherFolders
    $categoryName = 'OtherFolders'

    # Hyper-V Remote File Browsing
    # クライアントHyper-Vを有効にすると利用可
    newSpecialFolder 'shell:::{0907616E-F5E6-48D8-9D61-A91C3D28106D}'
    # Cabinet Shell Folder
    newSpecialFolder 'shell:::{0CD7A5C0-9F37-11CE-AE65-08002B2E1262}'
    # MS Graph Shared File Folder
    # Win11 22H2/23H2 Moment5からサポート
    newSpecialFolder 'shell:::{18F546F6-B34B-4B30-B4C6-5E88BED8BD84}'
    # Network
    newSpecialFolder 'shell:::{208D2C60-3AEA-1069-A2D7-08002B30309D}'
    # DLNA Media Servers Data Source
    newSpecialFolder 'shell:::{289AF617-1CC3-42A6-926C-E6A863F0E3BA}'
    # Results Folder
    newSpecialFolder 'shell:::{2965E715-EB66-4719-B53F-1672673BBEFA}'
    newSpecialFolder 'shell:AppsFolder'
    # MS Graph Recent File Folder
    # Win11 21H2からサポート
    newSpecialFolder 'shell:::{42254EE9-E625-4065-8F70-775090256F72}'
    # Command Folder
    newSpecialFolder 'shell:::{437FF9C0-A07F-4FA0-AF80-84B6C6440A16}'
    # Other Users Folder
    newSpecialFolder 'shell:::{6785BFAC-9D2D-4BE5-B7E2-59937E8FB80A}'
    # search:
    # search-ms:
    newSpecialFolder 'shell:SearchHomeFolder'
    # Recommendations File Folder
    # Win11 23H2からサポート
    # Win11 22H2 Moment4ではKB5030509かKB5031455をインストールすると利用可
    newSpecialFolder 'shell:::{AD182E17-4754-4742-8529-C11EEEF0C299}'
    # Win10 1511までサポート
    newSpecialFolder 'shell:StartMenuAllPrograms'
    # (cscui.dll)
    # 企業向けエディションで使用可
    newSpecialFolder 'shell:::{AFDB1F70-2A4C-11D2-9039-00C04F8EEB3E}' 'Offline Files Folder'
    # Linux
    # Win11 21H2から
    # WSLを有効にすると利用可
    newSpecialFolder 'shell:::{B2B4A4D1-2754-4140-A2EB-9A76D9D7CDC6}'
    # AppSuggestedLocations
    newSpecialFolder 'shell:::{C57A6066-66A3-4D91-9EB9-41532179F0A5}'
    # Win10 1709までサポート
    newSpecialFolder 'shell:Games'
    # Previous Versions Results Folder
    newSpecialFolder 'shell:::{F8C2AB3B-17BC-41DA-9758-339D7DBF2D88}'
    #endregion

    if (!$IncludeShellCommand) { return }

    #region Category: ShellCommandsExceptFolders
    # フォルダー以外のshellコマンド
    $categoryName = 'ShellCommandsExceptFolders'

    # System
    # Win10 20H2から
    # Win11 21H2から下部に移動するので非表示に
    if ([OS]::Win10_20h2 -and ![OS]::Win11) { newSpecialFolder 'shell:ControlPanelFolder\::{BB06C0E4-D293-4F75-8A90-CB05B6477EEE}' }
    # Troubleshooting
    # Win10 22H2 Moment4からここに移動
    if ([OS]::Win11_22h2_moment4) { newSpecialFolder 'shell:ControlPanelFolder\::{C58C4893-3BE0-4B45-ABB5-A63E4B8C8651}' }

    # Win11 22H2からここに移動
    if ([OS]::Win11_22h2) { newSpecialFolder 'shell:AppUpdatesFolder' 'Uninstalled updates' }

    # Taskbar
    newShellCommand '{0DF44EAA-FF21-4412-828E-260A8728E7F1}'
    # Set User Defaults
    # Win11 22H2からここに移動
    if ([OS]::Win11_22h2) { newSpecialFolder 'shell:::{17CD9488-1228-4B2F-88CE-4298E93E0966}' 'Default apps' }
    # Search
    # Win10 1511まで
    if (![OS]::Win10_1607) { newShellCommand '{2559A1F0-21D7-11D4-BDAF-00C04F60B9F0}' 'Search Files' }
    # Run...
    newShellCommand '{2559A1F3-21D7-11D4-BDAF-00C04F60B9F0}'
    # Set Program Access and Defaults
    newShellCommand '{2559A1F7-21D7-11D4-BDAF-00C04F60B9F0}'
    # (shell32.dll#SearchCommand)
    newShellCommand '{2559A1F8-21D7-11D4-BDAF-00C04F60B9F0}' 'Search'
    # Learn about this picture
    # Windows スポットライトを有効にすると利用可
    newShellCommand '{2CC5CA98-6485-489A-920E-B3E88A6CCCE3}'
    # Show Desktop
    # Win+Dと同じ
    newShellCommand '{3080F90D-D7AD-11D9-BD98-0000947B0257}'
    # Window Switcher
    # Win10 1607以降ではWin+Tabと同じ (Win10 1507/1511では使用不可)
    newShellCommand '{3080F90E-D7AD-11D9-BD98-0000947B0257}'
    # Phone and Modem Control Panel
    newShellCommand '{40419485-C444-4567-851A-2DD7BFA1684D}'
    # Open in new window (shell32.dll)
    newShellCommand '{52205FD8-5DFB-447D-801A-D0B52F2E83E1}' 'File Explorer'
    # Mobility Center Control Panel
    newShellCommand '{5EA4F148-308C-46D7-98A9-49041B1DD468}'
    # Region and Language
    newShellCommand '{62D8ED13-C9D0-4CE8-A914-47DD628FB1B0}'
    # Windows Features
    newShellCommand '{67718415-C450-4F3C-BF8A-B487642DC39B}'
    # Mouse Control Panel
    newShellCommand '{6C8EEC18-8D75-41B2-A177-8831D59D2D50}'
    # Folder Options
    newShellCommand '{6DFD7C5C-2451-11D3-A299-00C04F8EF6AF}'
    # Keyboard Control Panel
    newShellCommand '{725BE8F7-668E-4C7B-8F90-46BDB0936430}'
    # Device Manager
    newShellCommand '{74246BFC-4C96-11D0-ABEF-0020AF6B0B7A}'
    # User Accounts
    # netplwiz.exe / control.exe userpasswords2
    newShellCommand '{7A9D77BD-5403-11D2-8785-2E0420524153}'
    # Tablet PC Settings Control Panel
    newShellCommand '{80F3F1D5-FECA-45F3-BC32-752C152E456E}'
    # Indexing Options Control Panel
    newShellCommand '{87D66A43-7B11-4A28-9811-C86EE395ACF7}'
    # Portable Workspace Creator
    # Win10 1909まで
    # Enterpriseで使用可
    # Win10 1607以降ではProでも使用可
    newShellCommand '{8E0C279D-0BD1-43C3-9EBD-31C3DC5B8A77}' 'Windows To Go'
    # Infrared
    # Win10 1607から1809まで
    newShellCommand '{A0275511-0E86-4ECA-97C2-ECD8F1221D08}'
    # Internet Options
    newShellCommand '{A3DD4F92-658A-410F-84FD-6FBBBEF2FFFE}'
    # Color Management
    newShellCommand '{B2C761C6-29BC-4F19-9251-E6195265BAF1}'
    # Win11 21H2から
    if ([OS]::Win11) { newShellCommand '{B4FB3F98-C1EA-428D-A78A-D1F5659CBA93}' 'Media streaming options' }
    # System
    # Win11 21H2からここに移動
    if ([OS]::Win11) { newSpecialFolder 'shell:::{BB06C0E4-D293-4F75-8A90-CB05B6477EEE}' }
    # Text to Speech Control Panel
    newShellCommand '{D17D1D6D-CC3F-4815-8FE3-607E7D5D10B3}'
    # Add Network Place
    newShellCommand '{D4480A50-BA28-11D1-8E75-00C04FA31A86}'
    # Windows Defender
    # Win10 1607まで
    newShellCommand '{D8559EB9-20C0-410E-BEDA-7ED416AECC2A}'
    # Date and Time Control Panel
    newShellCommand '{E2E7934B-DCE5-43C4-9576-7FE4F75E7480}'
    # Sound Control Panel
    newShellCommand '{F2DDFC82-8F12-4CDD-B7DC-D4FE1425AA4D}'
    # Pen and Touch Control Panel
    newShellCommand '{F82DF8F7-8B9F-442E-A48C-818EA735FF9B}'
    #endregion
}

function Get-SpecialFolder {
    <#
    .SYNOPSIS
    Gets the special folders for Windows. This function supports the virtual folders, e.g. Control Panel and Recycle Bin.
    .OUTPUTS
    SpecialFolder[]
    #>


    [CmdletBinding()]
    [OutputType([SpecialFolder[]])]
    param ([switch]$IncludeShellCommand)

    return getSpecialFolder $IncludeShellCommand | Where-Object { $_ }
}

$folderKeys = $folderGuids.Keys

function Get-SpecialFolderPath {
    <#
    .SYNOPSIS
    Gets the path of the special folders for Windows.
    .DESCRIPTION
    Gets the path of the special folders for Windows. This function supports the virtual folders, e.g. ControlPanelFolder and RecycleBinFolder.
    .PARAMETER Name
    The name of the special folder.
    .OUTPUTS
    System.String
    .EXAMPLE
    PS >Get-SpecialFolderPath System
 
    C:\Windows\system32
    .EXAMPLE
    PS >Get-SpecialFolderPath MyComputerFolder
 
    shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
    #>


    [CmdletBinding()]
    [OutputType([string])]
    param (
        [Parameter(Mandatory, Position = 0)]
        [ValidateSet(
            '3D Objects',
            'AccountPictures',
            'AddNewProgramsFolder',
            'Administrative Tools',
            'AppData',
            'AppDataDesktop',
            'AppDataDocuments',
            'AppDataFavorites',
            'AppDataProgramData',
            'Application Shortcuts',
            'AppMods',
            'AppsFolder',
            'AppUpdatesFolder',
            'Cache',
            'Camera Roll',
            'CameraRollLibrary',
            'Captures',
            'CDBurning',
            'Common Administrative Tools',
            'Common Desktop',
            'Common Documents',
            'Common Programs',
            'Common Start Menu',
            'Common Start Menu Places',
            'Common Startup',
            'Common Templates',
            'CommonDownloads',
            'CommonMusic',
            'CommonPictures',
            'CommonRingtones',
            'CommonVideo',
            'ConflictFolder',
            'ConnectionsFolder',
            'Contacts',
            'ControlPanelFolder',
            'Cookies',
            'CredentialManager',
            'CryptoKeys',
            'CSCFolder',
            'DesktopFolder',
            'Development Files',
            'Device Metadata Store',
            'DocumentsLibrary',
            'Downloads',
            'DpapiKeys',
            'Favorites',
            'Fonts',
            'GameTasks',
            'History',
            'HomeGroupFolder',
            'ImplicitAppShortcuts',
            'Libraries',
            'Links',
            'LocalAppData',
            'LocalAppDataLow',
            'LocalizedResourcesDir',
            'MusicLibrary',
            'My Documents',
            'My Music',
            'My Pictures',
            'My Video',
            'MyComputerFolder',
            'NetHood',
            'NetworkPlacesFolder',
            'OEM Links',
            'OneDrive',
            'OneDriveCameraRoll',
            'OneDriveDocuments',
            'OneDriveMusic',
            'OneDrivePictures',
            'Original Images',
            'PhotoAlbums',
            'PicturesLibrary',
            'Playlists',
            'PrintersFolder',
            'PrintHood',
            'Profile',
            'ProgramData',
            'ProgramFiles',
            'ProgramFilesCommon',
            'ProgramFilesCommonX86',
            'ProgramFilesX86',
            'Programs',
            'Public',
            'PublicAccountPictures',
            'PublicGameTasks',
            'PublicLibraries',
            'Quick Launch',
            'Recent',
            'Recorded Calls',
            'RecordedTVLibrary',
            'RecycleBinFolder',
            'ResourceDir',
            'RetailDemo',
            'Ringtones',
            'Roamed Tile Images',
            'Roaming Tiles',
            'SampleMusic',
            'SamplePictures',
            'SampleVideos',
            'SavedGames',
            'SavedPictures',
            'SavedPicturesLibrary',
            'Screenshots',
            'Searches',
            'SearchHistoryFolder',
            'SearchHomeFolder',
            'SearchTemplatesFolder',
            'SendTo',
            'Start Menu',
            'Startup',
            'SyncCenterFolder',
            'SyncResultsFolder',
            'SyncSetupFolder',
            'System',
            'SystemCertificates',
            'SystemX86',
            'Templates',
            'User Pinned',
            'UserProfiles',
            'UserProgramFiles',
            'UserProgramFilesCommon',
            'VideosLibrary',
            'Windows'
        )]
        [string]$Name
    )

    try {
        if ($Name -in $folderKeys) {
            $path = getKnownFolderPath $Name
            if ($path) { return $path }
            else { throw [DirectoryNotFoundException]::new("Folder `"$Name`" not found.") }
        } else {
            try {
                $path = $shell.NameSpace("shell:$Name").Self.Path
                if ($path -match '^::') { return "shell:$path" } else { return $path }
            } catch {
                throw [NotSupportedException]::new("Folder `"$Name`" not supported in current Windows version.")
            }
        }
    } catch {
        $PSCmdlet.WriteError($_)
    }
}

function New-SpecialFolder {
    <#
    .SYNOPSIS
    Creates a new special folders for Windows.
    .PARAMETER Name
    The name of a new special folder.
    .OUTPUTS
    System.IO.FileSystemInfo
        A object representing a new folder.
    .EXAMPLE
    PS >Get-SpecialFolderPath SavedPictures
    #>


    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([System.IO.FileSystemInfo])]
    param (
        [Parameter(Mandatory, Position = 0)]
        [ValidateSet(
            '3D Objects',
            'AccountPictures',
            'Administrative Tools',
            'AppData',
            'AppDataDesktop',
            'AppDataDocuments',
            'AppDataFavorites',
            'AppDataProgramData',
            'Application Shortcuts',
            'AppMods',
            'Cache',
            'Camera Roll',
            'CameraRollLibrary',
            'Captures',
            'CDBurning',
            'Common Administrative Tools',
            'Common Desktop',
            'Common Documents',
            'Common Programs',
            'Common Start Menu Places',
            'Common Start Menu',
            'Common Startup',
            'Common Templates',
            'CommonDownloads',
            'CommonMusic',
            'CommonPictures',
            'CommonRingtones',
            'CommonVideo',
            'Contacts',
            'Cookies',
            'CredentialManager',
            'CryptoKeys',
            'DesktopFolder',
            'Development Files',
            'Device Metadata Store',
            'DocumentsLibrary',
            'Downloads',
            'DpapiKeys',
            'Favorites',
            'Fonts',
            'GameTasks',
            'History',
            'ImplicitAppShortcuts',
            'Libraries',
            'Links',
            'LocalAppData',
            'LocalAppDataLow',
            'LocalizedResourcesDir',
            'MusicLibrary',
            'My Documents',
            'My Music',
            'My Pictures',
            'My Video',
            'NetHood',
            'OEM Links',
            'OneDrive',
            'OneDriveCameraRoll',
            'OneDriveDocuments',
            'OneDriveMusic',
            'OneDrivePictures',
            'Original Images',
            'PhotoAlbums',
            'PicturesLibrary',
            'Playlists',
            'PrintHood',
            'Profile',
            'ProgramData',
            'ProgramFiles',
            'ProgramFilesCommon',
            'ProgramFilesCommonX86',
            'ProgramFilesX86',
            'Programs',
            'Public',
            'PublicAccountPictures',
            'PublicGameTasks',
            'PublicLibraries',
            'Quick Launch',
            'Recent',
            'Recorded Calls',
            'RecordedTVLibrary',
            'ResourceDir',
            'RetailDemo',
            'Ringtones',
            'Roamed Tile Images',
            'Roaming Tiles',
            'SampleMusic',
            'SamplePictures',
            'SampleVideos',
            'SavedGames',
            'SavedPictures',
            'SavedPicturesLibrary',
            'Screenshots',
            'Searches',
            'SearchHistoryFolder',
            'SearchTemplatesFolder',
            'SendTo',
            'Start Menu',
            'Startup',
            'System',
            'SystemCertificates',
            'SystemX86',
            'Templates',
            'User Pinned',
            'UserProfiles',
            'UserProgramFiles',
            'UserProgramFilesCommon',
            'VideosLibrary',
            'Windows'
        )]
        [string]$Name
    )

    try {
        if ($Name -notin $folderKeys) { throw [NotSupportedException]::new("Folder `"$Name`" not supported.") }

        if (!$PSCmdlet.ShouldProcess($Name)) { return }

        $KF_FLAG_DEFAULT = [uint32]0x00000000
        $KF_FLAG_CREATE = [uint32]0x00008000

        $folder = [KnownFolder]::new($folderGuids[$Name], $KF_FLAG_DEFAULT)
        if ($folder.Result -eq 'OK') { throw [IOException]::new("Folder `"$Name`" already exists.") }

        $folder = [KnownFolder]::new($folderGuids[$Name], $KF_FLAG_CREATE)
        switch ($folder.Result) {
            'OK' { return Get-Item -LiteralPath $folder.Path }
            'NotFound' {
                throw [NotSupportedException]::new("Folder `"$Name`" not supported in current Windows version.")
            }
            'AccessDenied' { throw [UnauthorizedAccessException]::new("Creating the Folder `"$Name`" is denied.") }
            Default { throw [IOException]::new("Fail to Create the Folder `"$Name`".") }
        }
    } catch {
        $PSCmdlet.WriteError($_)
    }
}

# 1つのリソースは1つのメニューにか設定できないので、必要な項目ごとに使用する
function getShieldImage {
    [OutputType([System.Windows.Controls.Image])]
    param ()

    # System.Drawing.Imageと曖昧になるので完全名で書いている
    $image = [System.Windows.Controls.Image]::new()

    $image.Source = [Imaging]::CreateBitmapSourceFromHIcon(
        [SystemIcons]::Shield.Handle, [Int32Rect]::Empty, $null
    )
    return $image
}

function Show-SpecialFolder {
    <#
    .SYNOPSIS
    Display the special folders for Windows in a dialog.
    .DESCRIPTION
    Display the special folders for Windows in a dialog. Open the folder to double-click on it. Show context menu to right-click on the folder.
    #>


    [CmdletBinding()]
    param ([switch]$IncludeShellCommand)

    # WPFが使えない場合
    if (($PSVersionTable['PSVersion'].Major -eq 6) -or ($Host.Runspace.ApartmentState -ne 'STA')) {
        $PSCmdlet.WriteError(
            [ErrorRecord]::new(
                [NotSupportedException]'Show-SpecialFolder can''t be started because this function needs WPF.',
                $null,
                'OperationStopped',
                $null
            )
        )
        return
    }

    Add-Type -AssemblyName PresentationFramework
    Add-Type -AssemblyName System.Drawing

    # SendKeys用
    # GUIにWPFを使っているのでWinFormsのSendKeysは使ってない
    $wsh = New-Object -ComObject WScript.Shell

    function selectInvokedCommand {
        $item = $dataGrid.SelectedItem
        if (!$item -or $item -isnot [SpecialFolder]) { return }

        $ErrorActionPreference = 'Stop'
        $modifiers = [Keyboard]::Modifiers
        try {
            if ($modifiers -band [ModifierKeys]::Alt) { $item.Properties() }
            elseif ($modifiers -band [ModifierKeys]::Control) { & $startPowershell }
            elseif ($modifiers -band [ModifierKeys]::Shift) { & $startCmd }
            else { & $openFolder }
        } catch { [MessageBox]::Show($_, $_.Exception.GetType().Name, 'OK', 'Warning') > $null }
    }

    function invokeCommandAsAdmin {
        param ([scriptblock]$command)

        # 昇格プロンプトで[いいえ]を選んだときのエラーを無視する
        $ErrorActionPreference = 'SilentlyContinue'
        & $command
    }

    $openFolder = { $dataGrid.SelectedItem.Open('open') }
    $startPowershell = { $dataGrid.SelectedItem.Powershell('open') }
    $startCmd = { $dataGrid.SelectedItem.Cmd('open') }
    $startWsl = { $dataGrid.SelectedItem.Wsl('open') }

    $window = [Window][XamlReader]::Parse((Get-Content -LiteralPath "$PSScriptRoot\window.xaml" -Raw -ErrorAction Stop))

    $open = [MenuItem]$window.FindName('open')
    $openEx = [MenuItem]$window.FindName('openEx')
    $openAsAdmin = [MenuItem]$window.FindName('openAsAdmin')
    $powershell = [MenuItem]$window.FindName('powershell')
    $powershellEx = [MenuItem]$window.FindName('powershellEx')
    $powershellAsAdmin = [MenuItem]$window.FindName('powershellAsAdmin')
    $cmd = [MenuItem]$window.FindName('cmd')
    $cmdEx = [MenuItem]$window.FindName('cmdEx')
    $cmdAsAdmin = [MenuItem]$window.FindName('cmdAsAdmin')
    $wsl = [MenuItem]$window.FindName('wsl')
    $wslEx = [MenuItem]$window.FindName('wslEx')
    $wslAsAdmin = [MenuItem]$window.FindName('wslAsAdmin')
    $properties = [MenuItem]$window.FindName('properties')

    $openAsAdmin.Icon = getShieldImage
    $powershellAsAdmin.Icon = getShieldImage
    $cmdAsAdmin.Icon = getShieldImage
    $wslAsAdmin.Icon = getShieldImage

    $dataGrid = [DataGrid]($window.FindName('dataGrid'))
    $dataGrid.add_PreviewKeyDown(
        {
            # インテリセンスを効かせるために型を明示している
            $ke = [KeyEventArgs]$_

            # Home/End単独で一番上/一番下に移動できるようにする
            if (!([Keyboard]::Modifiers -band [ModifierKeys]::Control)) {
                switch ($ke.Key) {
                    'Home' { $wsh.SendKeys('^{HOME}') }
                    'End' { $wsh.SendKeys('^{END}') }
                }
            }

            # $ke.KeyだとAlt単独もAlt+Enterも'System'になるので[Keyboard]::IsKeyDown('Enter')を見ている
            if (![Keyboard]::IsKeyDown('Enter')) { return }

            $source = [Control]$ke.OriginalSource
            if ($source -is [DataGridCell]) { $dataGrid.SelectedItem = $source.DataContext }

            $ke.Handled = $true
            selectInvokedCommand
        }
    )
    $dataGrid.add_MouseDoubleClick(
        {
            $me = [MouseButtonEventArgs]$_

            if ($me.OriginalSource -is [TextBlock]) { selectInvokedCommand }
        }
    )
    $dataGrid.add_ContextMenuOpening(
        {
            $ce = [ContextMenuEventArgs]$_

            $item = $dataGrid.SelectedItem
            if ($item -isnot [SpecialFolder]) {
                $ce.Handled = $true
                return
            }

            $open.Visibility = 'Visible'

            $openEx.Visibility = 'Collapsed'
            $powershell.Visibility = 'Collapsed'
            $powershellEx.Visibility = 'Collapsed'
            $cmd.Visibility = 'Collapsed'
            $cmdEx.Visibility = 'Collapsed'
            $wsl.Visibility = 'Collapsed'
            $wslEx.Visibility = 'Collapsed'
            $properties.Visibility = 'Collapsed'

            if ([Keyboard]::Modifiers -band [ModifierKeys]::Shift) {
                if ($canFolderBeOpenedAsAdmin) {
                    $open.Visibility = 'Collapsed'
                    $openEx.Visibility = 'Visible'
                }
                if ($item -is [FileFolder]) {
                    $cmdEx.Visibility = 'Visible'
                    $powershellEx.Visibility = 'Visible'
                    if ($isWslEnabled) { $wslEx.Visibility = 'Visible' }
                }
            } else {
                if ($item -is [FileFolder]) {
                    $cmd.Visibility = 'Visible'
                    $powershell.Visibility = 'Visible'
                    if ($isWslEnabled) { $wsl.Visibility = 'Visible' }
                }
            }
            if ($item.HasProperties()) { $properties.Visibility = 'Visible' }
        }
    )
    $open.add_Click($openFolder)
    $window.FindName('openAsInvoker').add_Click($openFolder)
    $openAsAdmin.add_Click({ invokeCommandAsAdmin { $dataGrid.SelectedItem.Open('runas') } })
    $window.FindName('copyAsPath').add_Click({ Set-Clipboard $dataGrid.SelectedItem.Path })
    $powershell.add_Click($startPowershell)
    $window.FindName('powershellAsInvoker').add_Click($startPowershell)
    $powershellAsAdmin.add_Click({ invokeCommandAsAdmin { $dataGrid.SelectedItem.Powershell('runas') } })
    $cmd.add_Click($startCmd)
    $window.FindName('cmdAsInvoker').add_Click($startCmd)
    $cmdAsAdmin.add_Click({ invokeCommandAsAdmin { $dataGrid.SelectedItem.Cmd('runas') } })
    $wsl.add_Click($startWsl)
    $window.FindName('wslAsInvoker').add_Click($startWsl)
    $wslAsAdmin.add_Click({ invokeCommandAsAdmin { $dataGrid.SelectedItem.Wsl('runas') } })
    $properties.add_Click({ $dataGrid.SelectedItem.Properties() })
    $window.add_Loaded({
            $window.Topmost = $true
            $window.Topmost = $false
            $window.Activate()
        })

    $category = ''
    $dataGrid.ItemsSource = Get-SpecialFolder @PSBoundParameters |
        ForEach-Object {
            if ($category -ne $_.Category) {
                $category = $_.Category
                [pscustomobject]@{ Name = "Category: $($_.Category)"; Path = $null }
            }
            $_
        }

    $window.ShowDialog() > $null
}