3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PowerShell でもマルチディスプレイの出力モードを切り替えたい(複製・拡張)

Posted at

[Win] + [P] でマルチディスプレイの出力モードを切り替えられることは有名ですよね。Windows 11 の場合、下図のクイック設定が表示されます:

Display-Switcher-Win11.png

一方、クイック設定を開かずにコマンドラインから切り替えたいケースもあるでしょう。そんなときのために、 PowerShell でマルチディスプレイの構成を変更できるコマンドレットを作成してみました。

Switch-DisplayMode コマンドレット

Win32API の SetDisplayConfig 関数でマルチディスプレイの構成を変更する PowerShell スクリプトです。なお、コード全文はこちらで公開しています。

Switch-DisplayMode.ps1
# Win32API の SetDisplayConfig 関数を読み込む
$cscode = @"
    [DllImport("user32.dll")]
    public static extern UInt32 SetDisplayConfig(
        UInt32 numPathArrayElements, 
        IntPtr pathArray, 
        UInt32 numModeInfoArrayElements, 
        IntPtr modeInfoArray, 
        UInt32 flags
    );
"@
$Win32Functions = Add-Type -Name Win32SetDisplayConfig -MemberDefinition $cscode -PassThru

# 引数 flags で使用するビット値。
$SDC_APPLY = 0x00000080
$SDC_TOPOLOGY_INTERNAL = 0x00000001
$SDC_TOPOLOGY_CLONE = 0x00000002
$SDC_TOPOLOGY_EXTEND = 0x00000004
$SDC_TOPOLOGY_EXTERNAL = 0x00000008

# システムエラーコード
$ERROR_SUCCESS = 0x0
$ERROR_ACCESS_DENIED = 0x5
$ERROR_GEN_FAILURE = 0x1F
$ERROR_NOT_SUPPORTED = 0x32
$ERROR_INVALID_PARAMETER = 0x57
$ERROR_BAD_CONFIGURATION = 0x64A 

# 引数を元にビット値を計算する
$action = switch ($Args[0]) {
    # 永続化データベースから最後の内部構成を設定する
    "internal" { $SDC_TOPOLOGY_INTERNAL }
    # 永続化データベースから最後の複製構成を設定する
    "clone" { $SDC_TOPOLOGY_CLONE }
    # 永続化データベースから最後の拡張構成を設定する
    "extend" { $SDC_TOPOLOGY_EXTEND }
    # 永続化データベースから最後の外部構成を設定する
    "external" { $SDC_TOPOLOGY_EXTERNAL }
    # 現在接続されているモニターの最後の既知の表示構成を設定する
    default { $SDC_TOPOLOGY_INTERNAL -bor $SDC_TOPOLOGY_CLONE -bor $SDC_TOPOLOGY_EXTEND -bor $SDC_TOPOLOGY_EXTERNAL }
}

# SetDisplayConfig 関数を実行する
$result = $Win32Functions::SetDisplayConfig(0, [IntPtr]::Zero, 0, [IntPtr]::Zero, $action -bor $SDC_APPLY)

# エラーハンドリング
$err = switch ($result) {
    $ERROR_SUCCESS { return }
    $ERROR_ACCESS_DENIED { "現在のデスクトップにアクセスできないか、リモートセッションで実行されている可能性があります。" }
    $ERROR_GEN_FAILURE { "未指定のエラーが発生しました。" }
    $ERROR_NOT_SUPPORTED { "Windows ディスプレイドライバーモデル (WDDM) に従って記述されたグラフィックスドライバーが実行されていません。" }
    $ERROR_INVALID_PARAMETER { "指定されたパラメーターとフラグの組み合わせが無効です。" } 
    $ERROR_BAD_CONFIGURATION { "呼び出し元が指定しなかったソースモードとターゲットモードの実行可能なソリューションを見つけることができませんでした。" }
    default { "未知のエラーが発生しました。エラーコード: $result" }
}
throw $err
使用方法
# Switch-DisplayMode コマンドレットをインポートする
. .\Switch-DisplayMode.ps1

# 永続化データベースから最後の内部構成を設定する
Switch-DisplayMode -DisplayMode internal

# 永続化データベースから最後の拡張構成を設定する
Switch-DisplayMode -DisplayMode extend

# 永続化データベースから最後の複製構成を設定する
Switch-DisplayMode -DisplayMode clone

# 永続化データベースから最後の外部構成を設定する
Switch-DisplayMode -DisplayMode external

余談、あるいは Windows 7 の発売が 15 年前という信じがたい現実

今回利用した SetDisplayConfig 関数は Windows 7 以降で使用できます。これは Connecting and Configuring Displays (CCD) API が Windows 7 以降に搭載された機能であるためです。一方、以前ご紹介した ScreenResolutionChangerEx が使う ChangeDisplaySettingsEx 関数は Windows 2000 以降とより長い歴史を持ちます。

そもそも [Win] + [P] でディスプレイスイッチャを表示するショートカットが実装されたのも Windows 7 からです。

Display-Switcher-Win7.png

Windows Vista 以前の場合、直接ディスプレイのプロパティを変更する必要がありました。そのため、Windows XP 用に C# でディスプレイスイッチャを作成する方法を紹介する記事が書かれていたりしました。当該記事では ChangeDisplaySettingsEx 関数を利用していますが、SetDisplayConfig 関数と比べ冗長なコードが必要だったようです。

本稿執筆の参考として Windows 7 発売直後のブログ記事等を漁っていたのですが、なんだか懐かしい気持ちになってしまいました。15 年前といえば 1.5 むかしに相当しますから、ずいぶんと時間が経ってしまいました。当時は Windows Aero が格好良く見えたものだけどなぁ…。

参考リンク

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?