@@ -58,7 +58,16 @@ def _parse_wmic_list(text: str) -> List[Dict[str, str]]:
5858 if not line or "=" not in line :
5959 continue
6060 k , v = line .split ("=" , 1 )
61- item [k .strip ().lower ()] = v .strip ()
61+ key = k .strip ().lower ()
62+ val = v .strip ()
63+ # 同一记录里可能出现多行 IPAddress=(数组);后者会覆盖前者导致 Win7 上漏配
64+ if key in item :
65+ if val :
66+ prev_parts = [x .strip () for x in item [key ].split ("," ) if x .strip ()]
67+ if val not in prev_parts :
68+ item [key ] = item [key ] + "," + val
69+ else :
70+ item [key ] = val
6271 if item :
6372 rows .append (item )
6473 return rows
@@ -78,6 +87,22 @@ def _clean_wmic_value(s: str) -> str:
7887 return s
7988
8089
90+ def _wmic_multivalue_tokens (s : str ) -> List [str ]:
91+ """展开 WMIC 里逗号/花括号数组字段(如 IPAddress),保留全部原子值供匹配。"""
92+ s = (s or "" ).strip ()
93+ if not s :
94+ return []
95+ inner = s [1 :- 1 ].strip () if s .startswith ("{" ) and s .endswith ("}" ) else s
96+ out : List [str ] = []
97+ for part in re .split (r"\s*,\s*" , inner ):
98+ p = part .strip ()
99+ if p .startswith ('"' ) and p .endswith ('"' ):
100+ p = p [1 :- 1 ]
101+ if p :
102+ out .append (p )
103+ return out
104+
105+
81106def _wmic_nic_name_for_index (idx : int ) -> Tuple [Optional [str ], str ]:
82107 try :
83108 p2 = subprocess .run (
@@ -159,6 +184,93 @@ def _interface_from_wmi_default_gateway() -> Tuple[Optional[str], str]:
159184 return None , msg
160185
161186
187+ def _windows_has_net_tcpip_ps_cmdlets () -> bool :
188+ """Get-NetRoute / Get-NetIPConfiguration 需 Win8+(6.2)或 Win10+ 且带 NetTCPIP;Win7 无此 cmdlet。"""
189+ if sys .platform != "win32" :
190+ return False
191+ v = sys .getwindowsversion ()
192+ if v .major > 6 :
193+ return True
194+ if v .major == 6 and v .minor >= 2 :
195+ return True
196+ return False
197+
198+
199+ def _powershell_has_get_net_route_cmdlet () -> bool :
200+ """
201+ 是否实际存在 Get-NetRoute(NetTCPIP 模块)。
202+ 仅用 getwindowsversion 不可靠:兼容模式/部分环境会误报为 6.2+,Win7 仍会执行失败。
203+ """
204+ if sys .platform != "win32" :
205+ return False
206+ ps = "if (Get-Command Get-NetRoute -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }"
207+ try :
208+ p = subprocess .run (
209+ [
210+ "powershell" ,
211+ "-NoProfile" ,
212+ "-NonInteractive" ,
213+ "-Command" ,
214+ ps ,
215+ ],
216+ capture_output = True ,
217+ text = True ,
218+ creationflags = _subprocess_flags (),
219+ timeout = 20 ,
220+ )
221+ return p .returncode == 0
222+ except (OSError , subprocess .TimeoutExpired , ValueError ):
223+ return False
224+
225+
226+ def _interface_from_wmi_ip4_route_table () -> Tuple [Optional [str ], str ]:
227+ """
228+ Win7+:WMI Win32_IP4RouteTable 取默认路由 (0.0.0.0/0.0.0.0) 的 InterfaceIndex,
229+ 不依赖 Win32_NetworkAdapterConfiguration.DefaultIPGateway 或 IPAddress 字符串反查。
230+ """
231+ try :
232+ p = subprocess .run (
233+ [
234+ "wmic" ,
235+ "path" ,
236+ "Win32_IP4RouteTable" ,
237+ "where" ,
238+ "Destination='0.0.0.0' and Mask='0.0.0.0'" ,
239+ "get" ,
240+ "InterfaceIndex,Metric1" ,
241+ "/format:list" ,
242+ ],
243+ capture_output = True ,
244+ text = True ,
245+ creationflags = _subprocess_flags (),
246+ timeout = 30 ,
247+ )
248+ except OSError as e :
249+ return None , "wmic Win32_IP4RouteTable unavailable: %s" % e
250+ if p .returncode != 0 :
251+ return None , (p .stderr or p .stdout or "wmic Win32_IP4RouteTable failed" ).strip ()[:500 ]
252+ candidates : List [Tuple [int , int ]] = [] # (metric, interface_index)
253+ for row in _parse_wmic_list (p .stdout or "" ):
254+ m = re .search (r"\d+" , row .get ("interfaceindex" , "" ))
255+ if not m :
256+ continue
257+ idx = int (m .group ())
258+ met_s = row .get ("metric1" , "" ).strip ()
259+ try :
260+ metric = int (met_s ) if met_s .isdigit () else 9999
261+ except ValueError :
262+ metric = 9999
263+ candidates .append ((metric , idx ))
264+ if not candidates :
265+ return None , "wmi Win32_IP4RouteTable no default row"
266+ candidates .sort (key = lambda x : x [0 ])
267+ idx = candidates [0 ][1 ]
268+ name , msg = _wmic_nic_name_for_index (idx )
269+ if name :
270+ return name , "wmi Win32_IP4RouteTable (%s)" % msg
271+ return None , msg
272+
273+
162274def _interface_from_powershell_net_route () -> Tuple [Optional [str ], str ]:
163275 """
164276 Win8+:用默认路由所在接口(不依赖 WMI 的 DefaultIPGateway 字段,避免静态/特殊驱动下为空)。
@@ -248,10 +360,10 @@ def _interface_from_route_print(interface_ip: str) -> Tuple[Optional[str], str]:
248360 target = interface_ip .strip ().lower ()
249361 for row in _parse_wmic_list (p .stdout or "" ):
250362 raw_ip = row .get ("ipaddress" , "" )
251- cleaned = _clean_wmic_value ( raw_ip ) .lower ()
252- # 可能为 "a.b.c.d" 或逗号分隔多个
253- parts = re . split ( r"[\s,;]+" , cleaned )
254- if target not in parts and target not in cleaned . replace ( "{" , "" ). replace ( "}" , "" ) :
363+ tokens = [ t .lower () for t in _wmic_multivalue_tokens ( raw_ip )]
364+ if not tokens :
365+ tokens = [ _clean_wmic_value ( raw_ip ). lower ()]
366+ if target not in tokens :
255367 continue
256368 m = re .search (r"\d+" , row .get ("interfaceindex" , "" ))
257369 if not m :
@@ -308,7 +420,8 @@ def _interface_from_route_print_only() -> Tuple[Optional[str], str]:
308420def get_default_ipv4_interface_name () -> Tuple [Optional [str ], str ]:
309421 """
310422 返回 (供 netsh 使用的接口名, 诊断说明)。
311- 依次尝试:WMI DefaultIPGateway → PowerShell 默认路由 → PowerShell NetIPConfiguration → route print。
423+ 依次尝试:WMI DefaultIPGateway →(若存在 cmdlet)PowerShell 默认路由 / NetIPConfiguration
424+ → WMI Win32_IP4RouteTable → route print + WMI 反查。
312425 """
313426 if sys .platform != "win32" :
314427 return None , "not windows"
@@ -320,13 +433,25 @@ def get_default_ipv4_interface_name() -> Tuple[Optional[str], str]:
320433 if name :
321434 return name , why
322435
323- name , why = _interface_from_powershell_net_route ()
324- tried .append ("ps-route:%s" % why )
325- if name :
326- return name , why
436+ if _powershell_has_get_net_route_cmdlet ():
437+ name , why = _interface_from_powershell_net_route ()
438+ tried .append ("ps-route:%s" % why )
439+ if name :
440+ return name , why
441+
442+ name , why = _interface_from_powershell_net_ip_configuration ()
443+ tried .append ("ps-ipcfg:%s" % why )
444+ if name :
445+ return name , why
446+ else :
447+ tried .append (
448+ "ps-route:skipped (no Get-NetRoute cmdlet; os claims net_tcpip=%s)"
449+ % _windows_has_net_tcpip_ps_cmdlets ()
450+ )
451+ tried .append ("ps-ipcfg:skipped (same)" )
327452
328- name , why = _interface_from_powershell_net_ip_configuration ()
329- tried .append ("ps-ipcfg :%s" % why )
453+ name , why = _interface_from_wmi_ip4_route_table ()
454+ tried .append ("ip4route :%s" % why )
330455 if name :
331456 return name , why
332457
0 commit comments