PowerShellからVBAマクロを実行する

# はじめに
前回の[バッチ処理](https://qiita.com/k12da/items/5b150026a6c7fc4e96bd)の投稿の続きです。
初めてPowerShellのスクリプトを作成した時のことをまとめます。

# PowerShellとは
>[PowerShell は、コマンドライン シェル、スクリプト言語、および構成管理フレームワークで構成されるクロスプラットフォームのタスク自動化ソリューションです。 PowerShell は Windows、Linux、および macOS 上で実行されます。](https://learn.microsoft.com/ja-jp/powershell/scripting/overview?view=powershell-7.4)

## 特徴
- [`.NET Framework`](https://learn.microsoft.com/ja-jp/dotnet/framework/get-started/overview)をベースに開発されており、オブジェクト指向の概念を取り入れています
- コマンドレットと呼ばれる、より直感的で強力なコマンドを使用します
- パイプライン処理により、複数のコマンドを組み合わせて複雑な処理を実行できます

### コマンドレット
PowerShellで使用される軽量で強力なコマンドです。

**動詞-名詞形式**: 「`Get-Item`」「`Set-Location`」のように、動詞と名詞を組み合わせた直感的な名前が付けられています。

#### 役割
- システム管理: ファイルやディレクトリの操作、サービスの管理、レジストリの操作など、様々なシステム管理タスクを実行できます
- データの操作: CSVファイルやXMLファイルの解析、テキストデータの抽出など、データの操作を自動化できます
- スクリプト作成: PowerShellスクリプトを作成し、繰り返し実行する処理を自動化できます

#### 例
- `Get-Command`: 利用可能なすべてのコマンドレットの一覧を取得します
- `Get-ChildItem`: 指定されたディレクトリ内のファイルやサブディレクトリの一覧を取得します
- `Set-Location`: カレントディレクトリを変更します
- `Remove-Item`: ファイルやディレクトリを削除します


## コマンドプロンプトとの違い
どちらもWindowsで利用できるシェル（コマンド入力画面）ですが、**PowerShellは、コマンドプロンプトをより高度に、そして柔軟に使えるように進化したツール**です。


|特徴| コマンドプロンプト | PowerShell |
|----| ---- | ---- |
|歴史| 古くからある | 比較的新しい |
|ベース| DOSコマンド | .NET フレームワーク |
|コマンド| テキストベース | コマンドレット (動詞-名詞) |
|機能| 基本的な操作 | 高度な操作、スクリプト作成 |
|オブジェクト指向| × | ○ |
|パイプライン処理| シンプル | 強力 |

### 使い分け
- **簡単な作業**: コマンドプロンプト
- **高度な作業、自動化**: PowerShell


# 実現したこと
1. バッチ処理でWindowsエクスプローラーからクラウド上のファイル名一覧をコピー ◀︎ [**前回の投稿内容**](https://qiita.com/k12da/items/5b150026a6c7fc4e96bd)
2. マクロ付きExcelファイル（.xlsm）のパスをPowerShellに引数として渡す ◀︎ [**前回の投稿内容**](https://qiita.com/k12da/items/5b150026a6c7fc4e96bd)
3. PowerShellでマクロを実行する ◀︎ **今回の投稿内容**
4. 取得したファイル名一覧をマクロでExcelのシートにペーストする ◀︎ [別記事の投稿内容](https://qiita.com/drafts/c240cfb3de1666d724c7)

コードをすぐ見たい方は[こちら](#作成したコード)

# PowerShellスクリプトの作成手順
1. テキストエディタで新しいファイルを作成する（メモ帳、Visual Studio Codeなど）
2. PowerShellのコマンドレット（スクリプト）を記述する
3. 拡張子を`.ps1`に変更して保存する
4. スクリプトの実行（今回は[バッチファイル](https://qiita.com/k12da/items/5b150026a6c7fc4e96bd#%E4%BD%9C%E6%88%90%E3%81%97%E3%81%9F%E3%82%B3%E3%83%BC%E3%83%89)から実行）

:::note info
**PowerShell ISE**というPowerShellに標準で付属している統合開発環境を使用することもできます。デバッグ機能やコマンド補完機能が用意されています。

![スクリーンショット 2024-10-12 201605.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677000/d3d34fa8-f056-27c1-ebe0-f17fd5bb99b6.png)
:::

## 実行ポリシーの設定
PowerShellスクリプトを実行する場合、セキュリティ上の理由から実行ポリシーが制限されていることがあります。
実行ポリシーの設定を変更して、ローカルで作成したスクリプトを実行できるようにします。

（例）
```powershell
# 実行ポリシーの設定確認
PS C:¥Users¥username¥Downloads> Get-ExecutionPolicy
Restricted

# 実行ポリシーの設定変更
PS C:¥Users¥username¥Downloads> Set-ExecutionPolicy RemoteSigned
```
:::note warn
実行ポリシーを変更する際は、セキュリティリスクを考慮の上で実施してください
:::

|実行ポリシー名|説明|
|----|----|
|Restricted	|すべてのスクリプトの実行が禁止されます。最も制限の強い設定です。|
|AllSigned	|デジタル署名されたスクリプトのみ実行できます。|
|RemoteSigned	|ローカルで作成されたスクリプトと、デジタル署名されたリモートスクリプトのみ実行できます。一般的に最もよく使用される設定です。|
|Unrestricted	|すべてのスクリプトの実行が許可されます。セキュリティリスクが高いため、注意が必要です。|
|Bypass	|実行ポリシーを完全に無視します。|
|Default	|既定の実行ポリシーを設定します。|
|Undefined	|実行ポリシーが未定義の状態です。|

>参考： [PowerShell 実行ポリシー](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4#powershell-execution-policies)

# 作成したコード

```call₋macro.ps1
Param([String]$filePath) #バッチファイルから受け取る引数（ファイルパス）

$target = "Module1.PasteClipboard" #実行するマクロを指定
$excel = $null

if(-not $filePath){
  $errormsg = "マクロブックのファイルパスが見つかりません"
  Write-Output $errormsg
  $wsobj = New-Object -ComObject wscript.shell
  $result = $wsobj.popup($errormsg) #ポップアップを表示
}

try {
  $excel = New-Object -ComObject Excel.Application
  $excel.Visible = $true
  $workbook = $excel.WorkBooks.Open($filePath)
  $sheet = $workbook.Sheets.Item("マクロ実行") #マクロを実行するシート名
  $excel.Run($target)
} catch {
  Write-Output "例外エラーです"
  Write-Output ('Error message: ' + $_.Exception.Message)
}
```

## コードの説明・補足
### `Param([String]$filePath)`
スクリプト実行時にファイルパスを引数として受け取るパラメーターを定義しています。
`[]`にはパラメーターのデータ型を指定します（例: `[string]`, `[int]`, `[bool]`など。）
```powershell
    Param(
        [型] $ParameterName,
    )
```

カンマでつなげて複数のパラメーターを指定することもできます。
```powershell
    Param(
        [型] $ParameterName,
        [型] $ParameterName
    )
```


### `New-Object -ComObject`
.NET Framework オブジェクトまたは COM オブジェクトのインスタンスを作成します。

### `wscript.shell`
**Windows Script Host (WSH)** の一部で、Windows環境を操作するためのオブジェクトです。このオブジェクトを使うことで、スクリプトから様々なWindowsシステムの機能を呼び出すことができます。

#### 主な用途
- **ファイル操作**: ファイルの作成、削除、コピー、移動など
- **アプリケーションの実行**: 外部プログラムの実行
- **レジストリの操作**: レジストリキーの読み書き
- **環境変数の操作**: 環境変数の設定や取得
- **フォルダの操作**: フォルダの作成、削除
- **ショートカットの作成**: ショートカットの作成


### `$excel.WorkBooks.Open($filePath)`
指定されたファイルを開きます。

### `$sheet = $workbook.Sheets.Item("マクロ実行")`
マクロを実行するシートを取得します。
今回はマクロを定義している`"マクロ実行"`というシート名を設定しています。

### `$excel.Run($target)`
指定されたマクロを実行します。

以下の行で実行したいマクロの場所を指定しています
`$target = "Module1.PasteClipboard"`

- Module1: マクロが記述されているモジュールの名前
- PasteClipboard: モジュール内のマクロの名称（＝プロシージャ名）

#### モジュールとプロシージャの関係
そもそも`Module1.PasteClipboard`のような例のように、<br>モジュール名とプロシージャ名を指定するのは、**特定のマクロ（プロシージャ）を正確に呼び出すため**です。

- **モジュール**: プロジェクトを構成しているより小さなプログラムの構成単位
- **プロシージャ**: プログラムを構成する最小単位


>プロシージャが命令を書いた**手順書**だとするとモジュールはその手順書を記述する**場所(エリア)**に相当します。
ひとつのモジュールに複数のプロシージャを記述することができます。
>![module2-500x329.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677000/786fb797-9882-c00f-665e-cb920d20fb8e.png)
>引用：[【ゼロからわかるVBA入門】モジュールとプロシージャ](https://tech-paclab.com/module-procedur/)

### `try~catch`
プログラムが実行中に発生するエラーや例外を処理するための構文です。
他の言語（FW）でもよく使われる構文ですが、PowerShellでも使用することができます。

### `$_.Exception.Message`
catchブロック内で、発生した例外に関する情報を格納しているオブジェクトのメッセージプロパティにアクセスすることで、エラーメッセージを取得します。

`$_`は、パイプラインを通して渡されてきた現在のオブジェクトを表す自動変数です。
上記のcatchブロック内では、**発生した例外オブジェクト**を指します。

この自動変数は、システムによって自動的に生成されて特定の情報を保持します。


### `Write-Output`
- **値の表示**: 変数の中身、コマンドの実行結果などをコンソールに表示します
- **パイプラインへの渡す**: Write-Outputで出力された値を、次のコマンドの入力として使用します。これにより、複数のコマンドを繋げて複雑な処理を行うことができます

```powershell
# 変数の値を表示
$message = "Hello, World!"
Write-Output $message

# Get-Processコマンドの結果を表示
Get-Process | Write-Output

# Get-Processの結果をWhere-Objectで絞り込み
Get-Process | Where-Object {$_.Name -eq "notepad"} | Write-Output
```

### `Get-Process`
PowerShellで実行中のプロセスに関する情報を取得します。

例えば、以下のようなプロセスが表示されます
```powershell
PS C:¥Users¥username¥Downloads> Get-Process
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)  Id  ProcessName
-------  -------  -------  -------  -------  --  -----------
   1061     2176   11372    42860       0.03 1484 System
   1227     2724   14424    54540       0.02 1844 svchost
   1262     2796   14904    56088       0.01 1924 svchost
   1385     3072   16432    61644       0.03 1364 svchost
   1448     3180   17008    63724       0.01 2348 svchost
   1483     3252   17460    65484       0.01 2088 svchost
   1554     3396   18160    68204       0.01 2264 svchost
   1619     3540   18860    70924       0.01 2428 svchost
   1690     3684   19560    73644       0.01 2592 svchost
   1761     3828   20260    76364       0.01 2756 svchost
```

# おわりに
バッチなどの異なるスクリプト言語同士を連携させることで、より複雑な自動化タスクを実現することができそうです。
PowerShellを使えるようになることで、システム管理や業務効率化の幅が広がりそうですね！

# 参考記事
- [PowerShell とは](https://learn.microsoft.com/ja-jp/powershell/scripting/overview?view=powershell-7.4)
- [PowerShell 実行ポリシー](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4#powershell-execution-policies)
- [【ゼロからわかるVBA入門】モジュールとプロシージャ](https://tech-paclab.com/module-procedur/)