風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

Surface Book 2は4K・60Hz・10ビット・HDRの夢を見るか?

PCを買い替えるような余裕が無いのもあって、いまだに2019年10月に購入したSurface Book 2 (15")をメインマシンとして愛用しているわけなんですが。
自宅ではいただきものの27インチ4Kモニタ(Dell U2720QM)に接続しているのですけれど、実はこれまでその性能を活かしきれていなかった……という衝撃の事実が発覚しました。

試行錯誤した結果、(限定的な条件ですが)Surface Book 2でも、4Kモニタに60Hz・色深度10ビット・HDR(High Dynamic Range)で出力可能、ということがわかり、現在は安定して使えております。

Surface Book 2を4Kモニタに60Hz・色深度10ビット・HDRで接続した状態

いまさらという話ですけれど、備忘のために記録しておきます。




結論

Surface Book 2から4Kモニタへの、60Hz・色深度10ビット・HDRでの出力は、

  • 本体のUSB-C端子から、DP Alt Mode(DisplayPort over alternate mode)対応のケーブル(DP1.4以上対応なら安心)でモニタのUSB-CもしくはDP端子に接続
  • その他の機器も接続したい場合、Surface Dock 2を使う(Surface Dock(無印)の方では、繋ぐだけで、本体側USB-C端子のDP Alt Modeが使えなくなってしまうので不可)
  • Surface Dock 2背面のUSB-C端子(ビデオディスプレイ対応)には、モニタを接続しないこと(接続してしまうと、モニタの方で入力信号として選択しなくても、本体側USB-C端子からモニタに出力されなくなる模様)

という条件でならば可能です。
当然ながらモニタやケーブルの方もそれぞれの規格に対応している必要があります
自分はWindows 10 Pro環境で下記のような機器で試しましたが、環境や設定が違うなどの要因で、実はDock/Dock2からでもできるよといった情報があれば、ご教示ください

経緯

最近になって、Surface Dockに直接接続しているものの動作が不安定(特にイーサネットが突然落ちてしまい、再起動するまで繋がらなくなる不具合発生)なため、やむを得ずSurface Dock 2を購入して入れ替えました

その際、Surface Dock 2のUSB-C端子(DP Alt Mode対応)からモニタに繋いだら30Hzでしか繋がらないから絶望しかけたけど、色々と試しているといつの間にか60Hzで繋がるようになった……とツイートしたのですが、


ここでとあるリプライを受けたのが調査するきっかけとなりました。

具体的には、モニタ(Dell U2720QM)の方はリフレッシュレート:60Hz/色深度(ビット深度):10ビット(10.7億色)/色空間:HDR 400に対応しているのに、8ビット/SDRになっているね、というご指摘でした。

せっかくモニタが対応しているのにそのモードで利用していないのはもったいないので、利用可能な条件を探してみた次第です。

試行

使用機器類

試行の際に使用した機器類は以下のようなものです。

略称 機器
PC Surface Book 2
モニタ Dell U2720QM
Dock Microsoft Surface Dock
Dock2 Microsoft Surface Dock 2
USB-Cケーブル モニタ付属のUSB Type-C(CーC)ケーブル
HDMIケーブル モニタ付属のHDMIーHDMIケーブル
DPケーブル iVANKY Mini DisplayPortーDisplayPortケーブル
DP変換ケーブル Silkland USB Type-CーDisplayPort 変換ケーブル
HDMIアダプタ・SANWA SUPPLY サンワサプライ Mini DisplayPortーHDMI 変換アダプタ
HDMIアダプタ・Dockteck Dockteck USB Type-CーHDMI アダプタ
HDMIアダプタ・Freesun Freesun Steam Deck(Nintendo Switch対応ドック・USB Type-CーHDMI変換機能を含む)
試行前の注意点

Dell U2720QMの場合、モニタ側の設定でディスプレイ>Smart HDRを「オフ」(初期値)以外にしておかないと、HDRが有効になってくれないようです。

また、ディスプレイ キャッシュをクリアするためのレジストリファイルを予めダウンロードしておき、接続方法を変更する際には(特にSurface Dock(無印)で試す場合や試した後には)随時クリアするようにしたほうがよいと思われます(クリアしないでやると、ときどき問題ないはずの条件でうまくいかない場合があるようです)。

試行結果
PC側端子 モニタ側 4K表示 60Hz 10ビット HDR 備考
本体USB-C(USB-Cケーブル) USB-C [^1][^2]
本体USB-C(DP変換ケーブル) DP [^1]
本体USB-C(HDMIアダプタ・DockteckーHDMIケーブル) HDMI × × [^1][^3]
本体USB-C(HDMIアダプタ・FreesunーHDMIケーブル) HDMI × × × [^1]
Dock Mini DP(DPケーブル) DP × ×
Dock Mini DP(HDMIアダプタ・SANWA SUPPLYーHDMIケーブル) HDMI × ×
Dock2 USB-C(DP)(USB-Cケーブル) USB-C × [^3]
Dock2 USB-C(DP)(HDMIアダプタ・DockteckーHDMIケーブル) HDMI × × × × [^4]
Dock2 USB-C(DP)(HDMIアダプタ・FreesunーHDMIケーブル) HDMI × × ×

[^1] Surface Dock接続時や、Surface Dock 2接続時でかつ裏面USB-Cポート(ビデオディスプレイ対応)にモニタを接続した場合には(モニタ側で入力信号として選ぶ/選ばないにかかわらず)、本体USB-Cからの映像出力は無効化されてしまう
[^2] USBとしては2.0の機能しか使えなくなる(=モニタ側のUSBハブとしての機能も実質使えなくなってしまう・ただしPower Delivery機能の方はどうなるかは未検証)
[^3] HDR使用時、色深度が「ディザリング使用時、8ビット」となってしまう
[^4] 本体側は認識しているが、モニタ側で「接続されていません」となってしまい、画面が表示できない

結果として、実質「本体側のUSB-C端子にモニタを接続する」しか選択肢がありませんでした。
本当はSurface Dock 2の背面USB-Cで使いたかったのですが、どうやっても色深度を10ビットにする方法がわからず、断念しました

また、モニタのUSB-C端子につなげてしまうと、USBとしての機能は2.0に制限されてしまうので、USB-CーDisplayPort 変換ケーブルを用いてDisplayPort端子の方に繋げるようにしています。
別途、Surface Dock 2の前面USB-C端子(ビデオディスプレイに対応していない方)をモニタのUSB-C端子に繋ぐことにより、モニタのUSBハブとしての機能も確保しています。
なお、背面USB-C端子に繋いでしまうと、本体USB-C側のDP Alt Modeが使えなくなってしまってハマります……
ちなみにこの構成だとモニタのUSB Power Delivery機能は活かすことはできませんが、まぁ問題ないでしょう……

付録

Surface Book 2|3・Surface Dock・Surface Dock 2のUSB-C(DisplayPort over alternate mode)/Mini DisplayPort仕様
機器 DPバージョン 伝送速度(名称) 伝送速度 DSC(圧縮) モニターx1 モニターx2
Surface Book 2 1.2 HBR2 17.28 Gbps - 4096x2304@60Hz 4096x2304@30Hz
2560x1600@60Hz
Surface Book 3 1.4a HBR3 25.92 Gbps 1.2 4096x2304@120Hz 4096x2304@60Hz
Surface Dock 1.2 HBR2 17.28 Gbps - 4096x2304@60Hz 4096x2304@30Hz
2560x1600@60Hz
Surface Dock 2 1.4a HBR3 25.92 Gbps 1.2 7680x4320@30Hz
3840x2160@120Hz
3840x2160@60Hz

当然ながら、BookとDockとを組み合わせて使う場合、低い方の性能に合わされてしまうことになります。
いろいろなページを参考にまとめましたが、推測で書いているところもあるため、不正確な情報があるかもしれません。
また、公式ページにおいては色深度10ビット対応やHDR対応等の情報は見つけられず、Surface Connect端子に関する仕様も見当たらないという……なんともユーザーに対して不親切だと思うのは自分だけですかね……

DPの伝送速度の規格そのものはUSB-Cのもの(3.0/3.1 Gen1/3.2 Gen1x1は5Gbps・3.1 Gen2/3.2 Gen2x1は10Gbps等)とは別物ですが、同じ信号線(SuperSpeed信号線)を利用しているため、無関係というわけではありません

たとえばDP1.2ではHBR2(最大17.28Gbps)まで使えますが、これは4レーン(SuperSpeed信号線4本)全てを使った場合であり、USB 3.1 Gen1用にSuperSpeed信号線2本を確保する場合には2レーンしか使えず伝送速度は半分となるため、4K表示だと30Hzまでしか使えなくなることになります。
逆に、4K・60Hz・色深度10ビットだと15.68Gbps必要となるため、4レーン全てが必要となり、USB 3.1用にSuperSpeed線が確保できない結果、USBの機能としては2.0までしか使えない、ということになります。

ちなみにSurface Book 3はDP1.4aで4K・120Hzにも対応となっていますが、色深度10ビットにすると32.27Gbps必要となるため、HBR3(25.92 Gbps)だと足りなくなってしまいますね。実際はどうなっているのでしょうね?
色深度10ビットは不可だったりするのか、それともDSC(Display Stream Compression)で対応しているのか……Surface Book 3をお持ちの方、情報をお待ちしております(笑)→ここを見る限り、Windows 11で4K・120Hz・10ビット・HDRで表示できているようですね(DSCが使われているかはわかりませんが)

セルの右横にユーザーフォームを出したかっただけなのに……

軽い気持ちというかほとんどネタでVBAのユーザーフォームを用いたなんちゃってモダンなDatePickerを作ってみたのですが、その際に「セルの右横にユーザーフォームを表示する」という一見簡単そうなことが一筋縄ではいきませんでした。

試行錯誤の結果、ひとまずは実用にできそうな手法がわかったので、備忘として記事にしておきます。

セルの右横にユーザーフォームを表示したかっただけなのに……




せっかちな方へ

「能書きはいいから具体例を出せ」という向きは、

[Excel][VBA] ユーザーフォームをアクティブセルの右横に表示する実装例 · GitHub

こちらに実装例(マクロ有効ワークシート(*.xlsm))とソースコードを置いてあるので、ご自分で読み解いてください。

前提知識

  • エクセルVBAでは、オブジェクト(Window、Range、UserForm等)の位置や大きさは、基本的に「ポイント」という単位で管理されている(Top/Left/Width/Height等のプロパティの数値は全てポイント単位)
  • 位置を表す座標系には、大きく分けて、画面*1の左上を原点とする「画面座標系」(WindowオブジェクトやUserFormオブジェクト等はこちらで考える)と、ワークシートの左上*2を原点とする「ドキュメント座標系」(Rangeオブジェクト等はこちらで考える)とが存在する*3
  • 「画面座標系」と「ドキュメント座標系」とでは、同じ1ポイントであっても、画面上における大きさは異なる場合がある(例えばウィンドウの拡大率などに左右される)
  • ポイントとピクセル(ドット・画素)との比率も、環境によって異なる場合がある(画面解像度や拡大率に左右される)*4

アクティブセルの右側にユーザーフォームを表示したいときの座標計算方法の具体例

ユーザーフォームをアクティブセルの右側に表示する場合の座標の求め方

アクティブウィンドウ上にあるアクティブなセルのすぐ右側にユーザーフォームを表示する際の、ユーザーフォームに指定する座標(Top/Leftプロパティ)の値(ポイント)の具体的な求め方を記します。

なお、ここでは例として、

画面 横3840ピクセル✕縦2160ピクセル(4K)・拡大率150%
アクティブウィンドウ Top:84.0・Left:221.5・Width:1379.0・Height:822.0・拡大率200%
ウィンドウ枠の固定 有り(B3の位置)
アクティブセル アドレス:BR118・Top:2106.0・Left:3726.0・Width:54.0・Height:18.0

のようになっているものとします。

アクティブセルの位置を画面座標系に変換

アクティブセルのプロパティ(Top/Left)は、ドキュメント座標系上のポイント単位の数値です。
ひとまずこれを、画面座標系へと変換します。

これには、ペイン(Pane)オブジェクトのPointsToScreenPixelsY()PointsToScreenPixelsX()メソッドを使用します。

このとき注意するのが、アクティブセルが属しているPaneオブジェクトを指定する必要があることです。
ウィンドウ枠の固定をしている場合、ActiveWindow.Panes.Countは最大4まで存在するため、アクティブセルがPanes(1)~Panes(4)のいずれの下にあるかを調べる必要があります。
もっとも、アクティブセルが前提なので、この場合は、PaneオブジェクトとしてActiveWindow.ActivePaneオブジェクトを指定するのが簡単でしょう。

これで、Top: 2106.0ポイント→1003ピクセル、Left: 3726.0ポイント→1399ピクセルのように、アクティブセルの左上の、画面座標上の位置をピクセル単位で求めることができます。

画面座標系上の位置をピクセル単位からポイント単位に変換

求まった値はピクセル単位ですが、ユーザーフォームのプロパティ(Top/Left)に設定しようと思うと、ポイント単位への変換が必要になります。

画面座標系におけるピクセル→ポイントの変換方法として、自分は、ActiveWindowのHeight・Widthプロパティ(ポイント単位)と、WinAPIであるGetWindowRect()により得られるActiveWindowの座標(Top/Bottom/Left/Right・ピクセル単位)から求まる高さ・幅との比率を求めて使う、という方法を思いつきました。
もっと簡便な方法があればご教示願います。

これにより、上記の例では1ポイント=2.0ピクセルと出ましたので、Top:1003ピクセル→501.5ポイント、Left:1399ピクセル→699.5ポイントのように、アクティブセルの左上の、画面座標上の位置をポイント単位に変換できました。

セル幅を画面座標上のポイント単位に変換

セルの「右」に表示するので、横方向の座標にはセル幅(Widthプロパティ)分を加える必要があります。

Widthプロパティはポイント単位なので、これを足せばいいのか……と思いきや、ドキュメント座標上のポイントと画面座標上のポイントでは実サイズが異なってきます。
具体的には、ウィンドウの拡大率に左右されます。
アクティブウィンドウの拡大率は、ActiveWindow.Zoom÷100で求まり、上記の例(200%)だと2.0となります。

よって、ドキュメント座標上のWidth:54.0ポイントは、画面座標上では108.0ポイントとなります。

ユーザーフォームのプロパティを設定

以上により、ユーザーフォームの設定すべき画面座標上の位置は計算上(ポイント値で)Top:501.5、Left:699.5+108.0=807.5となります。
あとは、ユーザーフォームのStartUpPositionプロパティを0(Manual)にして、TopとLeftプロパティに値を設定すればいいわけですが……さらにいくつかハマりどころがあるのが困ったところですね。

ハマりどころ

第3の座標系が存在!?(ユーザーフォームの位置が想定よりも右下にずれていく問題)

環境によっては(画面解像度や拡大率、マルチモニタ等が影響?)上記のようにして計算した値をユーザーフォームのプロパティにセットしても、想定位置からずれてしまう場合があります。

計算値と実際の表示位置がずれる

しかも、画面の右下に行くに連れてズレも広がっていくようです。

この現象に最初に遭遇したときにいろいろと試してみたところ、上記の計算値に一定の係数(試したケースでは14/15=0.93…)を掛けたものをTop/Leftプロパティに設定すると、想定位置に表示されることがわかりました。
いわば、画面座標系と原点は共通ですが、目盛(1ポイント当たりの大きさ)の大きい座標系(ユーザーフォーム座標系?)が存在するようなイメージです。

この係数は環境により異なってくるため、定数にすることはできません。
そこで、

  1. GetWindowRect()でユーザーフォームのピクセル単位での座標を取得し、これから高さと幅を求める(実測値)
  2. ユーザーフォームのHeight/Widthプロパティに、ActiveWindowから求めたピクセル/ポイント比をかけて、ピクセル単位の高さと幅を求める(想定値)

のように二種の方法で高さと幅を求め、想定値と実測値の比を割り出し、これを係数として使用することで、対応することにしました。

とりあえず、この補正を行うことで、状況は改善されたようです。

計算値と実際の表示位置のズレを補正
画面の右の方にいくと、突然ユーザーフォームが左側にずれるようになる

環境によっては(おそらくマルチモニタ環境でかつ特定の解像度や拡大率の場合に)、画面の左側だと問題なく表示されているのに、あるところから突然ユーザーフォームが想定位置よりも左に大きくずれ始める、という現象が発生することがあります。

画面の右の方でいきなりずれだす

調べてみると、Pane.PointsToScreenPixelsX()が返す値が、ある列より右側では明らかにおかしくなっていました

PoinstsToScreenPixelsXテスト(4K・150%)

しばらく原因がわからず途方にくれていたのですが、その後、たまたま気がついたエクセルの設定を変更することで改善されることがわかりました。

ファイル>オプション>設定>全般>ユーザー インターフェイスのオプション>複数ディスプレイを使用する場合
で、

◉ 表示を優先した最適化 (アプリケーションの再起動が必要)

にしていると発生することがあるようで、試しに

◉ 互換性に対応した最適化

に変更(これもエクセルの再起動は必要)すると、

全般>複数ディスプレイを使用する場合は「互換性に対応した最適化」を選択

正常動作するようになったようです。

PoinstsToScreenPixelsXテスト(4K・150%)・「互換性に対応した最適化」設定後
その他注意点・制限事項など

処理を簡便化するモジュール

上記の処理の一部を簡便化するためのプロシージャ群を標準モジュールとしてまとめてあります

ConvertToScreenPosition()
Type ScreenPosition
    x As Double
    y As Double
End Type

Function ConvertToScreenPosition(TargetTop As Double, TargetLeft As Double, Optional TargetWindow As Window) As ScreenPosition

ドキュメント座標系の座標(TargetTop/TargetLeft・ポイント)を、画面座標上の座標(ピクセル単位)に変換して返します(変換不可な場合にはx=0・y=0が返ります)。
TargetWindowで対象となるWindowオブジェクトを指定可能です(省略時はActiveWindowになります)。
例えば対象となるセルのTop/Leftを指定するだけでよく、Panesのどれに属しているかは意識しなくても済むようにひと工夫してあります。

GetDisplayDotsPerPoint()
Type DotsPerPoint
    x As Double
    y As Double
End Type

Function GetDisplayDotsPerPoint(Optional TargetWindow As Window) As DotsPerPoint

画面座標系上の1ポイントあたりのドット(ピクセル)数を返します。
TargetWindowで対象となるWindowオブジェクトを指定可能です(省略時はActiveWindowになります)。

SetUserFormPosition()
Type CoordinateFactor
    x As Double
    y As Double
End Type

Function SetUserFormPosition(TargetForm, Top As Double, Left As Double, Optional Calibration As Boolean = True) As CoordinateFactor

TargetFormで指定したユーザーフォームを、画面座標系上の指定位置(Top/Left・ポイント)に移動します(このプロシージャでは表示は行わないことに注意してください。別途、TargetForm.Showプロシージャで表示する必要があります)。
このとき、位置ずれ補正(画面の右下に行くほどにずれる現象に対する補正)も自動で行います(Calibration:=Falseで補正を無効化することもできます)。
戻り値として、適用した補正係数を返します(Calibration:=False時はx=1・y=1が返ります)。

具体的な使用方法は、実装例を参照してください

ひとりごと

ユーザーフォームってあまり使った経験がないのですけれど、まさかセルの右横にユーザーフォームを表示するだけでこんなに苦労することになろうとは思いもよりませんでした……。
よりよい方法があれば、ご教示願います。


*1:マルチモニタ環境における「画面」は、すべての有効なモニタ画面を包括するような仮想的な矩形となると思われる

*2:セルの設定で非表示になっているものは含まないため、必ずしもA1ではないことに注意

*3:座標系の名前は便宜上のもので、自分のソースコード内では「ディスプレイ座標系」「ワークシート座標系」などと書いてある場合もある(統一が取れていなくてすみません……)

*4:ネットではDPI(Dots Per Inch)を96として決め打ちしている実装をよくみかけるが、あまり望ましくないと考えられる(ポイント(DTPポイント)の方は1インチあたり72ポイントがもともとの定義。なお、これもPoints Per Inchを略してPPIと書いてしまうと、Pixels Per Inchと混同してしまうため(こちらがより一般的なPPI)混乱の元なので避けたほうがよさそう)

私のSurface Book 2が不安定なのはどう考えてもグラフィック・ドライバーが悪い!?

Surface Book 2をメインPCとして愛用しているのですが作業中に突然ディスプレイがブラックアウトして、ひどいときにはそのまま復帰せずに手の施しようがなくなる……という現象に悩まされてきました(なんどかブルースクリーンにもなった覚えがあります)。

その際に調べたところでは、「デスクトップ ウィンドウ マネージャー(dwm.exe)」がメモリを数GB~数十GB程も占有している、という状況でした(通常は数十MB程度のはず)。

どうやらこれは、Surface Book 2(自分のは15")のIntel(R) UHD Graphics 620用のドライバーが原因だったようです

「それなら、Intel® Graphics – Windows* DCH Driversを更新すればよいんでしょ?」となるわけですが……(少なくとも自分のところでは)一筋縄ではいかなかったので、試行錯誤の内容を備忘録として記しておきます。




せっかちな人向けの手順

デスクトップ ウィンドウ マネージャー(dwm.exe)のメモリリーク対策として、Surface Book 2のIntel® Graphics – Windows* DCH Driversを更新する手順を示す。
自分のところは「Surface Book 2 15"(8th Gen Intel® Core™ i7-8650U クアッドコア プロセッサ, 4.2GHz Max Turbo)」なので、ドライバー名等は自身の環境にあわせて適宜読み替えのこと

  1. デバイスマネージャーを起動して、
    ディスプレイ アダプター>Intel(R) UHD Graphics 620のプロパティ>ドライバー
    を確認。
    これのバージョンが「30.0.101.1191」以降(例:「30.0.101.1339」)に既になっている場合、対策済みのはず
  2. 上記よりも前のバージョンになっている場合(自分のところでは「27.20.100.8682」「26.20.100.8141」が存在)、同じくデバイスマネージャーから、
    ディスプレイアダプター>Intel(R) UHD Graphics 620
    の「デバイスのアンインストール」(「☑ このデバイスのドライバーソフトェアを削除します」にチェックの上で)を実施(必要に応じて再起動)し、
    ディスプレイアダプター>Intel(R) UHD Graphics 620
    が存在しなくなるまで繰り返す
    自分のところだとディスプレイアダプター下には「NVDIC GeForce GTX 1060」のみが残った
  3. MicrosoftのSurface 用のドライバーとファームウェアをダウンロードするページより、Surface のドライバーとファームウェアの手動更新から「Surface デバイス モデルを選択する」で自分のモデルにあったリンクに遷移した上で(Surface Book 2の場合はこちら)最新のドライバーとファームウェアをダウンロードの上、実行してインストールを実施
    Surface Book 2の場合、ダウンロードされたのは「SurfaceBook2_Win10_19041_22.080.2839.0.msi」だった(2022/10/11現在)
  4. 再起動後、Intel(R) UHD Graphics 620のドライバーのバージョンが「30.0.101.1339」(もしくは「30.0.101.1191」以降)になっていることを確認

不具合(dwm.exeのメモリリーク)の原因

www.intel.co.jp
に書かれている通り、デスクトップ ウィンドウ マネージャー(dwm.exe)がいつの間にか理不尽なほどにメモリを占有してしまう不具合は、Intel製ドライバーに起因するメモリリークが原因だった模様。
対象となるドライバー(Intel® Graphics – Windows* DCH Drivers)は、

  • 「27.20.100.8587」以降で不具合発生
  • 「30.0.101.1191」以降であれば対策済み

とのこと。

試行錯誤

とくになにもせずとも通常のWindows Updateのみでいつのまにか「30.0.101.1339」に更新された……というケースもあるようなので(Surfaceかどうかは不明)、あくまで参考まで……。
自分の場合、過去にも試行錯誤していたため、その際に余分なことをしてしまった可能性も大いにある

ドライバーのバージョンを確認してみる

自分の環境で確認してみると、「Intel(R) UHD Graphics 620」のドライバーは

  • 日付:2020/09/06
  • バージョン: 27.20.100.8682

となっており、見事に不具合発生するバージョンであった。
これは……なんとかしてバージョンアップをしなければ……!
なお、試しにデバイスマネージャーの「ドライバーの更新(P)」を行ってみたところ……バージョン「26.20.100.8141」(日付: 2020/04/11)になってしまった……待って……なんで下がるの……(その後、「ドライバーを元に戻す(R)」でとりあえず「27.20.100.8682」に戻しておいた)

Intel公式ページよりダウンロードしたドライバーはインストール不可

最初は単純に「Intel公式ページから最新のをダウンロードしてインストールすればいいんでしょ?」と思ったが、実際にやってみると「お使いのシステムには製造元の仕様にロックされたドライバーがあります」なる無慈悲なメッセージが表示されてしまい、インストールできない。
なお、そのあたりを配慮して更新してくれそうな「Automatic Driver and Software Updates」なるものもあるが、自分の環境だとインストールはできたものの、その後のダウンロードができない(インジケータが20数パーセントくらいまで進むと0になるを繰り返す→そのうちダウンロードがキャンセルされる)現象が発生し、うまくいかなかった

ドライバー/ファームウェアの最新版を入れてみたが……

Surface Book 2 更新履歴を見てみると、「2022 年 6 月の更新プログラム(6 月 17 日リリース)」のところに、

Windows Update の名前 デバイス マネージャー
Surface - システム – 6.134.139.0 Surface Integration Service Device – システム デバイス
Intel Corporation - ディスプレイ – 30.0.101.1339 Intel(R) UHD Graphics 620 (15") – ディスプレイ アダプター
Intel Corporation - ディスプレイ – 30.0.101.1339 Intel(R) HD Graphics 620 (13") – ディスプレイ アダプター
Surface Book 2 更新履歴 - Microsoft サポート

という記述が……! これこそ自分が求めていたものではあるまいかっ!

喜び勇んでダウンロードセンターから最新版(SurfaceBook2_Win10_19041_22.080.2839.0.msi(Date Published:8/26/2022))をダウンロードし、インストール&再起動したところ……ドライバーのバージョンは「27.20.100.8682」のままだった……いや、なんでよ……?

ドライバーはアンインストールした上でのクリーンインストールが必要だった……!

なんどか入れ直したり再起動したりを繰り返すも、状況は変わらず。
さすがに諦めかけた頃に、もう一度このページを見直してみたところ、解決方法のところにしっかりと

dwm.exe の漏洩の修正プログラムを含むドライバー 30.0.101.1191 をインストールする前に、 ドライバーのクリーン・インストール を実行する必要があります。

dwm.exe (デスクトップ PC ウィンドウマネージャー) は、27.20.100.8587...

と書かれていることに気づく。

ダメ元だと、

  1. 念のため、インストールしてあったSurface Book 2 のドライバー/ファームウェア(Surface Book 2 Update 22.080.2839.0 (64 bit))をアンインストール
  2. デバイスマネージャーから、
    ディスプレイアダプター>Intel(R) UHD Graphics 620
    の「デバイスのアンインストール」を、
    ☑ このデバイスのドライバーソフトェアを削除します
    にチェックした状態で実施(必要に応じて再起動)
    ディスプレイアダプター>Intel(R) UHD Graphics 620の項目がなくなる(自分の場合、「NVDIC GeForce GTX 1060」のみが残る)まで繰り返し(「27.20.100.8682」「26.20.100.8141」の2つの削除が必要だった)
  3. Surface Book 2のドライバー/ファームウェア(SurfaceBook2_Win10_19041_22.080.2839.0.msi)を再インストール

とやってみると……なんとっ!

Intel(R) UHD Graphics 620のドライバーのバージョンを確認

見事に、バージョン: 30.0.101.1339(日付: 2022/01/22)のドライバーへと更新されていた……!

Windows Helloが使えなくなっちゃった!

無事更新されたと喜んだものの、なぜかWindows Helloによる顔認証が効かなくなってしまった……。
ログイン画面で「カメラをオンにできませんでした。PINを使ってサインインしてください。」とか言われる

自分の場合には幸いにして、
設定>アカウント>サインインオプション>Windows Hello 顔認証
より[認識精度を高める]を再度実施することによって、使えるようになった模様。
うまくいかない場合、こちらの記事などを参照のこと



とりあえず、この状態で様子見中。

この記事を書くきっかけ

2022/10/11に発生し、Windowsの動作が重くなったことで気がついたdwm.exeのメモリリークはこんな感じ。

デスクトップウィンドウマネージャーのメモリリーク

2GB弱なので、まだまだ序の口という印象だが……。
過去にもいろいろと調べてみたものの抜本的な対策が見つからずにあきらめていたのだが、なんとか対応する方法はないものか……と再度調べてみると、Surface Book 2 更新履歴に対策されたらしき履歴があったので、試してみた次第。

過去のツイート等

Chrome拡張機能Manifest V3対応の勘どころ(?)

Chrome ウェブストアに登録している4つの拙作Chrome拡張機能Manifest V3への対応が先日ようやく完了しました

Manifest V3対応のために当方が実際に行った作業の概要やハマった点などを備忘として残しておきます。
詳細でなかったり整理できていない点に関してはご容赦ください



何はともあれ、公式ページを見ておくべし

developer.chrome.com
手探りでいろいろと検索して調べたりしましたが、結局は公式ページに書いてあることでした……というパターンも多々あったため、ひととおり目を通しておくことをおすすめします。
といいながら、英語が苦手な自分は見てない所も多いんですけどね……(汗)

Firefoxとコードを共通化したいんだけど?

現在(2022/10/1)のところ、Firefox(Web Extensions)ではManifest V3には(一般向けとしては)未対応です(2022年末に向けてサポート予定)。

でも製作者としては、manifest.jsonは切り替えるにしても、JavaScriptのコードはなるべく共通化したいという場合もあるかと思います。
その場合、

const MANIFEST_VERSION = chrome.runtime.getManifest().manifest_version;

のようにすれば、manifest.jsonで定義した"manifest_version"の値を取得できるので、コード内であらかじめManifestのバージョンを取得しておき、随時場合分けすることで対応することができるかと思います。

backgroundはService Workerに変更

Manifest V3では、backgroundページはService Workerに置き換わっています
V2ではmanifest.jsonに

    "background" : {
        "scripts" : [ "js/background.js" ],
        "persistent": true
    }

みたいに記述していたのが、V3では

    "background" : {
        "service_worker" : "js/background.js"
    }

のようになります。
注意点として、V2では配列で複数のスクリプトを指定できていたのが、V3では指定可能なスクリプトは1つだけです。
なのでV3で複数のスクリプトを指定したい場合には、メインとして指定したスクリプトからimportScripts()("background"で"type": "module"オプション(ES Module)を設定した場合にはimport)を使ってその他のスクリプト(ライブラリ)を読み込む必要があります。
"persistent"オプションは無くなったようです(常に"persistent": falseになるイメージか?)。
また、"type": "module"とすると、ES Moduleとして動作するらしいです(試していないです)。

Manifest V3では削除されてしまったAPIに注意

もともとV2時代から非推奨(deprecated)となっていたAPIのいくつかは、V3になると削除されて使えなくなっているので要注意です
古くからある拡張機能だと、chrome.extension.xx といったAPIが残っていたりすることもあると思われます。
自分の場合、chrome.extension.getURL()等が残っていてはまりました

manifest.json上"permissions"の指定方法変更(ホスト指定の分離)

Manifest V2では

"permissions": [
    "storage",
    "webRequest", 
    "webRequestBlocking", 
    "*://*.twitter.com/*", 
    "*://pbs.twimg.com/*",
    "*://video.twimg.com/*",
    "*://*.cdn.vine.co/*"
]

のように、機能の使用権限とリソース(URL)へのアクセス許可とが混在していましたが、V3では

"permissions" : [
        "storage",
        "declarativeNetRequest",
        "declarativeNetRequestFeedback"
],
"host_permissions" : [
    "*://*.twitter.com/*", 
    "*://pbs.twimg.com/*",
    "*://video.twimg.com/*",
    "*://*.cdn.vine.co/*"
]

のように明確に分離されています(permissionsはhost_permissionsに、optional_permissionsはoptional_host_permissionsに、それぞれ対応しているようです)。
なお、declarativeNetRequestのルールが適用されるホストについては、ほとんどの場合はhost_permissionsによる許可の方は不要ですが、一部必要となる場合もあります(リダイレクト時・ヘッダ変更時など)

Action APIについての変更(browser_actionとpage_actionの統合)

Manifest V2にあった"browser_action"と"page_action"はV3では"action"に統一されているようです
manifest.json上では単に"browser_action"/"page_action"→"action"に変更・統合すればよいのですが、JavaScriptのソースコード中でchrome.browserAction/chrome.pageActionとなっている箇所を探して書き換えないといけないので、

chrome.browserAction.setIcon( { path : icon_path } );

( chrome.action || chrome.browserAction ).setIcon( { path : icon_path } );

若干手間ですね。

content_scripts等から拡張機能内のファイルにアクセスしたい場合は?(Web-accessible resourcesの指定方法変更)

Web-accessible resourcesの扱いも(manifest.jsonでの定義方法)も、Manifest V2とV3では異なっています
例えばV2だと

"web_accessible_resources" : [
        "scripts/*.js"
]

のように単にアクセス許可を出したいリソースのパスだけを指定すればよかったものが、V3だと少なくとも

"web_accessible_resources" : [{
        "resources" : [ "scripts/*.js" ],
        "matches" : [ "*://*.twitter.com/*" ]
}]

のように、どのサイトからアクセスが可能かを明示する必要があります。
他にもリソースにアクセスできる拡張機能をIDで指定できるなどの設定が追加されています(詳細は公式ページを参照)。

background内でwindowにアクセスできなくなった!

Manifest V3だと、background(Service Worker)ではDOMやwindowオブジェクトは存在しません(undefined)。

自分の場合、V2ではwindowはグローバルなオブジェクトとしての利用で、オプション画面(options_ui)に提供したい関数を生やす(options_ui側でchrome.extension.getBackgroundPage()によりbackgroundのwindowを取得)という使い方でした。
V3ではそもそもこういう使い方はできないのですが、ソースコードを共通化するための便宜上、V3でのグローバルなオブジェクトであるselfを暫定的に割り当てています。

((window) => {
// V2/V3共通のbackgroundの処理
})(
    (typeof window !== 'undefined' ? window : self)
);

当然ながらこの方法だとたとえばwindowサイズを知りたい等の用途では使えませんので、必要に応じてその都度別のやり方を検討する必要があります。

background内でlocalStorageが使えなくなった!

Manifest V2では、background内でlocalStorageが使えたので、オプション設定の記憶用等に使っていることもあるかと思いますが、これはV3では使えなくなってしまいます。
代わりにchrome.storage APIが使えますが、

  • localStorageは同期的に使えましたが、chrome.storage.localは非同期です
    これが面倒でいつまでも置き換えなかったツケが今回回ってきました…
  • 当然localStorageとの互換性はないので、既存のバージョンで保持していたオプション設定などはリセットされてしまいます
    丁寧にやるならlocalStorage→chrome.storage.localにデータを移行する処理を盛り込めばいいのですが……正直面倒なので拙作では手を抜きました……あしからず

ちなみに以前からそもそもbackground等でlocalStorageの使用は推奨されておらず、chrome.storageを使うようにという注意書きがどこかにあったかと思います
なお、chrome.storage APIを使うためには、manifest.jsonの"permissions"に"storage"の追加が必要です。

ちなみに、拙作拡張機能は該当しませんでしたが、background(Service Worker)ではsetTimeout()やsetInterval()等も使えないことにご注意を……代わりにchrome.alarms APIを使います("permissions"に"alarms"の追加が必要です)。
なお、こちらはV2でも"persistent": falseなbackgroundだと使えなかった気がします

オプション画面(options_ui)でbackgroundの関数が呼び出せなくなった!

Manifest V2だと、chrome.extension.getBackgroundPage()によりbackgroundのページ(windowオブジェクト)を取得することができ、その下の関数を呼び出したりもできたのですが、V3でbackgroundがService Workerに変わったことによりこれができなくなりました(chrome.extension.getBackgroundPage()でundifinedが返される)。
options_uiとbackgroundとの間でやり取りを行いたい場合には、

  • 機能を呼び出したい場合、chrome.runtime.sendMessageを使う
  • データを共有したい場合、chrome.storage APIを使う

のように、content_scriptsの場合と同じような仕組みに書き換える必要があると思われます(他によい方法があれば教えてください)。

HTTPヘッダの書き換えなどはどうなるの?(webRequestBlockingからdeclarativeNetRequestへ)

Manifest V3では、"webRequestBlocking"(ブロッキング機能付きのWeb Request API)の機能が廃止され、"declarativeNetRequest"(宣言的なNet Request API)へと置き換えられています
なお、"webRequest"の方はV3でも存在しており、例えばネットワークトラフィックをモニタする等の用途でならば使用できます

ざっくりというと、今まではbackground(JavaScript)内でHTTP Request HeadersやResponse Headersを任意の条件で動的に書き換えたりできていたものが(chrome.webRequest.onBeforeSendHeaders.addListener/chrome.webRequest.onHeadersReceived.addListener)、予め宣言された定義に基づいての書き換えとなる、ということですかね。

定義方法には静的なものと動的なものがあります。
詳細は公式ページを参照してください

NetRequestの静的な定義方法

manifest.json内で、"permissions"に"declarativeNetRequest"を追加の上で、

"declarative_net_request": {
    "rule_resources": [
        {
            "id": "ruleset",
            "enabled": true,
            "path": "ruleset.json"
        }
    ]
}

のように、ルールを記述したJSON(例ではruleset.json)のパスを指定しておき、そのJSON内でルールを定義する方法です。
ルールの定義例はこちら

NetRequestの動的な定義方法

manifest.json内では、"permissions"に"declarativeNetRequest"を追加するだけにとどめ、background(Service Worker)のソースコード内にて、ルールを定義したオブジェクトをchrome.declarativeNetRequest.updateDynamicRules()にて追加/削除する方法です。
具体例はこちら
なお、動的に追加された既存のルール(拡張機能の更新前に登録されたものも含む)に同じidのものが存在したりすると登録に失敗するようなので、追加したいルールと同じidをremoveRuleIds で指定しておくのが無難なようです(よりよい方法があれば教えてください)

Service Workerのエラーが確認できない!?

Manifest V3にしたら(backgroundがService Worker)、拡張機能のページ(chrome://extensions)で[エラー]となっているのに、これをクリックしても正しく表示されないし、「Service Worker (無効)」を開こうとしても開けない(ついでにいえば単に「(無効)」と出ているだけなので、未使用時にサスペンドされているだけなのか、エラーにより起動されないのか区別がつかない)という現象がたまに発生します。

どうも、Service Workerの起動(初期化)時にエラーが発生した場合などでこうなってしまうようです。
もっとも、「Service worker registration failed. 」のようにきちんとエラーが表示されることもあるので、いまひとつ発生条件がよくわかりませんが……

これを回避してエラー内容を確認するためには、

といった対処方法があるようです。

自分はとりあえず後者でしのいでいます。
[manifest.json]

"background" : {
    "service_worker" : "background-wrapper.js"
}

[background-wrapper.js]

try {
    importScripts('scripts/background.js');
}
catch (error) {
    console.log(error);
}

「Wrapperはmanifest.jsonと同じ階層に置く必要がある」とのことですが、それ以外の階層においたときにどうなるのかは未検証です(例えば"scripts/background-wrapper.js"のように違う階層においても、importScripts()するときのパスにさえ気をつければ通常の動作では問題無さそうではあるんですが)

onRuleMatchedDebugはデベロッパーモードによる開発時にのみ有効(Chrome ウェブストアからインストールすると動かない件)

Twitter メディアダウンローダのManifest V3対応版Chrome ウェブストア等にアップロードしたところ、なぜかそこからインストール(更新)した拡張機能が動作しない、という罠にはまりました

原因は、デバッグ用に設定していたchrome.declarativeNetRequest.onRuleMatchedDebug.addListener()が、実はローカルで[パッケージ化されていない拡張機能を読み込む]により読み込んだ拡張機能でしか機能しない(Chrome ウェブストアからインストールした場合にはonRuleMatchedDebugはundefinedになってしまう)というものでした。
その時点でエラーになることでService Workerの実行が中断されて拡張機能が動作しない&wrapper設定前だったのでエラーの確認もできないという状態

これ、たしかに公式ページにも

Fired when a rule is matched with a request. Only available for unpacked extensions with the declarativeNetRequestFeedback permission as this is intended to be used for debugging purposes only.

https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#event-onRuleMatchedDebug

としっかり書かれているので、確認しなかった自分が悪いんですよ、ね……。

実運用としては、

のがよいかと思われます。

manifest.json上の"permissions"と、インストール時に確認される権限の関係について

Manifest V3に対応した拡張機能だと、更新時に無効化されて、再度有効化しようとすると追加で権限の確認をされることもあるかと思います。
今回自分が遭遇したパターンだと、

permissions(manifest.json) 権限(拡張機能) Permissions(Extension)
declarativeNetRequest 任意のページのコンテンツをブロック Block content on any page
declarativeNetRequestFeedback 閲覧履歴の読み取り Read your browsing history

が該当しました。
これ、V2のときにwebRequest/webRequestBlockingで実現していたのと同じようなことをやっているだけであっても、V2のときだと拡張機能のインストール時や更新時には権限として特になにも表示されなかったのに、declarativeNetRequest*にした途端、表示(権限の確認)がされるようになってしまいます。

まぁ、それだけセキュリティ的に厳しくなったということなんですが……正直、

  • 最初のインストール時→ほとんどの人が警告の中身を見ないでOKしてるんじゃないの?
  • 更新時→追加で権限を求める警告がでるので、そこで初めて気づくユーザーも多いと思うんだけど……これ、いたずらに不安を煽るだけになってない?
    更新したことで表示された=悪意がある機能が追加された、と思われて開発者に対する悪印象も植えつけられてしまう可能性

とも思えてしまい、どうにもモニョる……というのはここだけの話です……。
かといってより適切な注意喚起の仕方を思いつくわけでもないのが辛いところ……

その他(覚書)

拙作拡張機能では未対応もしくは対応する必要がなかったポイントについて覚書程度にメモしておきます。

Service Workerは「無効」時には破棄されてしまう

しばらく動いていないService Workerは「無効」化状態になります(破棄されます)。このとき、グローバル変数等も初期化されてしまうために、消えてほしくないデータに関してはchrome.storage APIを使うなりして保持しておく必要がある模様です。
拙作拡張機能は現時点(2022年10月初旬)で一部未対応(オプション変更時に把握可能なTwitterのページなどをリロードさせる処理など)

CSP(Content security policy)についても変更がある

CSP(Content security policy)のmanifest.jsonでの指定方法等も変更になっており、またsandbox用のオプションが追加されているようです
幸いにして(?)拙作拡張機能ではCSPを指定したものは無かったので、深く調べること無くスルーしてしまっています💦

リモートコードの制限

Manifest V2では可能であった、リモートでホストされている(拡張機能のパッケージに含まれていない)ソースコードを読み込んでの実行はできなくなっています
これはTampermonkey等のユーザースクリプトマネージャーにとっては致命的な制限となりえます。
どうやらGoogle側も対応予定ではあるようですが、2022年10月初旬時点ではどうなるのか不透明です。
単に自分が認識できていないだけですでに対応されているのかも知れませんが……

任意コード実行の制限

Manifest V2では可能であった、任意のコード文字列の埋め込み(実行)も、Manifest V3では制限が厳しくなっています
具体的にはmanifest.jsonの"permissions"に"scripting" 権限を追加した上で、chrome.scripting APIの仕様に従って、staticなファイルや任意のfunctionの挿入を行う必要があるようです。

「LOVE iLLUSiON #502 Ver.」歌詞 (「冴えない彼女の育てかた Fes. Fine ~glory moment~」特典CDより)

LOVE iLLUSiON #502 Ver.

  • 作詞: 稲葉エミ
  • 作曲: 奥井康介
  • 歌: 加藤恵(安野希世乃)/澤村・スペンサー・英梨々(大西沙織)/霞ヶ丘詩羽(茅野愛衣)/氷堂美智留(矢作紗友里)/波島出海(赤﨑千夏)
歌詞 英梨々 詩羽 美智留 出海
カシマシ 神絵師 自画自賛も才能 🌸
(Come on baby! "Illustrator") 🌸
度肝抜く 起承転結 本望だわ 🌸
(Gyu Gyu Bang! Gyu! "Novelist") 🌸
振り返ると なんだかなぁだよね 🌸
(Come on baby! "Main Heroin") 🌸
ともあれ それぞれ 笑えたら成功! 🌸 🌸 🌸 🌸 🌸
(Take a chance! 3, 2, 1, Let's go!) 🌸 🌸 🌸 🌸 🌸
凄腕の社長? 🌸
はたまた 命知らず? 🌸
いいえ ただのオタクだわ 🌸
うっかりここまできたね 🌸
人生ってわからないね 🌸
結局 もう とことんやるだけ 🌸 🌸 🌸 🌸 🌸
(Go Go Let's go! せーの!!) 🌸 🌸 🌸 🌸 🌸
妄想癖(クリエイティブ)にゴールはない 🌸 🌸 🌸 🌸 🌸
新たな勘違いの幕開け 🌸 🌸 🌸 🌸 🌸
おんなじとこクルクルリ 🌸 🌸 🌸 🌸 🌸
遠回りしても全員集合 🌸 🌸 🌸 🌸 🌸
なんだろこの感じ 🌸
愛かな? 🌸
ヘンな集まりかな? 🌸
夢のツヅキを見よう 🌸 🌸 🌸 🌸 🌸
みんなそう自称天才 🌸 🌸 🌸 🌸 🌸
あっぱれ☆LOVE iLLUSiON 🌸 🌸 🌸 🌸 🌸
画力と魅力を磨きますキュッキュッキュッ 🌸
(Oh my Darling! "Illustrator") 🌸
聴いてよ新曲 ギター背負(しょ)って暴走 🌸
(Ja Ja Jan! Ja Ja Ja! "Singer") 🌸
いつか拝みたい私たちルート 🌸 🌸
(Oh my Darling! Don't let me down!) 🌸 🌸
みんなで歌えばコワくないキャラソン 🌸 🌸 🌸 🌸 🌸
(Take a chance! 3, 2, 1, Let's go!) 🌸 🌸 🌸 🌸 🌸
好きをつらぬくと 🌸
決めたあの日の未来 🌸
なぜ? オタクの代弁者 🌸
忙殺の現場じゃ所詮 🌸
「かく」か「かかされる」か 🌸
やめて! 完徹のドーパミン 🌸 🌸
(Go Go Let's go! せーの!!) 🌸 🌸 🌸 🌸 🌸
二次元(ファンタズム)をこじらせて 🌸 🌸 🌸 🌸 🌸
ひょっとして型破りの距離感 🌸 🌸 🌸 🌸 🌸
煩悩だらけチラチラリ 🌸 🌸 🌸 🌸 🌸
呼んでもないのに全員集合 🌸 🌸 🌸 🌸 🌸
言っちゃおー アイシテルヨ 🌸
ギャグか バグか はったりかな? 🌸
一緒に悩んで進もう 🌸 🌸 🌸 🌸 🌸
毎日が最高傑作 🌸 🌸 🌸 🌸 🌸
どんだけ☆LOVE iLLUSiON 🌸 🌸 🌸 🌸 🌸
出会った日から 🌸 🌸
今日まで過ごした 🌸 🌸
言葉にできない 🌸 🌸
想い集めて 🌸 🌸
モノヅクリをしよう 🌸 🌸 🌸 🌸 🌸
安心してね ここにいる 🌸
想像を超えた絵を描きまくる 🌸
官能寄りの感動でfix 🌸
アイディア持ち寄り 🌸 🌸
全員集合 🌸 🌸 🌸 🌸 🌸
妄想癖(クリエイティブ)にゴールはない 🌸 🌸 🌸 🌸 🌸
新たな勘違いの幕開け 🌸 🌸 🌸 🌸 🌸
挑戦状をギラギラリ 🌸 🌸 🌸 🌸 🌸
握りしめたら全員集合 🌸 🌸 🌸 🌸 🌸
なんだろこの感じ 🌸
愛かな? 🌸
ヘンな集まりかな? 🌸
夢のツヅキを見よう 🌸 🌸 🌸 🌸 🌸
みんなそう自称天才 🌸 🌸 🌸 🌸 🌸
あっぱれ☆LOVE iLLUSiON 🌸 🌸 🌸 🌸 🌸
合格☆ 🌸
LOVE iLLUSiON 🌸 🌸 🌸 🌸 🌸

「冴えない彼女の育てかた Fes. Fine ~glory moment~」特典CD 収録

備考

歌詞を確認したくて「LOVE iLLUSiON #502 Ver.」で検索してみたら出てこなかったので、たまたま以前書き起こしていたものを貼り付けただけです。
なお、同じ特典CD内には「LOVE iLLUSION (Michiru & Izumi Ver)」も収録されているんですが、そちらは書き起こしていません……あしからず。