Skip to content

SeleniumBasic を止めた GetTcpTable は、なぜ令和のIEを止めなかったのか ​

GetTcpTable 秘話

〜他人のツールの起動エラーを掘っていたら、自分のコードの「なぜ」が一つ増えた話〜

最近、SeleniumBasic の起動エラーを調べていました。このツールとは直接関係のない話です。

もともとは「SeleniumBasic、Windows Update で大変そうだな🙃」くらいで眺めていました。ところが掘っていくと、GetTcpTable という Windows の関数を経由して、いつの間にか自分の WebSocket 接続(CDPCoreViaWebSocket.cls)の設計を振り返ることになりました。

このページでは、前半で SeleniumBasic の原因調査を、後半でこのツールが巻き添えにならずに済んだ理由を書きます。


SeleniumBasic が突然動かない ​

Windows 11 26H2 の PC で SeleniumBasic 2.0.9.0 から WebDriver を起動すると、次のエラーで止まります。

text
TimeoutError
The driver failed to open the listening port
127.0.0.1:<port> within 10s

既存の報告では、SeleniumBasic の DriverService.Start() から呼ばれる EndPointExt.WaitForListening() が、GetTcpTable で「指定のポートが待ち受け中か」を確かめていると指摘されています。待ち受け中だと確認できないまま10秒たつと、このエラーになります。

ところが、WebDriver(今回調べたのは msedgedriver 156.0.4314.58)は、実際にはちゃんと起動していました。http://127.0.0.1:9515/status に HTTP でアクセスすると、HTTP 200 と "ready": true(新しいセッションを受け付けられる)が返ってきます。

text
WebDriver           起動している
HTTP /status        200 が返る
        ↓
なのに……
        ↓
GetTcpTable         LISTEN と判定できない
        ↓
SeleniumBasic       「Driver が起動していない!」
        ↓
TimeoutError

WebDriver の起動に失敗したのではなく、「起動したかどうかの判定」のほうが失敗していたわけです。

GetTcpTable のおさらい ​

ここで、出てくる言葉を簡単に整理しておきます。

プログラム同士がネットワークで話すときは、「ポート」という番号付きの窓口を使います。窓口を開けて相手からの接続を待っている状態を「待ち受け中(LISTEN)」と呼びます。

GetTcpTable は、iphlpapi.dll に入っている関数です。Windows に「今ある TCP の窓口と接続を一覧でちょうだい」と頼むと、1行ごとに自分側の住所とポート、相手側の住所とポート、状態の番号が返ってきます。似た関数の GetTcpTable2 と GetExtendedTcpTable や、コマンドの netstat、PowerShell の Get-NetTCPConnection も、同じ種類の情報を表示します。

番号名前意味
0なし正しい状態の番号には存在しない値
1CLOSED閉じている
2LISTEN待ち受け中
5ESTABLISHED接続中
6FIN_WAIT1切断の途中
11TIME_WAIT切断した後の待機中

待ち受け中の窓口は、相手側が 0.0.0.0:0(相手なし)で、状態が 2 になるのが正しい姿です。以下、この形の行を「待ち受け行」と呼びます。

一覧の「状態」がおかしい ​

VBA から GetTcpTable を直接呼び出して、9515 番の待ち受け行を観測してみました。すると、同じ待ち受け行の状態が 0、1、2、6 と次々に変わりました。

おかしいのは待ち受け行だけではありません。HTTP をかけた側の行、つまり 127.0.0.1:57118 → 127.0.0.1:9515 のように相手が決まっている接続の行にも、LISTEN と出たものがありました。相手がいる接続が「待ち受け中」というのは、ありえない組み合わせです。

Microsoft Q&A にも、Windows 11 26H2 Build 26300.9818 と KB5127936 の組み合わせで、似た現象が報告されています。手元の PC も、ビルドは 26300.9818 で、KB5127936 は2026年10月29日にインストールされていました。報告とまったく同じ組み合わせです。

調べた時点では、Microsoft による原因の説明、既知の問題としての認定、修正の予定は確認できていません。

ドライバのポートとブラウザのポートで違うのか ​

ここで気になったのが、ポートを開けるプログラムによって挙動が変わるのか、という点です。比べたのは次の2つです。

  • msedgedriver.exe が開くポート

  • ブラウザ(Edge)が --remote-debugging-port で自分で開くポート。このツールの WebSocket 経路が使うのはこちらです

xlflowを活用した使い捨ての Excel ブックに計測用の VBA を書き、起動した瞬間から10秒間、SeleniumBasic と同じように LISTEN を探しました。その後も、HTTP をかけずに観測、HTTP を1回かけてから観測、HTTP と観測を交互に、と続けています。HTTP のアクセス先は、msedgedriver では /status、Edge では /json/version です。

起動したものポート起動後10秒以内に LISTEN を確認できたか
msedgedriver9333203ミリ秒 / 確認できず
msedgedriver9515確認できず / 140ミリ秒 / 2953ミリ秒
Edge9333469ミリ秒 / 781ミリ秒
Edge95151532ミリ秒 / 312ミリ秒
Edge0(自動。実際は 57329、59734、63923)3回とも確認できず(ずっと 0)
msedgedriver60123、612342回とも確認できず(ずっと 0)

HTTP のアクセスは、すべての回で 200 が返りました。

最初は「ブラウザが自動で選んだポートだと、ずっと 0 になる」ように見えました。そこで、msedgedriver を高い番号のポートで、Edge を 9515 番で起動して入れ替えてみました。すると、結果もそのまま入れ替わりました。違いを生んでいたのは、プログラムの種類ではありませんでした。

決め手は「何行目に並ぶか」 ​

自分のポートだけの話ではなかった ​

一覧全体を見ると、135 番、445 番、49664 番など、Windows が最初から開いている待ち受け行もおかしくなっていました。観測のたびに一覧全体の待ち受け行を数えると、延べ18,350件のうち 16,754件(約91%)が LISTEN 以外の値でした。

3つの関数(GetTcpTable、GetTcpTable2、GetExtendedTcpTable)は、毎回まったく同じ値を返しました。netstat と Get-NetTCPConnection も、同じ間違った状態を表示します。状態が 0 の行は netstat に表示されないので、待ち受けが「消えた」ように見えます。

Excel から一覧を取り、すぐに別のプロセスで netstat を実行し、もう一度 Excel から取っても、3つの結果は全行で一致しました。呼び出したプログラムによって値が変わるわけではなく、Windows の内部で一覧を作る段階で、すでに値がずれていると考えられます。

ちなみに IPv6 の一覧では、接続の行の状態は正しそうでした。おかしかったのは待ち受け行だけです。IPv4 では、接続の行もおかしくなっていました。

ポート番号を変えてみる ​

それでも「高い番号のポートが怪しい」と思い、msedgedriver を 13000〜55000 の14個のポートで、それぞれ3回ずつ起動しました。

結果は、40000 番や 43000 番でも LISTEN が出て、13000 番でもほとんどの回が 0、という、ポート番号では線を引けないものでした。しかも、1〜2回目はほぼ全部 0、3回目は多くのポートで値が出ていて、その時々の一覧の中身で結果が変わっていました。

並べ替える前の一覧を見る ​

GetTcpTable には、一覧を並べ替えて返すかどうかを選ぶ引数があります。SeleniumBasic を含め、普通は並べ替えた結果を見ます。そこで、Windows から返ってきたままの順番で見てみました。この順番は、ほぼ自分側のポート番号の小さい順でした。

待ち受け行の状態を、この順に並べるとこうなります。

text
行番号 : 0 1 2 | 3 4 5 | 6 7 8 | 9 10 11 | 12 13 14 | 15 | 16〜
状態   : 1 0 0 | 1 0 0 | 1 6 2 | 1  0  0 |  1  6  2 |  0 | すべて 0
  • 先頭の16行(行番号 0〜15)には、3行ごとにくり返す規則的な値が入っていました。時間をおいた別の回では組み合わせが変わりましたが、3行ごとのくり返しは同じでした

  • 行番号 16 以降の待ち受け行は、すべて 0 でした

正しい LISTEN(2)は、たまたま「2」が入る位置に並んだときにしか出ません。

ダミーで押し出してみる ​

位置で決まるのかを確かめるため、10001〜10003 番に msedgedriver をもう3つ起動しました。こうすると、それより大きい番号の待ち受け行は、すべて3行ずつ後ろにずれます。

すると、直前まで行番号 13〜14 にあって値が入っていた 12993 番と 12995 番が、行番号 16〜17 に押し出された途端に 0 になりました。13000 番も行番号 18 に押し出され、3回とも 0 でした。

ポート番号は同じままで、並ぶ位置だけを変えたら値が変わりました。値を決めているのは、ポート番号ではなく何行目に並ぶかです。

手元の PC には、Windows や常駐アプリの待ち受けが16個ほどあります。それより大きい番号のポートは、ほぼ確実に17行目以降に入り、ずっと 0 になります。

末尾の 445 番と 7680 番 ​

ひとつだけ、この説明に合わない行がありました。並べ替える前の一覧の一番最後には、ポート番号の並びがいったん途切れて、445 番と 7680 番の2行だけが付いています。この2行には、17行目以降なのに 0 以外の値が入る回がありました。

調べてみると、この2行は IPv4 と IPv6 を1つのソケットで待ち受ける「デュアルスタック」の待ち受けでした。VBA から自分で待ち受けを作って確かめた結果は、次のとおりです。

作った待ち受けIPv4 の一覧での位置
IPv4 だけ先頭のまとまりの、ポート番号順の位置
デュアルスタック(IPv6 のソケットで IPV6_V6ONLY を 0)末尾のまとまりの、445 番と 7680 番の後ろ
IPv6 だけIPv4 の一覧には出ない

末尾のまとまりは、IPv6 側の待ち受けを IPv4 の一覧にも載せた分だったわけです。

値の入り方も、先頭のまとまりの続きでした。先頭の16行と末尾の2行をつなげると、3行ごとのくり返しがそのまま続きます。

text
先頭のまとまり(16行): 0 2 0 | 0 2 0 | 0 1 6 | 0 2 0 | 0 1 6 | 0
末尾のまとまり        :                                        1 6 | 0(自分で足したデュアルスタック)

自分で足したデュアルスタックの待ち受けは、末尾のまとまりの3行目に並び、低い番号(20001 番)でも高い番号(50001 番)でも、3回とも 0 でした。手元の PC では、2つのまとまりを通して先頭の18行にだけ値が入り、それより後ろは 0 になる、と読むと説明がつきます。

なぜこうなるのか(推測) ​

ここからは観測にもとづく推測で、Windows の中身までは確かめていません。

3行ごとにきれいにくり返す値は、データを読むときの1件分の幅や読み始める位置がずれていると、よく出る形です。また、値が入るのが2つのまとまりを通して先頭の18行だけであることからは、決まった件数の分しか状態が書き込まれておらず、残りは 0 のまま、といった可能性が考えられます。呼び出すプログラムに関係なく同じ値になることから、iphlpapi.dll よりも下(NSI や TCP/IP スタック側)が怪しいと考えています。


ふと、自分のツールが気になった ​

SeleniumBasic の件を調べていて思い出したのが、このツールの WebSocket 接続でした。

このツールの WebSocket 経路は、ブラウザを --remote-debugging-port=0 で起動します(CDPHost.cls の GenerateArgRemoteDebugging)。0 を渡すと、ブラウザが空いているポートを自分で選びます。計測で選ばれたのは 57329、59734、63923 で、どれも5〜6万台でした。

これは、まさに「17行目以降に並んで、ずっと 0」になる帯です。実際、上の表の Edge(ポート 0)の行は、3回とも10秒以内に LISTEN を一度も確認できませんでした。

もしこのツールが、接続する前に GetTcpTable で LISTEN を確かめる作りになっていたら、この PC では毎回「ブラウザが起動していない」と判断していたはずです。

令和のIEは、ポートを確かめていなかった ​

WebSocket 経路の接続は、CDPCoreViaWebSocket.cls の OpenBrowserAndConnectCDP と ConnectCDP で、次の順に進みます。詳しい流れは、技術資料の WebSocket 経路にまとめています。

  1. ブラウザを --remote-debugging-port=0 で起動する
  2. ブラウザが書き出す DevToolsActivePort ファイルが現れるまで待つ(0.1秒おきに最大100回)
  3. ファイルから、ポート番号と接続先のパスを読む
  4. socket() でソケットを作り、connect() で TCP 接続する
  5. WebSocket のハンドシェイクを送る
  6. 応答に HTTP/1.1 101 が含まれているか確かめる

ConnectCDP の中身を抜き出すと、次のとおりです(手順4〜5とエラーメッセージの後半は省略しています)。

vb
'3. ソケット生成
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
If m_hSocket = SOCKET_ERROR Then printMsg ERROR_, "Failed to create WinSock handle. ...", FromProcedureName, doRaiseError:=True

'6. 接続実行 (TCPコネクションの確立)
ret = Connect(m_hSocket, addr, LenB(addr))
If ret = SOCKET_ERROR Then printMsg ERROR_, "Failed to connect to `Chrome DevTools Protocol`. ...", FromProcedureName, doRaiseError:=True

'7. WebSocketハンドシェイクの実行
If Not SendHandshake(Host, port, Path) Then printMsg ERROR_, "The WebSocket-`Chrome DevTools Protocol` handshake failed. ...", FromProcedureName, doRaiseError:=True

'8. WebSocketハンドシェイクの実行結果確認
If Not ReadHandshakeResponse Then printMsg ERROR_, "The results of the WebSocket-`Chrome DevTools Protocol` handshake could not be confirmed.", FromProcedureName, doRaiseError:=True

「そのポートが LISTEN か」という事前確認は、どこにもしていません。src の中にも、GetTcpTable や iphlpapi.dll を使っている箇所はありません。

最初は単に、

そこまで考えてなかったから🙃

でした。

計測でも、ブラウザが選んだポートは DevToolsActivePort ファイルに0.4秒ほどで書き出され、そのポートへの HTTP アクセスは10回とも成功しました。その間も、GetTcpTable の状態はずっと 0 のままです。

ツール本体でも接続してみた ​

念のため、このツール本体でも確かめました。本体のブックのコピーに計測用のモジュールを足し、公開 API の CDPContext.OpenBrowserAndConnectTab(Set_UseWebSocket = True)で Edge を起動して接続します。接続した後は、jsEval("1+1") で CDP のコマンドを送りながら、300ミリ秒おきに10回、デバッグポートの待ち受け行を観測しました。

回ブラウザが選んだポート接続までの時間CDP のコマンド待ち受け行の状態並ぶ位置
1521701390ミリ秒10回とも成功10回とも 0行番号 29
2606911203ミリ秒10回とも成功10回とも 0行番号 35
3528231281ミリ秒10回とも成功10回とも 0行番号 29〜30

並ぶ位置は、並べ替える前の一覧での行番号です。3回とも16行より後ろに並び、待ち受け行は一度も LISTEN になりませんでした。Excel とブラウザの間の WebSocket 接続の行も、状態が 0、1、2、5、6 とばらばらで、Excel 側の行が LISTEN(2)と出た回までありました。

それでも、接続とコマンドのやり取りは、すべて成功しています。一覧の状態がどれだけおかしくても、connect() とハンドシェイクで確かめるこのツールには関係ありませんでした。

今回の件を見た後では、むしろこれでよかったのでは? と思えてきました。

事前に確かめると、何が保証されるのか ​

たとえば、丁寧に作ろうとして次のようにしたとします。

text
GetTcpTable で一覧をもらう
   ↓
目当てのポートを探す
   ↓
LISTEN(2)か?
   ↓
connect()

一見すると堅牢そうです。しかし、結局 connect() 自体のエラー処理は必要になります。次のことは普通に起こり得るからです。

text
ポートを確認 → OK
     ↓
その直後に相手が終了
     ↓
connect() → 失敗

逆に今回のように、一覧では LISTEN ではないのに、connect() や HTTP は成功する、ということまで起こり得ます。事前チェックを入れると、本来成功できる処理を、自分から失敗させる可能性まで増えてしまいます。

ポートの確認は数行では終わらない ​

もしポートの確認が If IsPortOpen(Host, port) Then ... のような関数一本で済むなら、入れたくなっていたかもしれません。しかし、GetTcpTable で確かめるには、実際には次の処理が必要です。

text
GetTcpTable を Declare する
       ↓
必要なバッファの大きさを聞く
       ↓
Byte 配列を確保する
       ↓
GetTcpTable をもう一度呼ぶ
       ↓
MIB_TCPTABLE を読み解き、MIB_TCPROW を1行ずつ見る
       ↓
ポート番号の並び(ネットワークバイトオーダー)を直す
       ↓
目当てのポートを探して、状態が LISTEN か確かめる

そこまで作って得られる情報は、「たぶん今 connect() できそうです」です。そして次の行で、本当に connect() します。

……それなら、最初から connect() すればいいわけです🙃

頼る先も増える ​

このツールの WebSocket 接続は、WinSock(ws2_32.dll)の関数だけで完結しています。

text
ws2_32.dll
 ├ socket()
 ├ setsockopt()
 ├ inet_addr()
 ├ connect()
 ├ send()
 └ recv()

一覧で事前確認しようとすると、ここに iphlpapi.dll の GetTcpTable と、その一覧の読み方(MIB_TCPTABLE、MIB_TCPROW、バッファの確保、ポート番号の変換、状態の判定)まで加わります。これは、チェックを1つ足すだけの話ではありません。別の DLL、別の関数、別のデータの形、別の「状態」の考え方に頼ることになり、そのぶん故障する場所も増えます。

今回、まさにその加わるはずだった側で、状態がおかしくなりました。このツールは、WinSock の中で接続を完結させていたため、この問題に巻き込まれずに済みました。

確かめるなら、本番の操作そのもので ​

今回のことを一般化すると、こうなります。

目的の操作そのものを安全に試せるなら、代わりの情報から成功しそうかを推測するのではなく、本番の操作が成功したかを確かめる。

このツールの WebSocket 接続に当てはめると、それぞれの段階を、その段階の操作そのもので確かめていることになります。

確かめたいこと確かめ方
TCP で接続できるかconnect() してみる
WebSocket として話せるかハンドシェイクを送ってみる
相手がハンドシェイクを受け入れたか応答(HTTP/1.1 101)を確かめる
CDP でやり取りできるかCDP のコマンドを送ってみる

以前なら、「ポートの状態を確認 → TCP 接続 → WebSocket の確認 → CDP 接続」と段階を増やしたほうが、丁寧な作りに見えたかもしれません。しかし実際には、それぞれの操作が成功したら次へ進み、失敗したらその場で正しく扱う作りのほうが、余計な状態の観測に頼らずに済みます。

堅牢さとは、事前チェックの数ではなく、必要な操作それぞれの失敗を正しく扱えることなのだと思います。

設計の原則として残すなら、次のようになります。

Connection Validation Principle

接続できるかどうかを、外部の状態を観測する API から推測しない。安全に試せる実際の接続処理が成功したかで判断する。各プロトコルの段階は、その段階自身の操作で確かめ、成功に不要な事前チェックや外部への依存を足さない。

のちほどコミットを行いますが、VBA のコメントとして残すなら、こんな感じです。

vb
' TCP の LISTEN 状態の事前列挙は行わない。
' 接続可否は別 API による推測ではなく、
' WinSock connect() の実際の成否で判定する。
' その後、WebSocket Handshake と応答を
' 各プロトコル層の成功条件として検証する。

SeleniumBasic を使っている方へ ​

SeleniumBasic の起動判定は、手元の PC では運しだいになります。WebDriver のポートが17行目以降に並ぶと必ずタイムアウトし、16行以内でも「2」の位置に当たらなければタイムアウトします。

回避するには、WebDriver を自分で先に起動しておき、SeleniumBasic からはそこへつなぐだけにします。

vb
driver.StartRemotely _
    "http://127.0.0.1:9515", _
    "edge"

こうすると、GetTcpTable を使う起動判定を通りません。自分で起動判定を書く場合も、一覧の状態を見るより、/status に HTTP でアクセスして、200 と "ready": true が返るかを確かめるほうが確実です。これも「本番の操作そのもので確かめる」の一つです。

調べた環境とまだ分かっていないこと ​

項目内容
OSWindows 11 Pro 26H2(ビルド 26300.9818、KB5127936 適用済み)
WebDrivermsedgedriver 156.0.4314.58
調べた側Excel VBA から iphlpapi.dll の関数を直接呼び出し。接続の確認には、このツール本体(WebSocket モード)も使用
調べた日2026年11月4日

まだ分かっていないことは、次のとおりです。

  • 「16行」という区切りが、どの PC でも同じなのか

  • 値が入る行数(手元の PC では2つのまとまりを通して18行)が、何で決まっているのか

  • Windows 10 でも同じことが起きるのか

試行の回数は条件ごとに2〜3回で、統計として扱うには少なめです。


KB5127936 はプレビュー更新だった ​

ここまで調べてから、もう一つ大事なことに気づきました。

関連が疑われている KB5127936 は、毎月の定例のセキュリティ更新ではありません。Windows 11 25H2 / 26H2 向けの、セキュリティ以外の累積更新のプレビューとして、2026年10月28日に公開されたものでした。

Microsoft のサポートページ「2026 年 10 月 28 日 — KB5127936 (OS ビルド 26300.9818 および 26200.9818) プレビュー」でも、タイトルに「プレビュー」と付いています。本文では、セキュリティ以外のプレビュー更新プログラムとして扱われています。手元の PC の Get-HotFix でも、KB5127936 の種別はセキュリティ更新(Security Update)ではなく、ただの更新(Update)でした。

プレビューの更新は、ざっくり言うと次の流れの途中にあります。今回の更新も、この途中にいるのかもしれません。

text
プレビューの更新
    ↓
変更を先に配る
    ↓
問題が見つかるかもしれない
    ↓
直される
    ↓
後に続く正式な累積更新へ

ネットワークまわりの変更(推測) ​

ここからは推測です。

KB5127936 のサポートページを読むと、変更点の中に「Wi-Fi とイーサネットのネットワーク インターフェイスで IPv6 CLAT に対応した」という項目があります。CLAT は、IPv6 だけのネットワークにつながっていても IPv4 のアプリが通信できるように、IPv4 と IPv6 の間で変換する仕組みです。

今回の観測では、IPv4 の一覧は待ち受け行も接続の行もおかしくなっていたのに、IPv6 の一覧の接続の行は正しそうでした。また、デュアルスタックの待ち受けは、IPv4 の一覧の末尾に別のまとまりとして並んでいました。IPv4 と IPv6 の扱いに手が入った更新で、IPv4 の一覧にだけ目立つずれが出ている、という組み合わせは少し気になります。

ただし、CLAT への対応と今回の現象に関係があるかは分かりません。この PC で CLAT が実際に動いているかも確かめていませんし、サポートページの既知の問題にも、TCP の一覧に関する記述はありません。

次の更新で直るかもしれない ​

もちろん、Microsoft から GetTcpTable まわりの正式な原因の説明や、修正の予定が発表されたことは、まだ確認できていません。

それでも、今回の更新がプレビューである以上、こんなオチも十分に考えられます。

text
2026年10月
KB5127936(プレビュー)
        ↓
GetTcpTable のまわりで怪しい挙動
        ↓
SeleniumBasic 民
「なんで動かないんだ!!!」
        ↓
GetTcpTable まで掘る
        ↓
VBA で再現 PoC まで作る
        ↓
設計原則まで考え始める
        ↓
次の Windows Update
        ↓
LISTEN
LISTEN
LISTEN
LISTEN
        ↓
「……直ってる🙃」

それはそれで、とても Windows Update らしい後日談になりそうです。

次の更新の後にもう一度試す ​

今回作った検証コードは、この後日談を確かめるためにも残しておく価値があります。

次の正式な累積更新は、11月の定例のセキュリティ更新です。第2火曜日にあたる2026年11月10日に予定されています。プレビューの変更は、ふつうこの定例の更新で正式に配られます。そこで直っていれば「プレビューの間だけの問題だった」、直っていなければ「正式な更新にもそのまま入った」ことになります。

11月10日以降にこの更新を入れたら、条件をできるだけ変えずに、もう一度試すつもりです。

  1. msedgedriver.exe --port=9515 で起動する
  2. http://127.0.0.1:9515/status にアクセスし、HTTP 200 と "ready": true を確かめる
  3. VBA から GetTcpTable を400ミリ秒おきに観測する
  4. 待ち受け行(127.0.0.1:9515 → 0.0.0.0:0)の状態の値を記録する
  5. HTTP のアクセスをくり返した場合も観測する

比べたいことは単純です。KB5127936 の時点では CLOSED、LISTEN、0、FIN_WAIT1 などがばらばらに出ていた待ち受け行が、後の更新でずっと LISTEN になれば、とても分かりやすい before / after になります。

ただ、9515 番は並ぶ位置しだいで、今でもたまたま LISTEN になることがあります。今回ずっと 0 だった、ブラウザが自分で選ぶ高い番号のポートも合わせて見ると、違いがよりはっきりするはずです。

それでも残る教訓 ​

仮に次の Windows Update で完全に直ったとしても、今回得た設計の学びまでは消えません。

SeleniumBasic が本当に知りたかったのは、「一覧の上で LISTEN か」ではなく、「WebDriver が要求を受け付けられる状態か」でした。WebDriver なら、/status に HTTP でアクセスすれば、HTTP 200 と "ready": true で本人から直接答えが返ってきます。TCP の接続そのものなら、connect() の成功か失敗かが返ってきます。

Windows 側の挙動が後で直ったとしても、「安全に本番の操作を試せるなら、代わりの状態から成功しそうかを推測するより、本番の操作の結果を確かめる」という原則は残ります。

おわりに ​

今回の調査は、次のように妙なところまで広がりました。

text
SeleniumBasic が動かない
        ↓
GetTcpTable が怪しい
        ↓
Windows の TCP の状態の一覧がおかしい?
        ↓
自分の CDP 接続の connect() の設計を見直す
        ↓
「ポートの事前確認、そもそも要らなくない?」
        ↓
Connection Validation Principle の誕生

そして最後に分かったのが、関連が疑われる更新そのものがプレビューだった、ということです。もしかすると数週間後には、

あれだけ掘ったのに、次の Windows Update でしれっと直った🙃

となるのかもしれません。そうなれば、上に書いた「まだ分かっていないこと」も、確かめる前に迷宮入りかもしれません。

それでも、無駄ではありません。バグそのものは消えても、なぜ自分の接続処理では、余計なポートの事前確認を置かないのかという理由は残るからです。

この調査で、GetTcpTable は「知らなかったから使わなかった」関数から、「知ったうえで、この用途には使わない」関数に変わりました。これが一番大きな収穫だった気がします。

他人の10年前のコードを深掘りしたら、自分のコードに「なぜそう書くのか」という理由が一つ増えました😳 もししれっと直ってしまったとしても、今回の調査はかなり面白い寄り道でした。

今回調べて思ったこと ​

今回の更新はあくまでも、プレビュー扱いなのであまり声を大にして言えませんが率直にいうと

txt
GetTcpTable、

「TCP状態を返すAPI」だと思ってたら、

「その日の気分でTCP状態っぽい何かを返すAPI」疑惑が出てきて困惑🙃

これに尽きます。


後日談: 本当にしれっと直った ​

2026年11月11日、手元の PC に11月の定例のセキュリティ更新「November 10, 2026—KB5132061 (OS Builds 26300.9974, 26200.9974 and 26100.9974)」が入りました。ビルドは 26300.9818 から 26300.9974 に上がっています。そこで翌日、同じ検証コードで、同じ手順をもう一度試しました。

結果は、上に書いたオチのとおりでした。

……直ってる🙃

ドライバとブラウザのポート ​

起動したものポートKB5127936 の時点KB5132061 の後
msedgedriver9515確認できず / 140ミリ秒 / 2953ミリ秒391ミリ秒 / 125ミリ秒 / 109ミリ秒
msedgedriver9333203ミリ秒 / 確認できず94ミリ秒
msedgedriver60123確認できず(ずっと 0)141ミリ秒
Edge9333469ミリ秒 / 781ミリ秒359ミリ秒
Edge95151532ミリ秒 / 312ミリ秒344ミリ秒
Edge0(自動)3回とも確認できず(ずっと 0)3回とも、ポートが分かった時点ですでに LISTEN

表は、起動してから10秒以内に LISTEN を確認できるまでの時間です。更新の後は、その後の観測でもずっと LISTEN(2)のままでした。3つの関数は、今回も同じ値を返しています。

一覧全体 ​

KB5127936 の時点KB5132061 の後
待ち受け行のうち LISTEN 以外16,754件 / 18,350件(約91%)0件 / 13,000件
接続の行なのに LISTEN2,713件0件
状態が 0 の行16,154件0件

ほかの項目も、すべて元に戻っていました。

  • 並べ替える前の一覧でも、待ち受け行はすべて LISTEN です。3行ごとのくり返しも、18行目より後ろが 0 になることもなくなりました

  • デュアルスタックの待ち受けは、今も一覧の末尾に別のまとまりとして並びます。ただし値はすべて LISTEN です。末尾に並ぶこと自体は元からの作りで、おかしかったのは値の入り方だけだったと分かります

  • IPv6 の一覧の待ち受け行も、すべて LISTEN になりました

  • IPv4 の接続の行も、TIME_WAIT の行だけが PID 0 という、自然な形に戻りました

  • Excel から取った一覧と netstat の結果は、全行で一致しました。状態が 0 で netstat から行が消えることもなくなりました

このツール本体の WebSocket 接続も試しました。3回とも接続に成功し、CDP のコマンドも30回すべて成功しています。前回は10回とも 0 だったデバッグポートの待ち受け行は、今回は30回すべて LISTEN でした。

Microsoft は何か言っていたか ​

「GetTcpTable がおかしかったので直しました」という発言がないか、2026年11月12日の時点で次の3か所を確かめました。

  • KB5132061 のサポートページ:TCP や GetTcpTable、netstat に触れた記述はありません。既知の問題の欄も「現時点で認識している問題はない」という内容でした

  • Windows 11 26H2 の既知の問題のページ:KB5132061 で解決した問題として載っているのは USB オーディオの件で、TCP の一覧に関する項目はありません

  • 冒頭で紹介した Microsoft Q&A の2つのスレッド:Microsoft 側のモデレーターは返信していますが、不具合を認めたり、修正したと書いたりした返信はありません。どちらも「Microsoft としてはまだ確認できていない」という趣旨の返信のまま終わっていて、片方は11月11日に「返事がないので閉じる」として閉じられていました

修正したという発表は、どこにも見つかりませんでした。まさに、しれっと直ったわけです。

ちなみに GitHub では、DAX Studio や cua など、ほかのツールでも「状態の値を信用せずに待ち受けを探す」修正が入っていました。cua の修正は、まさにブラウザの DevTools の待ち受けを探す処理です。SeleniumBasic だけの話ではなかったようです。

迷宮入りしたもの、残ったもの ​

「まだ分かっていないこと」に書いた18行という件数の理由や、IPv6 CLAT との関係は、確かめる前に直ってしまったので、予告どおり迷宮入りです。

それでも、このツールの接続処理に「余計なポートの事前確認を置かない」理由は、Windows が直った今もそのまま残っています。

Excel VBA × CDP / WebDriver BiDi