PowerShell でモーダルのメッセージボックスを表示する 【Windows 11/PowerShell】 - サポンテ 勉強ノート

サポンテ 勉強ノート

サポンテの勉強ノート・読書メモなどを晒します。

PowerShell でモーダルのメッセージボックスを表示する 【Windows 11/PowerShell】

はじめに

 シェルの実行中でも、ユーザーに注意を促すために GUI のダイアログを表示したい要件はしばしばあります。

 PowerShell で実現する方法を調べていたところ、いくつかの方法がありつつ、自分の求めているものとは違っていた部分もあったので、ここに記します。

要件

 サポンテが求める要件は以下の二つです。

  • PowerShell から GUI のダイアログを表示する。
  • 実行中の PowerShell のコンソールウィンドウの手前に表示されること。

 特に大切なのは後者です。実際に業務で「ダイアログがウィンドウの後ろ側に回り込んで、ダイアログが表示されていることが分からなかった」「ウィンドウが固まって操作が不能になった」「どのウィンドウで発生しているエラーか分からない」などのクレームをもらうことがあります。「ダイアログをモーダルで表示する」ところで考えが止まってしまって「どのウィンドウで管理されているダイアログなのか」を考慮していないアプリケーションは少なくないようです。ユーザーはとても困ってます。

実際のサンプル

 Windows 11 で動作確認しました。Windows 10 だとどうなるかは不明です。

$src=@'
using System;
using System.Runtime.InteropServices;

public static class Win32
{
    private static int GW_HWNDNEXT = 2;

    [DllImport("user32.dll")]
    public extern static int MessageBox(int hWnd, string msg, string caption, int uType);

    [DllImport("user32")]
    private extern static int GetParent(int hWnd);

    [DllImport("user32")]
    private extern static int GetWindow(int hWnd, int wCmd);

    [DllImport("user32")]
    private extern static int FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32")]
    private extern static int GetWindowThreadProcessId(int hWnd, out int lpDwProcessId);

    [DllImport("user32")]
    private extern static int IsWindowVisible(int hWnd);

    // プロセスID(pid)をウィンドウハンドル(hWnd)に変換する
    public static int GetHwndFromPid(int pid)
    {
        int hWnd;
        hWnd = FindWindow(null, null);
        while (hWnd != 0)
        {
            if (GetParent(hWnd) == 0 &&
                IsWindowVisible(hWnd) != 0 &&
                pid == GetPidFromHwnd(hWnd))
            {
                return hWnd;
            }

            hWnd = GetWindow(hWnd, GW_HWNDNEXT);
        }
        return hWnd;
    }

    // ウィンドウハンドルを(hWnd)をプロセスID(pid)に変換する
    public static int GetPidFromHwnd(int hWnd)
    {
        int pid;
        GetWindowThreadProcessId(hWnd, out pid);
        return pid;
    }
}
'@
Add-Type -TypeDefinition $src

# 自分自身を実行しているターミナルのプロセス ID を取得する
$parentPid = 0
Get-WmiObject win32_process -filter processid=$pid | ForEach-Object { $parentPid = $_.parentprocessid; }

# 自分自身を実行しているターミナルの Window Handle を取得する
$hWnd = [Win32]::GetHwndFromPid($parentPid)

# 取得できないなら止むを得ない。無念...。
if ($hWnd -eq 0) { $hWnd = $null; }

# メッセージボックスを表示する
[Win32]::MessageBox($hWnd, "コンテキスト", "キャプション", 0)

参考にしたサイト様

 以下のサイトを参考にしました。ありがとうございます。

PowerShellでメッセージボックスを使う4つの方法 - 適材適所 PIDとHWNDの変換(C#/VB.NET) [サンプルソース] [ヨーキー景吾の逃走]

 あと、ターミナルのプロセス ID を取得するところは Yahoo!知恵袋 さんのどこかのアンサーを参考にしたのですが失念してしまいました。