LoginSignup
2
1

More than 1 year has passed since last update.

Excel, PowerPoint, Word ファイルをオープンしてはクローズするだけの PowerShell スクリプト

Last updated at Posted at 2021-07-01

動機

大量の Excel, PowerPoint, Word ファイルに破損したものがあるかどうかをしらべたい。

概略

指定したフォルダーの配下の Excel, PowerPoint, Word ファイルをひたすらオープンしてはクローズするだけの PowerShell スクリプトです。オープンの成否を記録したカスタム オブジェクトを出力します。

コード

Test-MSOfficeFile.ps1
param(
    [Parameter(Mandatory=$true)]
    [String[]]$Path
)

Set-StrictMode -Version Latest

$excel = New-Object -ComObject Excel.Application
$powerpnt = New-Object -ComObject PowerPoint.Application
$word = New-Object -ComObject Word.Application

$excel.DisplayAlerts = $false
$word.DisplayAlerts = [Microsoft.Office.Interop.Word.WdAlertLevel]::wdAlertsNone

try {
    Get-ChildItem -Path (Join-Path -Path $Path -ChildPath '*') -Include *.xls*, *.xlt*, *.pot*, *.pps*, *.ppt*, *.doc*, *.dot* -Recurse |
        Where-Object -Property PSIsContainer -EQ -Value $false |
        Select-Object -ExpandProperty FullName |
        ForEach-Object -Process {
            $fileName = $_

            $outputObject = [PSCustomObject]@{
                FileName = $fileName
                Open = ''
                Close = ''
                Message = ''
            }

            # https://social.msdn.microsoft.com/Forums/ja-JP/5deec897-a897-404b-a610-f7d894fde1b3/office?forum=officesupportteamja
            $books = $book = $presns = $presn = $docs = $doc = $null

            try {
                switch -Regex ($fileName) {
                    '\.xl[ast][bmx]?$' {
                        $books = $excel.Workbooks
                        # https://docs.microsoft.com/en-us/office/vba/api/excel.workbooks.open
                        $book = $books.Open($fileName, $false, $true, [System.Reflection.Missing]::Value, '')
                        $outputObject.Open = 'Success'
                    }
                    '\.p(ot|p[st])[mx]?$' {
                        $presns = $powerpnt.Presentations
                        # https://docs.microsoft.com/en-us/office/vba/api/powerpoint.presentations.open2007
                        $presn = $presns.Open2007($fileName + '::*', [Microsoft.Office.Core.MsoTriState]::msoTrue, [System.Reflection.Missing]::Value, [Microsoft.Office.Core.MsoTriState]::msoFalse, [Microsoft.Office.Core.MsoTriState]::msoFalse)
                        $outputObject.Open = 'Success'
                    }
                    '\.do[ct][mx]?$' {
                        $docs = $word.Documents
                        # https://docs.microsoft.com/en-us/office/vba/api/word.documents.opennorepairdialog
                        $doc = $docs.OpenNoRepairDialog($fileName, $false, $true, $false, '*')
                        $outputObject.Open = 'Success'
                    }
                }
            } catch [System.Runtime.InteropServices.COMException] {
                $outputObject.Open = 'Failed'
                $outputObject.Message = $_.Exception.Message
            }

            try {
                if ($book) {
                    # https://docs.microsoft.com/en-us/office/vba/api/excel.workbook.close
                    $book.Close($false)
                    $outputObject.Close = 'Success'
                    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) | Out-Null
                    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($books) | Out-Null
                }
                if ($presn) {
                    # https://docs.microsoft.com/en-us/office/vba/api/powerpoint.presentation.close
                    $presn.Saved = $true
                    $presn.Close()
                    $outputObject.Close = 'Success'
                    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($presn) | Out-Null
                    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($presns) | Out-Null
                }
                if ($doc) {
                    # https://docs.microsoft.com/en-us/office/vba/api/word.document.close(method)
                    $doc.Close([Microsoft.Office.Interop.Word.WdSaveOptions]::wdDoNotSaveChanges)
                    $outputObject.Close = 'Success'
                    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null
                    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($docs) | Out-Null
                }
            } catch [System.Runtime.InteropServices.COMException] {
                $outputObject.Close = 'Failed'
                $outputObject.Message = $_.Exception.Message
            }

            $books = $book = $presns = $presn = $docs = $doc = $null
            Remove-Variable -Name books, book, presns, presn, docs, doc -ErrorAction SilentlyContinue
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
            [System.GC]::Collect()

            Write-Output -InputObject $outputObject
        } | Out-GridView -PassThru
} finally {
    $books = $book = $presns = $presn = $docs = $doc = $null
    Remove-Variable -Name books, book, presns, presn, docs, doc -ErrorAction SilentlyContinue
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
    [System.GC]::Collect()
    $excel.Quit()
    $powerpnt.Quit()
    $word.Quit()
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($powerpnt) | Out-Null
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null
    $excel, $powerpnt, $word = $null
    Remove-Variable -Name excel, powerpnt, word -ErrorAction SilentlyContinue
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
    [System.GC]::Collect()
}

(2021/10/7 よくわかっていませんが、「.NETを使った別プロセスのOfficeの自動化が面倒なはずがない―そう考えていた時期が俺にもありました。」を参考にさせていただき、 COM オブジェクトを解放するコードを追加しました。)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1