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 を起動すると、次のエラーで止まります。
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(新しいセッションを受け付けられる)が返ってきます。
WebDriver 起動している
HTTP /status 200 が返る
↓
なのに……
↓
GetTcpTable LISTEN と判定できない
↓
SeleniumBasic 「Driver が起動していない!」
↓
TimeoutErrorWebDriver の起動に失敗したのではなく、「起動したかどうかの判定」のほうが失敗していたわけです。
GetTcpTable のおさらい
ここで、出てくる言葉を簡単に整理しておきます。
プログラム同士がネットワークで話すときは、「ポート」という番号付きの窓口を使います。窓口を開けて相手からの接続を待っている状態を「待ち受け中(LISTEN)」と呼びます。
GetTcpTable は、iphlpapi.dll に入っている関数です。Windows に「今ある TCP の窓口と接続を一覧でちょうだい」と頼むと、1行ごとに自分側の住所とポート、相手側の住所とポート、状態の番号が返ってきます。似た関数の GetTcpTable2 と GetExtendedTcpTable や、コマンドの netstat、PowerShell の Get-NetTCPConnection も、同じ種類の情報を表示します。
| 番号 | 名前 | 意味 |
|---|---|---|
| 0 | なし | 正しい状態の番号には存在しない値 |
| 1 | CLOSED | 閉じている |
| 2 | LISTEN | 待ち受け中 |
| 5 | ESTABLISHED | 接続中 |
| 6 | FIN_WAIT1 | 切断の途中 |
| 11 | TIME_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 を確認できたか |
|---|---|---|
| msedgedriver | 9333 | 203ミリ秒 / 確認できず |
| msedgedriver | 9515 | 確認できず / 140ミリ秒 / 2953ミリ秒 |
| Edge | 9333 | 469ミリ秒 / 781ミリ秒 |
| Edge | 9515 | 1532ミリ秒 / 312ミリ秒 |
| Edge | 0(自動。実際は 57329、59734、63923) | 3回とも確認できず(ずっと 0) |
| msedgedriver | 60123、61234 | 2回とも確認できず(ずっと 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 から返ってきたままの順番で見てみました。この順番は、ほぼ自分側のポート番号の小さい順でした。
待ち受け行の状態を、この順に並べるとこうなります。
行番号 : 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行ごとのくり返しがそのまま続きます。
先頭のまとまり(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 経路にまとめています。
- ブラウザを
--remote-debugging-port=0で起動する - ブラウザが書き出す
DevToolsActivePortファイルが現れるまで待つ(0.1秒おきに最大100回) - ファイルから、ポート番号と接続先のパスを読む
socket()でソケットを作り、connect()で TCP 接続する- WebSocket のハンドシェイクを送る
- 応答に
HTTP/1.1 101が含まれているか確かめる
ConnectCDP の中身を抜き出すと、次のとおりです(手順4〜5とエラーメッセージの後半は省略しています)。
'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 のコマンド | 待ち受け行の状態 | 並ぶ位置 |
|---|---|---|---|---|---|
| 1 | 52170 | 1390ミリ秒 | 10回とも成功 | 10回とも 0 | 行番号 29 |
| 2 | 60691 | 1203ミリ秒 | 10回とも成功 | 10回とも 0 | 行番号 35 |
| 3 | 52823 | 1281ミリ秒 | 10回とも成功 | 10回とも 0 | 行番号 29〜30 |
並ぶ位置は、並べ替える前の一覧での行番号です。3回とも16行より後ろに並び、待ち受け行は一度も LISTEN になりませんでした。Excel とブラウザの間の WebSocket 接続の行も、状態が 0、1、2、5、6 とばらばらで、Excel 側の行が LISTEN(2)と出た回までありました。
それでも、接続とコマンドのやり取りは、すべて成功しています。一覧の状態がどれだけおかしくても、connect() とハンドシェイクで確かめるこのツールには関係ありませんでした。
今回の件を見た後では、むしろこれでよかったのでは? と思えてきました。
事前に確かめると、何が保証されるのか
たとえば、丁寧に作ろうとして次のようにしたとします。
GetTcpTable で一覧をもらう
↓
目当てのポートを探す
↓
LISTEN(2)か?
↓
connect()一見すると堅牢そうです。しかし、結局 connect() 自体のエラー処理は必要になります。次のことは普通に起こり得るからです。
ポートを確認 → OK
↓
その直後に相手が終了
↓
connect() → 失敗逆に今回のように、一覧では LISTEN ではないのに、connect() や HTTP は成功する、ということまで起こり得ます。事前チェックを入れると、本来成功できる処理を、自分から失敗させる可能性まで増えてしまいます。
ポートの確認は数行では終わらない
もしポートの確認が If IsPortOpen(Host, port) Then ... のような関数一本で済むなら、入れたくなっていたかもしれません。しかし、GetTcpTable で確かめるには、実際には次の処理が必要です。
GetTcpTable を Declare する
↓
必要なバッファの大きさを聞く
↓
Byte 配列を確保する
↓
GetTcpTable をもう一度呼ぶ
↓
MIB_TCPTABLE を読み解き、MIB_TCPROW を1行ずつ見る
↓
ポート番号の並び(ネットワークバイトオーダー)を直す
↓
目当てのポートを探して、状態が LISTEN か確かめるそこまで作って得られる情報は、「たぶん今 connect() できそうです」です。そして次の行で、本当に connect() します。
……それなら、最初から connect() すればいいわけです🙃
頼る先も増える
このツールの WebSocket 接続は、WinSock(ws2_32.dll)の関数だけで完結しています。
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 のコメントとして残すなら、こんな感じです。
' TCP の LISTEN 状態の事前列挙は行わない。
' 接続可否は別 API による推測ではなく、
' WinSock connect() の実際の成否で判定する。
' その後、WebSocket Handshake と応答を
' 各プロトコル層の成功条件として検証する。SeleniumBasic を使っている方へ
SeleniumBasic の起動判定は、手元の PC では運しだいになります。WebDriver のポートが17行目以降に並ぶと必ずタイムアウトし、16行以内でも「2」の位置に当たらなければタイムアウトします。
回避するには、WebDriver を自分で先に起動しておき、SeleniumBasic からはそこへつなぐだけにします。
driver.StartRemotely _
"http://127.0.0.1:9515", _
"edge"こうすると、GetTcpTable を使う起動判定を通りません。自分で起動判定を書く場合も、一覧の状態を見るより、/status に HTTP でアクセスして、200 と "ready": true が返るかを確かめるほうが確実です。これも「本番の操作そのもので確かめる」の一つです。
調べた環境とまだ分かっていないこと
| 項目 | 内容 |
|---|---|
| OS | Windows 11 Pro 26H2(ビルド 26300.9818、KB5127936 適用済み) |
| WebDriver | msedgedriver 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)でした。
プレビューの更新は、ざっくり言うと次の流れの途中にあります。今回の更新も、この途中にいるのかもしれません。
プレビューの更新
↓
変更を先に配る
↓
問題が見つかるかもしれない
↓
直される
↓
後に続く正式な累積更新へネットワークまわりの変更(推測)
ここからは推測です。
KB5127936 のサポートページを読むと、変更点の中に「Wi-Fi とイーサネットのネットワーク インターフェイスで IPv6 CLAT に対応した」という項目があります。CLAT は、IPv6 だけのネットワークにつながっていても IPv4 のアプリが通信できるように、IPv4 と IPv6 の間で変換する仕組みです。
今回の観測では、IPv4 の一覧は待ち受け行も接続の行もおかしくなっていたのに、IPv6 の一覧の接続の行は正しそうでした。また、デュアルスタックの待ち受けは、IPv4 の一覧の末尾に別のまとまりとして並んでいました。IPv4 と IPv6 の扱いに手が入った更新で、IPv4 の一覧にだけ目立つずれが出ている、という組み合わせは少し気になります。
ただし、CLAT への対応と今回の現象に関係があるかは分かりません。この PC で CLAT が実際に動いているかも確かめていませんし、サポートページの既知の問題にも、TCP の一覧に関する記述はありません。
次の更新で直るかもしれない
もちろん、Microsoft から GetTcpTable まわりの正式な原因の説明や、修正の予定が発表されたことは、まだ確認できていません。
それでも、今回の更新がプレビューである以上、こんなオチも十分に考えられます。
2026年10月
KB5127936(プレビュー)
↓
GetTcpTable のまわりで怪しい挙動
↓
SeleniumBasic 民
「なんで動かないんだ!!!」
↓
GetTcpTable まで掘る
↓
VBA で再現 PoC まで作る
↓
設計原則まで考え始める
↓
次の Windows Update
↓
LISTEN
LISTEN
LISTEN
LISTEN
↓
「……直ってる🙃」それはそれで、とても Windows Update らしい後日談になりそうです。
次の更新の後にもう一度試す
今回作った検証コードは、この後日談を確かめるためにも残しておく価値があります。
次の正式な累積更新は、11月の定例のセキュリティ更新です。第2火曜日にあたる2026年11月10日に予定されています。プレビューの変更は、ふつうこの定例の更新で正式に配られます。そこで直っていれば「プレビューの間だけの問題だった」、直っていなければ「正式な更新にもそのまま入った」ことになります。
11月10日以降にこの更新を入れたら、条件をできるだけ変えずに、もう一度試すつもりです。
msedgedriver.exe --port=9515で起動するhttp://127.0.0.1:9515/statusにアクセスし、HTTP 200 と"ready": trueを確かめる- VBA から
GetTcpTableを400ミリ秒おきに観測する - 待ち受け行(
127.0.0.1:9515 → 0.0.0.0:0)の状態の値を記録する - 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 側の挙動が後で直ったとしても、「安全に本番の操作を試せるなら、代わりの状態から成功しそうかを推測するより、本番の操作の結果を確かめる」という原則は残ります。
おわりに
今回の調査は、次のように妙なところまで広がりました。
SeleniumBasic が動かない
↓
GetTcpTable が怪しい
↓
Windows の TCP の状態の一覧がおかしい?
↓
自分の CDP 接続の connect() の設計を見直す
↓
「ポートの事前確認、そもそも要らなくない?」
↓
Connection Validation Principle の誕生そして最後に分かったのが、関連が疑われる更新そのものがプレビューだった、ということです。もしかすると数週間後には、
あれだけ掘ったのに、次の Windows Update でしれっと直った🙃
となるのかもしれません。そうなれば、上に書いた「まだ分かっていないこと」も、確かめる前に迷宮入りかもしれません。
それでも、無駄ではありません。バグそのものは消えても、なぜ自分の接続処理では、余計なポートの事前確認を置かないのかという理由は残るからです。
この調査で、GetTcpTable は「知らなかったから使わなかった」関数から、「知ったうえで、この用途には使わない」関数に変わりました。これが一番大きな収穫だった気がします。
他人の10年前のコードを深掘りしたら、自分のコードに「なぜそう書くのか」という理由が一つ増えました😳 もししれっと直ってしまったとしても、今回の調査はかなり面白い寄り道でした。
今回調べて思ったこと
今回の更新はあくまでも、プレビュー扱いなのであまり声を大にして言えませんが率直にいうと
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 の後 |
|---|---|---|---|
| msedgedriver | 9515 | 確認できず / 140ミリ秒 / 2953ミリ秒 | 391ミリ秒 / 125ミリ秒 / 109ミリ秒 |
| msedgedriver | 9333 | 203ミリ秒 / 確認できず | 94ミリ秒 |
| msedgedriver | 60123 | 確認できず(ずっと 0) | 141ミリ秒 |
| Edge | 9333 | 469ミリ秒 / 781ミリ秒 | 359ミリ秒 |
| Edge | 9515 | 1532ミリ秒 / 312ミリ秒 | 344ミリ秒 |
| Edge | 0(自動) | 3回とも確認できず(ずっと 0) | 3回とも、ポートが分かった時点ですでに LISTEN |
表は、起動してから10秒以内に LISTEN を確認できるまでの時間です。更新の後は、その後の観測でもずっと LISTEN(2)のままでした。3つの関数は、今回も同じ値を返しています。
一覧全体
| KB5127936 の時点 | KB5132061 の後 | |
|---|---|---|
| 待ち受け行のうち LISTEN 以外 | 16,754件 / 18,350件(約91%) | 0件 / 13,000件 |
| 接続の行なのに LISTEN | 2,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 が直った今もそのまま残っています。

