UiPathからVBAコール時、返却値が返ってこないパターンまとめ

# はじめに
UiPath から [Execute Macro](https://docs.uipath.com/activities/lang-ja/docs/execute-macro) アクティビティを使用しての VBA コール時、<font color="red"> VBA からの返却値を取得できずに少しハマッた</font>ことがあったので、そのときにわかったことを記事としてまとめておきます。

[Execute Macro](https://docs.uipath.com/activities/lang-ja/docs/execute-macro) アクティビティ使用時、これを知っておいて損は無いと思います。

# 前提
UiPath と VBA の連携を想定した際の「VBA 実装の基本方針」は、UiPath 社様が公式のナレッジベースにまとめて下さっているので、まずは以下の記事を読んでいることを前提として本記事を記載します。

https://www.uipath.com/ja/resources/knowledge-base/vba-macro-onerrorgoto

# 結論から
VBA は必ず**標準モジュール内に実装**しましょう。
それ以外の箇所に実装した場合、<font color="red">マクロは動作しても返却値だけが返ってこない</font>、という奇妙な現象に遭遇します。

次章以降で詳しく解説します。

# 検証環境
- UiPath
    - Studio Pro 2021.8.0-beta.5246 Community License

- 使用パッケージ
    - UiPath.Excel.Activities v2.10.4
    - UiPath.System.Activities v21.4.1

# 検証内容
Excel VBA を、UiPath からコールします。
Excel VBA 側には 100% エラーになる処理を入れておき、UiPath 側 で VBA でのエラー発生有無を拾えるかどうか、を検証ポイントとします。

### Excel VBA
検証に使用する Excel VBA は以下です。
UiPath 社様の [ナレッジベース](https://www.uipath.com/ja/resources/knowledge-base/vba-macro-onerrorgoto) を元にしています。

簡単に解説すると、

- 正常時：空白を返す
- 異常時：エラー内容を String 型で返す

上記の返却値を持つ関数で、今回は処理内容に `Err.Raise` を入れているため必ずエラーになります。

```vb:VBA
Option Explicit

'/**
' * 100%エラーが発生するサンプル。
' *
' * @return {String} - 正常時は空白, 異常時はエラー内容を返す
' */
Public Function VbaSample() As String
' エラートラップ
On Error GoTo ERR_HANDLER_
    
    ' 意図的にエラーを発生させる
    Err.Raise 9999, Description:="エラー内容"
    
    ' 正常終了（空の値を返す）
    VbaSample = ""
    Exit Function
    
' エラーハンドラー
ERR_HANDLER_:
    
    ' エラー詳細情報を返す
    VbaSample = "Excelマクロ内でエラーが発生しました。" & vbCrLf & _
                "エラー番号：" & Err.Number & vbCrLf & _
                "エラー内容：" & Err.Description
End Function
```

### 検証動作
1. UiPath から Execute Macro アクティビティを使用し、上記 Excel マクロをコール
1. Excel マクロ内でエラー発生、エラー内容を返却
1. UiPath 側で返ってきたエラー内容をログに出力

上記の動作を、<font color="red">Excel VBA の保存場所を変えるだけ</font>の以下3パターンで、実動作を見ていきたいと思います。
[パターン①：VBA の保存場所：標準モジュール](#パターンvba-の保存場所標準モジュール)
[パターン②：VBA の保存場所：Sheet](#パターンvba-の保存場所sheet)
[パターン③：VBA の保存場所：ThisWorkbook](#パターンvba-の保存場所thisworkbook)

# パターン①：VBA の保存場所：標準モジュール
まずは Excel VBA の保存場所を、**標準モジュール** にして検証します。

### VBA 実装
- Excel ファイル名：VBA_Module.xlsm
- VBA 保存場所：標準モジュール（Module1）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/46d080c3-409c-d8fb-2340-e5fffd2084d4.png)

### UiPath 実装
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/a7ecd99e-c467-d547-5ba7-4b869eaa3996.png)

**マクロを実行** アクティビティの出力変数に、Object 型変数 `MacroResult` を設定。
その内容を **メッセージをログ** アクティビティで出力するだけ、というシンプルな実装になっています。

### UiPath 動作結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/09381959-89f8-cbd5-b28b-6242a3893b18.png)

Excel マクロ内でエラーが発生し、そのエラー内容を返却。
UiPath 側では、返却されたエラー内容を正常に出力できていることを確認できました！

### 結論
:::note info
VBA の保存場所が <B>標準モジュール</B> の場合、VBA からの返却値を UiPath 側で取得可能
:::

# パターン②：VBA の保存場所：Sheet
次は Excel VBA の保存場所を、**Sheet** にして検証します。
パターン①との変更点は、**VBA の保存場所を変えたのみ** です。

### VBA 実装
- Excel ファイル名：VBA_Sheet.xlsm
- VBA 保存場所：Sheet1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/bb338b09-f39c-a8f3-9ec2-da79d6d6ffb7.png)

### UiPath 実装
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/796215aa-d026-1644-3a62-d1249dc55795.png)

前項との実装変更箇所は以下一点のみです。

- **マクロを実行** アクティビティの、プロパティ `マクロ名`
    - 変更前：`Module1.VbaSample`
    - 変更後：`Sheet1.VbaSample`

### UiPath 動作結果
なんとエラーで止まってしまいました :scream:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/7957c18e-bad7-a07f-ef75-62523a87d450.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/e240130f-41d7-0f13-4610-9b7b920bdcba.png)

### エラー原因
エラー発生時の `MacroResult` 変数をローカルパネルで見てみると、`null` になっていました。
よって `MacroResult.ToString` が実行できずにエラーになっていたようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/f263f113-d16a-d843-9809-f20211f72d9d.png)

検証したところ、この `MacroResult` 変数については初期化してようがしてなかろうが、関係なく `null` になりエラーで落ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/01863068-35ee-14b0-86b3-1f82f9afbf73.png)

**マクロを実行** アクティビティから Sheet 内のマクロを呼ぶと、出力変数に設定した値は `null` になるのが仕様のようです。
（マクロの結果が正常でも異常でも関係なく、出力変数が `null` になってしまう）

### 結論
:::note alert
VBA の保存場所が <B>Sheet</B> の場合、VBA からの返却値は常に null になってしまう
:::

# パターン③：VBA の保存場所：ThisWorkbook
結論、Excel VBA の保存場所を **ThisWorkbook** にしてもパターン②とまったく同じ動作をします。
前項と同動作ですので、
<details><summary>本項の検証結果は折り畳んでおきます。（確認したい方は開いて下さい）</summary><div>

### VBA 実装
- Excel ファイル名：VBA_ThisWorkbook.xlsm
- VBA 保存場所：ThisWorkbook

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/5be9e755-7027-ddb7-4c74-67f4742e08c2.png)

### UiPath 実装
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/ee5d42c3-abe1-44d9-04a7-d1be095fd674.png)

前項との実装変更箇所は以下一点のみです。

- **マクロを実行** アクティビティの、プロパティ `マクロ名`
    - 変更前：`Sheet1.VbaSample`
    - 変更後：`ThisWorkbook.VbaSample`

### UiPath 動作結果
同じくエラーで止まってしまいました…。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/ff0d0ad9-1955-e009-18b5-c79f1160f1ef.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/be20a3dc-5c37-1171-1b33-16c867987e4b.png)

### エラー原因
やはりエラー発生時の `MacroResult` 変数が `null` になっておりエラーになっていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/3fc5d306-46cb-cf6e-57de-fda203e520a5.png)

**マクロを実行** アクティビティから ThisWorkbook 内のマクロを呼んでも、出力変数に設定した値は `null` になるのが仕様のようです。
（前項と同じく、マクロの結果が正常でも異常でも関係なく、出力変数が `null` になってしまう）
</div></details>

### 結論
:::note alert
VBA の保存場所が <B>ThisWorkbook</B> の場合、VBA からの返却値は常に null になってしまう
:::

# 結論まとめ
Excel VBA の保存先と動作結果をまとめました。

| VBA保存先      | マクロ動作 | 返却値 |
|:---------------|:----------:|:------:|
| 標準モジュール | 〇         | 〇     |
| Sheet          | 〇         | ×     |
| ThisWorkbook   | 〇         | ×     |


**マクロ自体は動作するのに、返却値が取得できたり できなかったりする**、という点が注意です。
上記のことから、UiPath から呼ばれることを前提とした VBA は標準モジュール内に配置したほうが無難と言えそうです。

# 最後に
私が知る限り、少なくとも2年以上前からこの動作は変わっていないので、これが UiPath の仕様なのでしょうね。
（**[Execute Macro](https://docs.uipath.com/activities/lang-ja/docs/execute-macro)** アクティビティの公式ページに説明が欲しい気もします）

# 関連リンク
- UiPath ドキュメントポータル：マクロを実行（Execute Macro）アクティビティ
https://docs.uipath.com/activities/lang-ja/docs/execute-macro

- ナレッジベース：VBA Macroの自動化対応時のエラーハンドリング（OnErrorGoto）
https://www.uipath.com/ja/resources/knowledge-base/vba-macro-onerrorgoto

- UiPath Forum：Execute MacroのMacro outputパラメータの使いかた
https://forum.uipath.com/t/execute-macro-macro-output/30869