リモートデスクトップやHMD接続時に「仮想ディスプレイアダプタ」が必要となる場面があるだろう。
Amazon等で「HDMI ダミーアダプタ」を購入しても良いのだが、多少なりともコストが掛かる上に、出力ポートを占有してしまう。
ダミーディスプレイをソフトウェアで再現する「Virtual Display Driver」を使えば、追加費用なしで実現可能だ。
本記事では、使い方と設定手順を実際に見ていく。
<検証環境>
マシン:Surface Pro 9(Intel Core i7-1255U)
OS:Windows 11 23H2
「Virtual Display Driver」の設定手順
ソフトウェアのダウンロード
GitHubからZIPファイルをダウンロードする。
itsmikethetech / Virtual-Display-Driver - GitHub
筆者の環境(Windows 11 23H2)では、HDR対応/非対応ともに安定版がうまく動かず、ベータリリースの方(24.10.27)は正常に動作した。
「IddSampleDriver.zip」をダウンロードする。ベータ版の場合は自分のPCのCPUに合わせて「VirtualDisplayDriver-x64.zip」または「VirtualDisplayDriver-ARM64.zip」を使用。
ダウンロードしたZIPは展開し、任意の場所に移動しておく。
証明書のインストール
「IddSampleDriver」フォルダ内にある「installCert.bat」を管理者権限で実行する。
セキュリティ警告が出るが無視して詳細情報 > 実行の順に押下する。
証明書が正しくインストールされると上記のような画面が出るので、キーを押して終了する。
ドライバーの追加
デバイス マネージャーを開く。
メニューバーの操作 > レガシー ハードウェアの追加でウィザードを開始する。
インストール方法は「一覧から選択したハードウェアをインストールする(詳細)」。
共通ハードウェアの種類は「ディスプレイ アダプター」を選択し、次へ。
右下の「ディスク使用」を押下。
「製造元ファイルのコピー元」には、展開したZIPファイルのうち「IddSampleDriver」フォルダのパスを入力する。
モデル「IddSampleDriver Device HDR」が出現しない場合は、きちんとフォルダパスが入力されているか要確認。
ウィザードを進めると、一瞬画面が暗転して、そのまま完了する。
デバイス マネージャー上にディスプレイ アダプターが追加されていれば成功!
同様の手順を繰り返せば、2枚以上に仮想ディスプレイを増やせる。
解像度・リフレッシュレートの変更
Windowsの設定から、画面解像度やリフレッシュレート、ディスプレイ配置等を変更可能。
最大4K 165Hzまで定義されている。
再起動の度に設定がリセットされてしまう問題の対処法
Virtual Display Driverには、デバイスを再起動する度に、設定した解像度やディスプレイの拡張/複製が元に戻ってしまう、という問題点がある。
拡張/複製の設定
拡張の設定については「DisplaySwitch.exe」で変更できる。デスクトップにbatを置くと便利。DisplaySwitch.exe /extend
タスクスケジューラに登録し、起動時に走るようにしておくと、尚良し。
解像度の変更
ディスプレイ解像度の変更は、C#でWindows APIを呼ぶという一工夫が必要。
以下Generative AIとにらめっこして作ったコードなので悪しからず(セカンダリディスプレイの解像度変更は未対応、誰かやり方教えて)。
# 画面解像度を変更するPowerShellスクリプト
# C#コード
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class ScreenResolution
{
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public int dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
[DllImport("user32.dll")]
public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll")]
public static extern int ChangeDisplaySettings(ref DEVMODE devMode, int flags);
public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int DISP_CHANGE_SUCCESSFUL = 0;
public static bool SetResolution(int width, int height)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
if (0 != EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref dm))
{
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
int iRet = ChangeDisplaySettings(ref dm, CDS_UPDATEREGISTRY);
return iRet == DISP_CHANGE_SUCCESSFUL;
}
return false;
}
}
"@
# 解像度を設定
$width = 1920
$height = 1080
# 変更実行
if ([ScreenResolution]::SetResolution($width, $height)) {
Write-Output "解像度を${width}x${height}に変更しました。"
} else {
Write-Output "解像度の変更に失敗しました。"
}
ちょっとクセのあるツールだが、色々と応用できそうだ。