【PowerShell】Outlookのmsgファイルを変換

# 【PowerShell】<Outlookのmsgファイルを変換>

## ■はじめに
:::note info
- **この記事は？**
OutlookのmsgファイルをテキストとCSVに変換するスクリプトを解説
:::

## ■スクリプトの概要
:::note info
- **何をするスクリプトか？**
　- Outlookからコピーした.msgファイルの入ったディレクトリを指定し、
　　ディレクトリ内のすべての.msgファイルを.txtファイルに変換
　- 変換したすべての.txtファイルからサマリーのcsvファイルを作成
:::
:::note info
- **どんな場面で役に立つか？**
　- 大量のメールから、必要な情報を検索・抽出
:::

## ■動作確認環境
:::note info
- Windows 11 Pro 24H2 (OS ビルド 26100.3476)
- PowerShell 5.1.26100.2161
- Outlook 2502 (ビルド 18526.20168)
:::

## ■スクリプトのソースコード
```powershell:outlook-msg-change.ps1
# Read-Hostのメッセージにカラーを付与するための関数(確認用)
function Set-Conf {
    param(
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [string]$msg,
        [string]$BackgroundColor = "DarkGray",
        [string]$ForegroundColor = "White"
    )

    Write-Host -BackgroundColor $BackgroundColor -ForegroundColor $ForegroundColor -NoNewline $msg
    return Read-Host
}

# Read-Hostのメッセージにカラーを付与するための関数(出力用)
function Set-Out {
    param(
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [string]$msg,
        [string]$BackgroundColor = "DarkBlue",
        [string]$ForegroundColor = "White"
    )

    Write-Host -BackgroundColor $BackgroundColor -ForegroundColor $ForegroundColor -NoNewline $msg
    return Read-Host
}

# Read-Hostのメッセージにカラーを付与するための関数(エラー出力用)
function Set-Err {
    param(
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [string]$msg,
        [string]$BackgroundColor = "DarkRed",
        [string]$ForegroundColor = "White"
    )

    Write-Host -BackgroundColor $BackgroundColor -ForegroundColor $ForegroundColor -NoNewline $msg
    return Read-Host
}

Set-Out "＜処理開始＞指定したディレクトリの .msgファイルをテキストファイルに変換します。Enterを押してください。"

$srcDir = Set-Conf "＜１＞.msgファイルが格納されているディレクトリを入力（省略時はカレントディレクトリ）："
$destDir = Set-Conf "＜２＞変換後のテキストファイルを保存するディレクトリを入力（省略時はカレントディレクトリのmsg_txtフォルダ）："

# ディレクトリ存在確認・設定
if ($srcDir -eq "") {
    $srcDir = Split-Path $myInvocation.MyCommand.Path -Parent
}
if (!(Test-Path $srcDir)) {
    Set-Err "▶ 【変換元ディレクトリが存在しません。処理を終了します。Enterを押してください。】"
    return
} else {
    Set-Out "▶ 【変換元ディレクトリ: $srcDir を確認しました。Enterを押してください。】"
}

if ($destDir -eq "") {
    $destDir = Join-Path (Split-Path $myInvocation.MyCommand.Path -Parent) "msg_txt"
}
if (!(Test-Path $destDir)) {
    New-Item -Path $destDir -ItemType Directory | Out-Null
    Set-Out "▶ 【変換後ディレクトリ: $destDir を作成しました。Enterを押してください。】"
} else {
    Set-Out "▶ 【変換後ディレクトリ: $destDir を確認しました。Enterを押してください。】"
}

# 再帰的に処理するかどうかの確認
$YesNo = Set-Conf "サブフォルダも含めて再帰的に処理しますか？[Y(y)/N(n)]"
if ($YesNo -eq "Y") {
    $RecurseOption = $true
} else {
    $RecurseOption = $false
}

# CSV出力準備
$csvPath = Join-Path $destDir "mail_summary.csv"
"件名,差出人,宛先,CC,受信日時,本文" | Out-File -Encoding UTF8 $csvPath

# 変換処理開始
$outlook = New-Object -ComObject Outlook.Application

Get-ChildItem -Path "$srcDir\*.msg" -Recurse:$RecurseOption | ForEach-Object {
    $msg = $outlook.Session.OpenSharedItem($_.FullName)

    $text = @"
件名: $($msg.Subject)
差出人: $($msg.SenderName)
宛先: $($msg.To)
CC: $($msg.CC)
受信日時: $($msg.ReceivedTime)

本文:
$($msg.Body)
"@

    $outFile = Join-Path $destDir ($_.BaseName + ".txt")
    $text | Out-File -Encoding UTF8 $outFile

    # CSV用データ処理
    $subject = '"' + $msg.Subject.Replace('"','""') + '"'
    $sender = '"' + $msg.SenderName.Replace('"','""') + '"'
    $to = '"' + $msg.To.Replace('"','""') + '"'
    $cc = '"' + $msg.CC.Replace('"','""') + '"'
    $received = '"' + $msg.ReceivedTime + '"'
    $body = '"' + $msg.Body.Replace('"','""').Replace("`r`n", "<改行>") + '"'

    "$subject,$sender,$to,$cc,$received,$body" | Out-File -Encoding UTF8 -Append $csvPath

    Write-Host "変換済: $($_.Name)"

    # 各メールのCOMオブジェクトを解放する（重要）
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($msg) | Out-Null
}

Start-Sleep -Milliseconds 5000  # メール処理後5秒待機

# Outlookオブジェクトの終了処理を安全に行う（Quitしない）
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($outlook) | Out-Null

$outlook = $null
$msg = $null

# ガベージコレクションを明示的に行い、メモリを解放
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Set-Out "すべての変換とCSVファイルへの出力が完了しました。Enterを押して終了してください。"
Read-Host
```