VBAでTaskDialogを表示してみた

Windows Vista 以降から使用可能な Windows API の [TaskDialog](https://docs.microsoft.com/ja-jp/windows/win32/api/commctrl/nf-commctrl-taskdialog "TaskDialog function (commctrl.h) - Win32 apps | Microsoft Docs")をVBAで使ってみたときのメモです。

TaskDialog 自体の説明は、以下のページがわかりやすかったです。

> [C# で TaskDialog を使う | grabacr.nét](http://grabacr.net/archives/105 "C# で TaskDialog を使う | grabacr.nét")

# 動作確認環境

- Windows 10 Pro 1909 64bit
- Excel Microsoft 365 64bit

**OfficeのバージョンやVBAホストによっては、古いバージョンのDLLを参照してしまうため使用が難しい場合があります**

# コード

> [TaskDialog function (commctrl.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/win32/api/commctrl/nf-commctrl-taskdialog "TaskDialog function (commctrl.h) - Win32 apps | Microsoft Docs")

上記 API を簡単に呼び出せるように、列挙型などを定義・関数でラップ(ShowTaskDialog)したものになります。

上記 API だけだと出来ることは少ないですが、これだけでも MsgBox と差別化はできると思います。

GitHubにもコードを上げています→ [imihito_Sandbox/WinAPIforDialogs.bas at master · imihito/imihito_Sandbox · GitHub](https://github.com/imihito/imihito_Sandbox/blob/master/vba_WinAPI/WinAPIforDialogs.bas "imihito_Sandbox/WinAPIforDialogs.bas at master · imihito/imihito_Sandbox · GitHub")

```vb:TaskDialog
'TaskDialog の引数用の列挙型。
    '参考:https://memos-by-oxalis.hatenablog.com/entry/2019/09/22/223121
Public Enum TaskDialogButtons
    tdOK = 2 ^ 0
    tdYes = 2 ^ 1
    tdNo = 2 ^ 2
    tdCancel = 2 ^ 3 '設定しないと×ボタンも表示されない。
    tdRetry = 2 ^ 4
    tdClose = 2 ^ 5
    
    tdOKCancel = tdOK Or tdCancel
    tdRetryCancel = tdRetry Or tdCancel
    tdYesNo = tdYes Or tdNo
    tdYesNoCancel = tdYes Or tdNo Or tdCancel
End Enum

Public Enum TaskDialogIcons
    tdNone = 0&
    tdWarning = &HFFFF&     '黄色の△!  vbExclamation とかと同じ。
    tdError = &HFFFE&       '赤色の○× vbCritical とかと同じ。
    tdInformation = &HFFFD& '青色の○i  vbInformation とかと同じ。
    tdShield = &HFFFC&
End Enum

Private Enum HRESUT
    S_OK = 0&
End Enum

Private Declare PtrSafe Function _
    TaskDialog Lib "Comctl32.dll" ( _
        ByVal hwndOwner As LongPtr, _
        ByVal hInstance As LongPtr, _
        ByVal pszWindowTitle As LongPtr, _
        ByVal pszMainInstruction As LongPtr, _
        ByVal pszContent As LongPtr, _
        ByVal dwCommonButtons As TaskDialogButtons, _
        ByVal pszIcon As LongPtr, _
        ByRef pnButton As VBA.VbMsgBoxResult _
    ) As HRESUT

'今のプロセスで最前面にあるウィンドウのハンドルを取得する。
Private Declare PtrSafe Function _
    GetActiveWindow Lib "User32.dll" ( _
    ) As LongPtr

Public Function ShowTaskDialog( _
                 ByVal inContent As String, _
        Optional ByVal inButtons As TaskDialogButtons = TaskDialogButtons.tdOKCancel, _
        Optional ByVal inIcon As TaskDialogIcons = TaskDialogIcons.tdInformation, _
        Optional ByVal inTitle As String, _
        Optional ByVal inHeader As String, _
        Optional ByVal inParentHwnd As LongPtr = 0 _
    ) As VBA.VbMsgBoxResult
Rem .DESCRIPTION
Rem TaskDialog を表示する。
Rem .PARAMETER inContent
Rem ダイアログ内に表示するメッセージ。
Rem .PARAMETER inButtons
Rem 表示するボタン。省略時は「OK」ボタンと「キャンセル」ボタンが表示される。
Rem .PARAMETER inIcon
Rem タイトルとダイアログ内に表示するアイコン。省略時は青の○にiのアイコン。
Rem .PARAMETER inTitle
Rem ダイアログのタイトルに表示される文字列。省略時はホストアプリケーションのファイル名(Excelであれば"EXCEL.EXE")。
Rem .PARAMETER inHeader
Rem ダイアログ内で青字で表示される文字列。省略時は表示されない。
Rem .PARAMETER inParentHwnd
Rem 親とするウィンドウのハンドル。省略時はこのプロセスの最前面のウィンドウが親となる。
Rem .OUTPUTS
Rem ユーザーが押したボタン。
    
    '親ウィンドウが指定されなかった場合、このプロセスの最前面のウィンドウを親とする。
    If inParentHwnd = 0 Then inParentHwnd = GetActiveWindow()
    
    Const NullHInstance = 0
    Dim hr As HRESUT
    hr = TaskDialog( _
            inParentHwnd, _
            NullHInstance, _
            VBA.[_HiddenModule].StrPtr(inTitle), _
            VBA.[_HiddenModule].StrPtr(inHeader), _
            VBA.[_HiddenModule].StrPtr(inContent), _
            inButtons, _
            inIcon, _
            ShowTaskDialog _
        )
    Debug.Assert hr = S_OK
End Function
```

# サンプル

上記コードの`ShowTaskDialog`の使用例です。

## 最小限の引数指定

デフォルト設定では以下のような表示になります。

```vb:最小限の引数指定
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("inContent")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/108138c3-5500-714f-1c02-c9ee430cd8c8.png)

## ボタンの表示

第2引数は表示するボタンの指定になります。

```vb:最小限の引数指定
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("ボタンをたくさん表示", tdOKCancel Or tdRetryCancel Or tdYesNoCancel)
```

以下のように、指定したボタンが全て表示されるため、必要なボタンだけ指定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/d0011c32-b5bb-043d-95d0-5cb3525fc78c.png)

## アイコンの設定

```vb
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("アイコン無し", tdYesNo, tdNone)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/4d7d2f7f-60e4-0119-01a4-1b49a21336a0.png)

---

```vb
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("盾のアイコン", tdYesNoCancel, tdShield)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/cfc2508c-d626-afe0-f704-e62e9b63e309.png)

---

```vb
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("青色のｉのアイコン" & vbLf & "vbInformation 相当", tdOKCancel, tdInformation)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/97f2cc1d-3424-9948-b509-143424b9f8a3.png)

---

```vb
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("黄色の△のアイコン" & vbLf & "vbExclamation 相当", tdRetryCancel, tdWarning)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/aab1f02c-f585-52e5-e87b-f596e7a65393.png)

---

```vb
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("赤色の×のアイコン" & vbLf & "vbCritical 相当", tdClose Or tdCancel, tdError)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/e254ab0a-11b9-7cae-d1d6-3d998f0fe47d.png)

## 見出し部分の設定

`inHeader`の引数に文字列を渡すことで、ダイアログの上部に大きめの文字列で表示されます。

```vb
Dim userClickedButton As VBA.VbMsgBoxResult
userClickedButton = ShowTaskDialog("本文部分", inHeader:="見出し部分")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142842/6ca08a6f-cd17-50af-3932-3d947ae50a62.png)


# 参考サイト

[TaskDialog function (commctrl.h) - Win32 apps | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/win32/api/commctrl/nf-commctrl-taskdialog "TaskDialog function (commctrl.h) - Win32 apps | Microsoft Docs")
[C# で TaskDialog を使う | grabacr.nét](http://grabacr.net/archives/105 "C# で TaskDialog を使う | grabacr.nét")
[C#でWin32 API TaskDialog関数を呼び出すサンプルコード - カタバミさんのプログラミングノート](https://memos-by-oxalis.hatenablog.com/entry/2019/09/22/223121 "C#でWin32 API TaskDialog関数を呼び出すサンプルコード - カタバミさんのプログラミングノート")

# 関連記事

[VBAでWindowsAPIを使うには - Qiita](https://qiita.com/nukie_53/items/6a88ce82565e238a3c71 "VBAでWindowsAPIを使うには - Qiita")