2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【PowerShell】クラス内で.NETアセンブリを利用する際の注意点

Last updated at Posted at 2021-02-09

前置き

PowerShell を使っていたら「型[~]が見つかりません。」というエラーが出たので、どう対応したかを纏めさせていただきました。
今回はMicrosoft.Office.Interop.Excel.XlDirectionというアセンブリを題材で話を進めさせていただきます。
サンプルプログラムはExcelが入っていないと動作しませんが、動作させなくても分かるように頑張って記載していきたいと思います。
Microsoft.Office.Interop.Excel.XlDirectionはExcelのCtrl + 十字キーを押したときの動作をしてくれます

XlDirectionは、リストの最終行を取得する際によく使ったりしますが、今回はエラーの解消方法について明記したいため、サンプルプログラムではExcelの最終行を1048576をコンソール出力するだけとなっています。

環境

Windows PowerShell 5.1
Excel 2016

確認したエディタ

Windows PowerShell ISE
Visual Studio Code

クラス内で.NETアセンブリを利用する場合

このパターンで今回のエラーを確認しました。
[Microsoft.Office.Interop.Excel.XlDirection]に赤波線が付きます。

HogeClass.ps1
class HogeClass {
    [void]hogeClassMethod() {
        $excel = New-Object -ComObject Excel.Application
        $book = $excel.Workbooks.Add()
        # 「型[Microsoft.Office.Interop.Excel.XlDirection]が見つかりません。」と赤波線でエラーが発生
        Write-Host $book.sheets(1).Range("A1").End([Microsoft.Office.Interop.Excel.XlDirection]::xlDown).row()
        $excel.Quit()
        $excel = $null
        [GC]::Collect()
    }
}
$hogeClass = New-Object HogeClass
$hogeClass.hogeClassMethod()

メソッドで.NETアセンブリを利用する場合

このパターンはエラーが出ませんでした。
メソッドに定義している場合は、実行前にコードのチェックが行われていないと推測しています。

HogeMethod.ps1
function hogeMethod {
    $excel = New-Object -ComObject Excel.Application
    $book = $excel.Workbooks.Add()
    Write-Host $book.sheets(1).Range("A1").End([Microsoft.Office.Interop.Excel.XlDirection]::xlDown).row()
    $excel.Quit()
    $excel = $null
    [GC]::Collect()
}
hogeMethod

コードのチェックでなぜ引っかかるのか

下記のコマンドで読み込まれているアセンブリを確認したところ、Powershellを起動した時点ではMicrosoft.Office.Interop.Excelが存在しないため、コードのチェックでエラーとして検知されいると推測しております。

 [System.AppDomain]::CurrentDomain.GetAssemblies() | ForEach-Object { $_.GetName().Name }

解消方法1

個人的にこの方法は非推奨です。
別ファイルで事前にアセンブリを読み込む方法です。

Main.ps1
Set-Location (Split-Path -Parent $MyInvocation.MyCommand.Path)

# アセンブリを読み込む
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Excel")
# Add-Type -AssemblyName Microsoft.Office.Interop.Excel ← でも同じ動作します

.".\Hoge.ps1"
$hogeClass = New-Object HogeClass
$hogeClass.hogeClassMethod()
HogeClass.ps1
class HogeClass {
    [void]hogeClassMethod() {
        $excel = New-Object -ComObject Excel.Application
        $book = $excel.Workbooks.Add()
        # エディタ上では「型[Microsoft.Office.Interop.Excel.XlDirection]が見つかりません。」の赤波線が残ったまま
        Write-Host $book.sheets(1).Range("A1").End([Microsoft.Office.Interop.Excel.XlDirection]::xlDown).row()
        $excel.Quit()
        $excel = $null
        [GC]::Collect()
    }
}
非推奨理由

同じファイル内に記載するとコードのチェックのエラーが先に引っかかってエラーは解消されません。
そのため、使用例ではMain.ps1にアセンブリを読み込むコードを記載いています。

あと、赤波線は一度実行して、Hoge.ps1ファイルを開きなおさないと消えませんでした。
なので、テキストエディタ自体を閉じて、また開くと赤波線は復活します。

解消方法2

個人的にこの方法を推奨してます。
同一のファイル内でアセンブリを読み込みを行い、文字列で指定した値を-as [type]で.NET型に変換しています。

ただ今回のサンプルでは、New-Object -ComObject Excel.Application
・Microsoft.Office.Interop.Excel
・office
・Microsoft.PowerShell.Commands.Utility.resources
の3つのアセンブリが読み込まれるため、別でアセンブリを読み込むコードは不要となってます。

Excel2013の環境で試したところNew-Object -ComObject Excel.Application利用時に新しくアセンブリが追加されなかったため、読み込むコードは記載する方が無難です。

Hoge.ps1
# アセンブリを読み込む
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Excel")
# Add-Type -AssemblyName Microsoft.Office.Interop.Excel ← でも同じ動作します

class HogeClass {
    [void]hogeClassMethod() {

        $excel = New-Object -ComObject Excel.Application
        $book = $excel.Workbooks.Add()

        # 追記箇所
        $xlDirection = "Microsoft.Office.Interop.Excel.XlDirection" -as [type]

        # [Microsoft.Office.Interop.Excel.XlDirection] ⇒ 上で定義した「$xlDirection」に変更
        Write-Host $book.sheets(1).Range("A1").End($xlDirection::xlDown).row()
        $excel.Quit()
        $excel = $null
        [GC]::Collect()
    }
}
$hogeClass = New-Object HogeClass
$hogeClass.hogeClassMethod()
推奨理由

・1つのファイルで完結する点
・赤波線がつかない点
・コードを変数に定義することで短く記載できる点
でこちらの方が使いやすいと感じました。

いまいちなところ

$xlDirection = "Microsoft.Office.Interop.Excel.XlDirection" -as [type]で誤字脱字があった場合、実行するまで気づけない。

まだもやもやしているところ

本文中では「コードのチェック」と記載させていただいておりますが、「クラス定義にしたらなぜチェックができるのか?」というところが疑問です。
静的解析機能がクラス内では適用されるとかの表現が正しいのでしょうか?

どなたかご存知でしたらコメント欄で教えてください。

終わりに

今回、解消方法1の方は調べたらすぐに見ったのですが、「微妙やな~」って思いながら粘って探したら解消方法2を偶然見つけれたのでラッキーでした。
ただ、一番いいのはアセンブリの読み込みをするコードの記述があったら、それをくみ取ってコードのチェックをしてほしいですね。試してないですが、PowerShell Coreだとできるんですかね。

ということで、ここまで読んでくださいまして、ありがとうございました。
この記事を見ていただいた方に少しでもお役に立てたなら嬉しいです。

参考元URL

PowerShell でロード済みのアセンブリを確認する
-asのヒントになった記事

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?