cmdlets/CommonUtils/CommonUtils.ps1

#01.2018©willynilly

#----------------------------------------------------------------------------------------
# Получает согласие пользователя на продолжение
function Get-Permission {
<#
    .Synopsis
        Получить разрешение от пользователя.
    .Description
        Задает вопрос пользователю и ожидает ответа Да/Нет клавишами Enter/Esc.
    .Parameter TextQuestion
        Текст однозначного вопроса (без знака вопроса) на который можно ответить Да/Нет.
        По умолчанию: "Продолжить?"
        Псевдонимы: t, q
    .Example
        if (Get-Permision "Очистить экран") {Clear-Host}
#>

    [CmdletBinding()]
    param(
        [Alias("t", "q", "Question")][string]$TextQuestion="Продолжить" # текст вопроса
    )
    # в ISE есть глобальная переменная psISE, а в VS Code есть psEditor
    # в ISE параметр $host.ui.RawUi.KeyAvailable равен NULL
    $ispsISE = (Test-path variable:psISE)
    $ispsEditor = (Test-path variable:psEditor)
    $isNullKeyAvailable = ($host.ui.RawUi.KeyAvailable -eq $null)
    Write-Verbose "psISE = $ispsISE"
    Write-Verbose "psEditor = $ispsEditor"
    Write-Verbose "isNullKeyAvailable = $isNullKeyAvailable"
    Write-Verbose ('host.ui.RawUi.KeyAvailable = {0}' -f $host.ui.RawUi.KeyAvailable)
    $Result = $true
    Write-Host ("`n{0}? (Enter\Esc)" -f $TextQuestion) -ForegroundColor White
    while($True)
    {
        try {
            $Key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") # !!!НЕ РАБОТАЕТ в ISE и VS CODE!!!
            if ($key.VirtualKeyCode -eq 27) {$Result = $false; Break} # нажали Esc - выходим из скрипта
            if ($key.VirtualKeyCode -eq 13) {Break} # нажали Enter - выходим из цикла и идем удалять
        }
        catch {
            $InputUser = Read-Host ('Да <Enter>\Нет <любой символ>')
            if($InputUser.Length -gt 0) {$Result = $false}
            Break
        }
    }
    if (-not $Result){Write-Host "Отказ" -ForegroundColor Red}
    $Result
        
}# end Get-Permission

#----------------------------------------------------------------------------------------
# проверяет параметр скрипта WaitPressAnyKey и ждет нажатия любой клавиши если надо
function Wait-PressAnyKey {
<#
    .Synopsis
        Ждет нажатия любой клавиши.
    .Description
        Выводит сообщение "Для выхода нажмите любую клавишу..." и ждет.
    .Example
        if ($Wait) {Wait-PressAnyKey}
#>

    [CmdletBinding()]
    param()
    # в ISE есть глобальная переменная psISE, а в VS Code есть psEditor
    # в ISE параметр $host.ui.RawUi.KeyAvailable равен NULL
    $ispsISE = (Test-path variable:psISE)
    $ispsEditor = (Test-path variable:psEditor)
    $isNullKeyAvailable = ($host.ui.RawUi.KeyAvailable -eq $null)
    Write-Verbose "psISE = $ispsISE"
    Write-Verbose "psEditor = $ispsEditor"
    Write-Verbose "isNullKeyAvailable = $isNullKeyAvailable"
    Write-Verbose ('host.ui.RawUi.KeyAvailable = {0}' -f $host.ui.RawUi.KeyAvailable)
    if (-not($ispsISE -or $ispsEditor -or $isNullKeyAvailable))
    {
        #Остановка скрипта БЕЗ возможности отмены !!!НЕ РАБОТАЕТ В ISE и VS Code!!!
        Write-host "`nДля выхода нажмите любую клавишу..." -ForegroundColor White
        $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,AllowCtrlC") | Out-Null
    }
}# end Wait-PressAnyKey

#----------------------------------------------------------------------------------------
# получает кодировку файла
function Get-Encoding {
<#
    .Synopsis
        Получает кодировку текстового файла.
    .Description
        Определяет кодировку переданного текстового файла.
    .Parameter filePath
        Полный путь к текстовому файлу.
    .Outputs
        [System.Text.Encoding]
    .Example
        Get-Encoding C:\Names.txt
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True)][string]$filePath
    )
    Try {
        $sr = New-Object System.IO.StreamReader($filePath, $true)
        [char[]] $buffer = new-object char[] 3
        $sr.Read($buffer, 0, 3) | Out-Null # задавим вывод, иначе попадет в результат функции
        $encoding = $sr.CurrentEncoding
        $sr.Close()
    } catch {
        #ничего делать не надо
    }
    #вернем полученную кодировку
    $encoding

}# end Get-Encoding

#----------------------------------------------------------------------------------------
#Открывает диалог выбора файла
function Select-FileDialog {
<#
    .Synopsis
        Выбор файла в диалоговом окне.
    .Description
        Открывает стандартное диалоговое окно выбора файла и возвращает полный путь выбранного файла.
    .Parameter Title
        Заголовок окна выбора файла.
    .Parameter Directory
        Начальный каталог выбора файла.
    .Parameter Filter
        Фильтр расширений файлов. По умолчанию все файлы.
    .Outputs
        [System.String]
    .Example
        $FileConfig = Select-FileDialog "Укажите файл настроек" "C:\Settings" "Файлы настроек (*.cfg)|*.cfg|Все файлы (*.*)|*.*"
#>

    param(
        [string]$Title,
        [string]$Directory,
        [string]$Filter="Все файлы (*.*)|*.*"
    )

    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
    $objForm = New-Object System.Windows.Forms.OpenFileDialog
    $objForm.InitialDirectory = $Directory
    $objForm.Filter = $Filter
    $objForm.Title = $Title
    $objForm.ShowDialog() | Out-Null
    $objForm.FileName
}
#----------------------------------------------------------------------------------------
# Показывает диалоговое окно вопроса или предупреждения
function Show-Dialog {
    <#
        .Synopsis
            Показывает диалоговое окно вопроса или предупреждения.
        .Description
            Показывает диалоговое окно вопроса или предупреждения.
        .Parameter Text
            Текст сообщения.
        .Parameter SecondsToWait
            Количество секунд, по истечении которого окно будет автоматически закрыто.
        .Parameter Title
            Текст заголовка окна сообщения.
        .Parameter Type
            Комбинация флагов, определяет тип кнопок и значка. Возможные значения флагов:
            0 — кнопка ОК.
            1 — кнопки ОК и Отмена.
            2 — кнопки Стоп, Повтор, Пропустить.
            3 — кнопки Да, Нет, Отмена.
            4 — кнопки Да и Нет.
            5 — кнопки Повтор и Отмена.
            16 — значок Stop.
            32 — значок Question.
            48 — значок Exclamation.
            64 — значок Information.
        .Outputs
            Возвращает целое значение, с помощью которого можно узнать, какая кнопка была нажата. Возможные значения:
            -1 — таймаут.
            1 — кнопка ОК.
            2 — кнопка Отмена.
            3 — кнопка Стоп.
            4 — кнопка Повтор.
            5 — кнопка Пропустить.
            6 — кнопка Да.
            7 — кнопка Нет.
        .Example
             
            Example #1
            Простая форма предупреждения с одной кнопкой Ок, без и иконок и автозакрытия.
            Show-Dailog "Отправка завершена"
 
            Example #2
            Форма вопроса с двумя кнопками Да/Нет, иконкой вопроса и таймаутом на размышление 30 сек.
            Show-Dailog "Завершить работу?" 30 "Вопрос" (4+32)
    #>

    [CmdletBinding()]

    param(
        [Parameter(Mandatory=$True)][string]$Text,
        [int]$SecondsToWait,
        [string]$Title="Предупреждение",
        [int]$Type
    )
    $wshell = New-Object -ComObject Wscript.Shell
    $Output = $wshell.Popup($Text,$SecondsToWait,$Title,$Type)
    return $Output
}# end Show-Dialog
#----------------------------------------------------------------------------------------
# Конвертирует PSObject полученный после Convert-FromJSON в обычный Hastable
function Convert-PSObjectToHashtable
{
    <#
        .Synopsis
            Конвертирует PSObject в Hashtable.
        .Description
            Рекурсивно конвертирует PSObject полученный после Convert-FromJSON в полноценный Hashtable.
        .Parameter InputObject
            Входной объект (может быть передан по конвейеру).
        .Outputs
            [System.Hastable]
        .Example
            $Config = Get-Content C:\Config.json | ConvertFrom-Json | Convert-PSObjectToHashtable
    #>

    param (
        [Parameter(ValueFromPipeline)]
        $InputObject
    )

    process
    {
        if ($null -eq $InputObject) { return $null }

        if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
            $collection = @(
                foreach ($object in $InputObject) { Convert-PSObjectToHashtable $object }
            )
            Write-Output -NoEnumerate $collection
        }
        elseif ($InputObject -is [psobject]) {
            $hash = @{}
            foreach ($property in $InputObject.PSObject.Properties) {
                $hash[$property.Name] = Convert-PSObjectToHashtable $property.Value
            }
            $hash
        }
        else {
            $InputObject
        }
    }
}# end Convert-PSObjectToHashtable

#----------------------------------------------------------------------------------------
# Выполняет перекодировку строки
function ConvertTo-Encoding ([string]$From, [string]$To)
{
    Begin
    {
        $encFrom = [System.Text.Encoding]::GetEncoding($from)
        $encTo = [System.Text.Encoding]::GetEncoding($to)
    }
    Process
    {
        $bytes = $encTo.GetBytes($_)
        $bytes = [System.Text.Encoding]::Convert($encFrom, $encTo, $bytes)
        $encTo.GetString($bytes)
    }
}# end ConvertTo-Encoding

#----------------------------------------------------------------------------------------
# Выводит результат теста
function Write-Test($Name, $Result, $Count=25, $Sym='.'){

    $Test = $Name.PadRight($Count, $Sym)
    if ($Result -is [bool]){
        if ($Result) {
            Write-Host ($Test + "Ok")
        } else {
            Write-Host ($Test + "No") -ForegroundColor DarkYellow
        }
    } else {
        Write-Host ($Test + $Result)
    }
}# end Write-Test