Windowsにおける仮想的ディスプレイ

提供:Turgenev's Wiki

例えばZoomでプレゼンテーションをするときに、PowerPointの「発表者ツール」を使いたければ、普通は2つのディスプレイが必要である。しかしディスプレイが常に2つあるとは限らないので、画面共有に使用するだけの「仮想的なサブディスプレイ」があればいいのではないか、という発想がうまれる。他には、リモートのみで使用するPCや、ゲーム画面の配信などにおいても需要があるようである。

解決策1 - ダミープラグ

そこそこ普及している、ハードウェア的な解決策である。端子に差し込むとディスプレイが接続しているものと誤認させることができる。EDIDエミュレータとも呼ぶ?価格は500-1000円程度。4K対応かどうかなどスペックにより値段に多少の差はあるだろう。近年はHDMIが主流と思われるが、VGAのものもある(こちらは自作が可能との情報も)。

金はかかるが、後者の解決策にないメリットとして、一気に複数のOSに対応できる(製品による?)というのがある。

解決策2 - ソフトウェア的に

しかしPCを騙すためだけの虚構的な存在に金を払うのも考えれば面白くない話である。ソフトウェア的に解決できないだろうか。ということでこの記事ではWindowsに対するソフトウェア的解決策を紹介する。なおLinuxについてはxvfb?だとか、そういうのを使えばできそうな気がしたが今のところ手元ではできていない。できたら記事を書くかも。Macは持っていないので試せないが、https://github.com/tSoniq/displayxとか、betterdisplayとかがあるらしい。PowerPointやゲーム実況という用途を考えるとWindowsが一番需要が大きそうではある。

ソフトウェア的解決策が欲しいといってすぐソフトが出てくればいいが、調べてもあまり出てこない。比較的それっぽくて怪しくなさそうだったのが

https://www.amyuni.com/forum/viewtopic.php?t=3030

だが、それでもやはりちょっと素性が知れない。もう少し調べていくと、Indirect Display Driver (IDD)という技術を使うとそういうソフトウェアが書けるというMicrosoftのドキュメントが出てきた。そこからさらに調べてみるとこんなものがあった。

link_preview

これを試したら無事動いたので、ついでに解像度を変えてみたら、普通に8Kくらいまで動いた(ただしその分重くなる)。↑のレポジトリでも解像度増やしてというissueがあったので、forkして公開した。さらにver0.0.1.2では"C:\IddSampleDriver\option.txt”というファイルを設定することで解像度オプションやディスプレイの個数をユーザーが設定できるように変更した。

link_preview

本家のreadmeで紹介された結果、どうやらゲーム配信関係のRedditとかに載ったみたいで、実質的にはほぼ何もしていないのに400以上のstarを獲得している。

以下、このソフトの話を主にしていく。

インストール手順

Scoopを使う方法と使わない方法の2種類がある。いずれもリポジトリのREADME.mdに書いてある通りで、それほど難しくはない。

Scoopを使う

筆者がかかわったものではないが、Scoopを用いてインストールができるようにしてくれた方がいるようである。インストールを行うスクリプトのソースコードはhttps://github.com/ScoopInstaller/Nonportable/blob/master/bucket/iddsampledriver-ge9-np.jsonにある。

通常のインストール

途中で、バッチファイルを用いて証明書(.cerという拡張子のファイル)をローカルコンピューターの「信頼されたルート証明機関」に登録する必要がある。そのバッチファイルではcertutilが使用されている(ちなみに以前のリリースではcertmgrが使用されていた)が、証明書を開いて表示される画面からの操作などによっても可能である。

登録を行わないと、デバイスマネージャで「このドライバーはデジタル署名されていません。」と警告が出て、最終的に「デバイスをインストール中にエラーが発生しました。」「ストアへドライバーを追加する際に問題が発生しました。」と言われて失敗する。

開発者向け情報

Indirect Display DriverについてはMicrosoft公式のドキュメントや公式サンプルhttps://github.com/microsoft/windows-driver-samples/tree/master/video/IndirectDisplay を参照(IddSampleDriverはこれを編集してできたようである)。前述の通り自分は解像度を増やしただけなので技術的なことについては何もわかっていない。

公式サンプルはなんといっても公式のものであるから、まずはこれをビルドできる状態にするのが確実だろう。以下の説明は2021年8月頃にWindows10で試したときのものである。ビルドしようとするとWindowsUserModeDriver10.0がないと言われるので、https://docs.microsoft.com/ja-jp/windows-hardware/drivers/download-the-wdk を入れる。

すると次は「Spectre軽減策」(セキュリティ関係?)に関するエラーが出た。そこでVisual Studioのインストール時の「個別のコンポーネント」→「コンパイラ、ビルドツール、およびランタイム」のところから「最新」「(ARMではなく)x86とx64」と書いてある「Spectre軽減ライブラリ」を入れる(細かい表示は正確に記録していないかもしれないor変わっているかもしれない)。「SDK、ライブラリ、およびフレームワーク」にあるMFCとかATLみたいなやつは要らなさそうっぽい。

これでビルドが通った。exeを管理者権限で起動するとBluetoothのところのその他のデバイスとかに表示されるところまではいった。tmhがないとかSpectre関連のエラーとかwdfのエラーとか色々出ていたが全部大丈夫になった。

  • https://learn.microsoft.com/ja-jp/windows/win32/api/swdevice/nf-swdevice-swdevicecreateに書いてあるように、SwDeviceCreateを呼びだすには管理者権限が必要である。従って、IddSampleDriverのようなソフトウェアをインストールするには管理者権限が事実上必須ということになる。ただし、インストールされたIddSampleDriverというデバイス自体は一般ユーザーでも使用可能である。

では次に本題のIddSampleDriverをビルドする。公式サンプルがビルドできている状況ならこちらも通るものと期待できる。少なくとも手元ではそうなった。逆にIddSampleDriverから始めてみても同様に、WindowsUserModeDriver10.0のエラー→Spectreのエラー、などとなった。

以下、当時の元リポジトリをビルドした際のメモ

  • ソースコードに赤線は出ていたがコンパイルは通る。署名に関するエラー(file digestがなんとか)が出るのでプロジェクトのプロパティの下のほうのDriver SigningのFile Digest Algorithmに SHA256 と書く。
  • 解像度やリフレッシュレートの設定が不適切だと、デバイスマネージャで何度か再読み込みされる感じの動作をしたあと自動的に停止され警告アイコンが表示される。
  • 日付を超えるとタイムゾーン(筆者はJST=UTC+9)の関係でInf2Catが失敗する。プロジェクトのプロパティからInf2Catのメニューに行き、General → Use Local Time を「はい (/uselocaltime)」に設定する。参考: https://monoist.atmarkit.co.jp/mn/articles/1307/26/news003_3.html
    • さらに、これを設定してもなぜか現地時間の21:00~24:00の間にビルドしたものは正常動作しなかった(ビルドは通るしデバイスマネージャでの警告アイコンも出ないのにサブディスプレイが出ない)ので、ビルド時はその範囲外になるようにPCの時計設定を適宜変更する(報告済み https://github.com/roshkins/IddSampleDriver/issues/5

自分でビルドしたやつのインストール時は、(おそらく自分のユーザー名を用いて署名が作られているため?)最初の一回のみ改めて.cerを登録する必要がある。二回目以降は、サブフォルダに生成されてるcat, dll, infをもってきて置き換えたあとデバイスマネージャから「ドライバーの更新」をすればよい。デバイスマネージャーは使ったフォルダをちゃんと覚えているようなので、頻繁に移動しなくていい安定した場所でビルドを行うのがいいだろう。

HDR対応について

GitHubのissueでも複数回挙がっているのが、HDR (High Dynamic Range)への対応であるが、現在のところ解決の目処は立っていない。

  • IDDの枠組みではそもそもサポートされてない?

https://github.com/MicrosoftDocs/windows-driver-docs-ddi/issues/199

  • 報奨金を設定した人がいる

https://github.com/5andr0/Virtual-HDR-Display/issues/1

その他参考情報

このIDDというのはUser-modeで動作するやつなのだがKernel-modeで動作するKMDODとかいうものもあるらしく、そっちの方が多くのことができるのかもしれない。ただし調べた感じ、ただちに使えそうなものは見つからなかった。まあWindowsのディスプレイドライバなんか趣味でそうそう書くもんではないのでしょうがない。

一応適当に調べて出てきた参考リンクなど

  • IDDとKMDODに関する議論(2019年)

https://community.osr.com/discussion/291368/wddm-display-only-driver-to-create-a-virtual-display-monitor

  • KMDODで仮想ディスプレイを作れるかどうかについて(2017年)

https://community.osr.com/discussion/286843/how-to-implement-virtual-display-adapter

    • そこで言及されているLin JiaBang氏のソフト(動作未確認)

https://github.com/LinJiabang/virtual-display

  • WDDMで仮想ディスプレイを作れるかどうかについて(2016年)

https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/2d60c1c2-5198-47e2-b5d9-2ba28461e72e/how-to-implement-virtual-display-adapter-driver-on-windows-10?forum=wdk

課題

あくまで仮想的なディスプレイであるからその中身を目視で確認することはできない(ただし別途その内容をウインドウに表示するソフトなどは作れそう)。従って例えば、これを使って共有している間にZoomのミーティングコントロールが非表示になってミュートのon/offなどができなくなるなどの不便さはある。

ちなみに、Windowsでのマルチモニター管理を切り替えるメニューのショートカットはWin+Pであるが、ここで「セカンドスクリーンのみ」を選ぶなどして仮想でないほうのディスプレイが真っ暗になってしまったときはWin+Pを押せば復帰できる。

変更記録

もともとディスプレイは5個追加されるが、重過ぎるのでNUM_VIRTUAL_DISPLAYSを1に変更。

解像度を大きくしたいのでソースコードを「1920」で検索し、3840x2160と2560x1440を追加する。

まず s_KnownMonitorModes のところ。この際リフレッシュレートが60HzになるようにPixel Clock Rateをちゃんと計算する必要がある。

{
      229008 * KHZ,                                      // pixel clock rate [Hz]
    { 229008 * KHZ, 2560 + 40 },                         // fractional horizontal refresh rate [Hz]
    { 229008 * KHZ, (2560 + 40) * (1440 + 28) },          // fractional vertical refresh rate [Hz]
    { 2560, 1440 },                                    // (horizontal, vertical) active pixel resolution
    { 2560 + 40, 1440 + 28 },                         // (horizontal, vertical) total pixel resolution
    { { 255, 0 }},                                   // video standard and vsync divider
    DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE
},
{
      509366 * KHZ,                                      // pixel clock rate [Hz]
    { 509366 * KHZ, 3840 + 40 },                         // fractional horizontal refresh rate [Hz]
    { 509366 * KHZ, (3840 + 40) * (2160 + 28) },          // fractional vertical refresh rate [Hz]
    { 3840, 2160 },                                    // (horizontal, vertical) active pixel resolution
    { 3840 + 40, 2160 + 28 },                         // (horizontal, vertical) total pixel resolution
    { { 255, 0 }},                                   // video standard and vsync divider
    DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE
},

のようにする。Pixel Clock Rateの数値は多少のズレがあってもいいみたい?だけど気分的に少しだけ大きめに設定してある。

そのすぐ下のEDIDのところはそのままでよさげ。

そして、一番下のほうのTargetModesのところを変える。配列サイズの変更も忘れずに。いやvectorだから関係ないか。

ちなみにs_KnownMonitorModesがノータッチだとここには1920x1280までしか書けないっぽい。

EDID編集に関して(メモ)

HDR機能の追加に使えるかと思ったが結局効果はなかった。

Deltacast E-EDID Editorがふつうに有能。ここに書いてあった: https://ameblo.jp/holycater/entry-12467348533.html

https://www.ninshoshiken.com/about-ajsc1-edid-editor/

http://blawat2015.no-ip.com/~mieki256/diary/201602063.html

http://www.edidreader.com/