よく使う処理

#### 文字列を渡すと、ダブルクオートで囲ったものを返してくれる処理
```powershell
function Add-DoubleQuotes{
    param(
        [Parameter(Mandatory=$true)][string]$Str
    )
    return '"{0}"' -f $Str
}
```
#### ディレクトリパスを渡すと、ディレクトリとして利用可能かの判定結果を返してくれる処理（パスが成立していなかった場合、成立するようにディレクトリを新規作成する）
```powershell
function Get-DirPathStatus{
    param(
        [Parameter(Mandatory=$true)][string]$DirPath
    )
    #ExitCodeDefinition
    $EXIT_OK = 0
    $EXIT_FILE_EXIST = 1
    $EXIT_UNKNOWN_ERROR = 2

    if(-not (Test-Path $DirPath)){
        try{
            [System.IO.Directory]::CreateDirectory($DirPath) | Out-Null
        }catch{
            Write-Warning "ディレクトリ【$($DirPath)】の作成に失敗しました。：$($_.ToString())"
            return [int]$EXIT_UNKNOWN_ERROR
        }
    }elseif(-not (Test-Path -Path $DirPath -PathType Container)){
        return [int]$EXIT_FILE_EXIST
    }
    return [int]$EXIT_OK
}
```
#### バルーンチップ通知を表示する処理
```powershell
function Show-Notify{
    param(
        [Parameter(Mandatory=$true)][string]$Msg,
        
        [ValidateSet("INFO", "WARNING", "ERROR", "NONE")]
        [string]$IconType = "INFO",
        
        [string]$Title = "通知"
    )

    #IconTypeをPascalCaseに変換
    $IconType = $IconType.Substring(0,1).ToUpper() + $IconType.Substring(1).ToLower()
    
    if(-not ("System.Windows.Forms.Form" -as [Type])){
        Add-Type -AssemblyName System.Windows.Forms
    }
    if(-not ("System.Drawing.Icon" -as [Type])){
        Add-Type -AssemblyName System.Drawing
    }
    
    $notify = New-Object System.Windows.Forms.NotifyIcon
    switch($IconType.ToUpper()){
        "INFO"   { $notify.Icon = [System.Drawing.SystemIcons]::Information }
        "WARNING"{ $notify.Icon = [System.Drawing.SystemIcons]::Warning }
        "ERROR"  { $notify.Icon = [System.Drawing.SystemIcons]::Error }
        "NONE"   { $notify.Icon = [System.Drawing.SystemIcons]::Application }
    }
    $notify.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]$IconType
    $notify.BalloonTipText = $Msg
    $notify.BalloonTipTitle = $Title
    
    $notify.Visible = $true
    $notify.ShowBalloonTip(3000)
    Start-Sleep -Milliseconds 3500
    $notify.Dispose()
    $notify = $null
}
```
#### メッセージにタイムスタンプ・エラーレベルを付与し、ログとして出力する処理（出力先の選択可能）
```powershell
function Write-Log{
    param(
        [Parameter(Mandatory=$true)]
        [string]$Msg,
        
        [ValidateSet=("INFO", "WARNING", "ERROR", "DEBUG")]
        [string]$MsgLv = "INFO",
        
        [string]$LogFilePath = $null,
        
        [switch]$ToConsole,
        
        [switch]$ToLogFile
    )

    $MsgLv = $MsgLv.ToUpper()
    $console = $ToConsole
    $log = ($ToLogFile -and (-not [string]::IsNullOrEmpty($logFilePath)))
    
    if($console -or $log){
        if($Msg -match '^\s*$'){
            $line = ""
        }else{
            $timeStamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss.FFF")
            $line = "[$($timeStamp)][$($MsgLv)] $($Msg)"
        }
    }
    
    if($console){
        switch($MsgLv){
            "INFO"    { Write-Host $line }
            "WARNING" { Write-Warning $line }
            "ERROR"   { Write-Error $line }
            "DEBUG"   { Write-Host $line -ForegroundColor Gray}
        }
    }
    
    if($log){
        try{
            Add-Content -Path $LogFilePath -Value $line -ErrorAction Stop
        }catch{
            Write-Warning "[$($timeStamp)][ERROR]ログファイルへの書き込みに失敗しました。：$($_.Exception.ToString())"
        }
    }
}
```
#### YN問答処理
```powershell
function Get-YesNoAsBool{
    param(
        [Parameter(Mandatory=$true)][string]$Question
    )
    while($true){
        $ynAnswer = Read-Host "$($Question)(Y/N)"
        switch($ynAnswer.ToUpper()){
            "Y"    { return $true  }
            "N"    { return $false }
            default{ Write-Host "YかNで回答してください。" }
        }
    }
}
```
#### ショートカット作成処理（ディレクトリパスを渡すとディレクトリとして利用可能かの判定結果を返してくれる処理と、YN問答処理を使用）
```powershell
function Make-Shortcut{
    param(
        [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$LnkFileDirPath,
        [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$LnkFileName,
        [Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][string]$LnkTargetPath
    )

    #ExitCodeDefinition
    $EXIT_OK = 0
    $EXIT_USER_CANCEL = 1
    $EXIT_DIR_CREATE_FAIL = 2
    $EXIT_CREATE_FAIL = 3

    #ショートカットを配置するディレクトリが存在していない場合は作成
    if((Get-DirPathStatus -DirPath $LnkFileDirPath) -ne 0){
        Write-Warning "ショートカットの作成に失敗しました。"
        return $EXIT_DIR_CREATE_FAIL
    }

    #ファイル名に拡張子がない場合は追加
    $extension = ".lnk"
    if(-not ($LnkFileName.ToLower().EndsWith($extension))){ $LnkFileName += $extension }
    $LnkFilePath = Join-Path -Path $LnkFileDirPath -ChildPath $LnkFileName
    
    #ショートカットを上書きしようとしている場合
    if((Test-Path $LnkFilePath) -and (-not (Get-YesNoAsBool "作成しようとしているショートカットは既存です。上書きしますか？【$($LnkFilePath)】"))){ return $EXIT_USER_CANCEL }

    #作成
    $wsShell = $null
    $shortcut = $null
    $isSuccess = $true
    try{
        $wsShell = New-Object -ComObject WScript.Shell
        $shortcut = $wsShell.CreateShortcut($LnkFilePath)
        $shortcut.TargetPath = $LnkTargetPath
        $shortcut.Save()
    }catch{
        $isSuccess = $false
        Write-Warning "ショートカット作成に失敗しました。【$($_.ToString())】"
    }finally{
        foreach($obj in @($shortcut, $wsShell)){
            if($obj -ne $null){ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($obj) | Out-Null }
        }
        if($isSuccess){ return $EXIT_OK }
        return $EXIT_CREATE_FAIL
    }
}
```
#### 年月の入力と確認をする処理（YN問答処理を使用）
```powershell
function Get-ConfirmedYearMonth{
    param(
        [int]InitialMonthOffset = 0
    )
    $selectedDate = (Get-Date).AddMonths($InitialMonthOffset)
    $inputFormat = "yyyyMM"

    if(-not (Get-YesNoAsBool "年月は$($selectedDate.ToString("yyyy/MM"))でよろしいですか？")){
        $culture = [System.Globalization.CultureInfo]::InvariantCulture
        while($true){
            $userInput = Read-Host "希望する年月を、$($inputFormat)形式で入力してください。"
            try{
                $parsedDate = [DateTime]::ParseExact($userInput, $inputFormat, $culture)
            }catch{
                Write-Host "入力形式が異なるか、無効な日付です。"
                continue
            }
            $selectedDate = $parsedDate
            break
        }
    }

    return $selectedDate
}
```
#### Excelワークシートが存在するか確認
```powershell
function isUseableWs{
    param(
        [Parameter(Mandatory=$true)][Array]$targets
    )

    <#
        実行例
        if(isUseableWs -targets @(
            @{ FilePath = "任意のパス"; SheetNames = "シート名" }
            @{ FilePath = "任意のパス"; SheetNames = @("シート名", "シート名") }
        )
    #>

    [bool]$returnValue = $true
    $excel = $null
    $wb = $null

    #エクセルアプリケーションを準備
    $isExcelReady = $true
    try{
        $excel = New-Object -ComObject Excel.Application
        $excel.Visible = $true
    }catch{
        Write-Warning "エクセルアプリケーションへのアクセスに失敗しました。"
        $isExcelReady = $false
    }

    if($isExcelReady){
        foreach($target in $targets){

            try{
                #確認するシート名が指定されているか
                if(-not $target.SheetNames){
                    throw "確認するシート名が指定されていません。"
                }

                #確認対象のブックが開けるか
                $fileExtension = [System.IO.Path]::GetExtension($target.FilePath).ToLower()
                if(($fileExtension -ne ".xls") -and ($fileExtension -ne ".xlsx") -and ($fileExtension -ne ".xlsm")){
                    throw "エクセルファイル（.xls .xlsx .xlsm）以外は指定できません。【$($target.FilePath)】"
                }
                if(-not (Test-Path $target.FilePath)){
                    throw "ファイルパスが存在しません。【$($target.FilePath)】"
                }
                try{
                    $wb = $excel.Workbooks.Open($target.FilePath)
                }catch{
                    throw "ワークブックの実行に失敗しました。ファイルがロック中、または破損している可能性があります。【詳細：$($_)】"
                }

                #シートの存在を確認
                $existingSheetNames = @()
                foreach($ws in $wb.Sheets){ $existingSheetNames += $ws.Name }
                $missingSheetNames = @(@($target.SheetNames) | Where-Object{ $existingSheetNames -notContains $_ })
                if($missingSheetNames.Count -gt 0){
                    throw "存在しないシートがあります。【$($target.FilePath)】【$($missingSheetNames -Join ', ')】"
                }
            }catch{
                Write-Warning $_.Exception.Message
                $returnValue = $false
            }finally{
                try{
                    if($wb -ne $null){ $wb.Close($false) }
                }catch{
                    Write-Warning "ワークブックのクローズに失敗しました。"
                }
                $wb = $null
            }
            
        }
    }

    #後処理
    if($excel -ne $null){ $excel.Quit() }
    foreach($obj in @($wb, $excel)){
        if($obj -ne $null){ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($obj) | Out-Null }
    }
    [GC]::Collect()
    [GC]::WaitForPendingFinalizers()

    #戻り値を返す
    return $returnValue
}
```