You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Windows crate调用GetInterfaceInfo时的多网卡内存分配问题

问题描述

使用Microsoft的windows crate调用GetInterfaceInfo获取系统网卡信息时,单网卡场景可正常运行,但多网卡时崩溃,报错:

panicked at 'index out of bounds: the len is 1 but the index is 1'

查看IP_INTERFACE_INFO结构体定义,其Adapter字段为长度1的数组:

#[repr(C)]
pub struct IP_INTERFACE_INFO {
    pub NumAdapters: i32,
    pub Adapter: [IP_ADAPTER_INDEX_MAP; 1],
}

疑惑该结构体如何存储多网卡数据,测试代码如下:

#[cfg(test)]
mod tests {
    use super::*;
    use windows::{
        core::*, Win32::Foundation::*, Win32::NetworkManagement::IpHelper::*,
    };

    #[test]
    fn main() {
        unsafe {
            let mut dw_out_buf_len: u32 = 0;
            let mut dw_ret_val = GetInterfaceInfo(std::ptr::null_mut(), &mut dw_out_buf_len as *mut u32);
            if dw_ret_val != ERROR_INSUFFICIENT_BUFFER.0 {
                panic!();
            }
            let mut ip_interface_info = Vec::with_capacity(dw_out_buf_len as usize);
            let mut ptr = ip_interface_info.as_mut_ptr() as *mut IP_INTERFACE_INFO;
            dw_ret_val = GetInterfaceInfo(ptr, &mut dw_out_buf_len as *mut u32);
            println!("Num adapters: {}", (*ptr).NumAdapters);
            for i in 0..(*ptr).NumAdapters as usize {
                println!(
                    "\tAdapter index: {}\n\tAdapter name: {}",
                    (*ptr).Adapter[i].Index,
                    String::from_utf16(&(*ptr).Adapter[i].Name).unwrap()
                );
            }
        }
    }
}

请问该如何正确分配内存以支持多网卡信息获取?

解决方案

这个问题的核心是Windows API中可变长度结构体的特殊处理逻辑:IP_INTERFACE_INFO的Adapter字段定义为长度1的数组,是C语言里用来表示"紧跟在结构体后的可变数量元素"的写法——实际内存中,NumAdapters的值指定了结构体后紧跟的IP_ADAPTER_INDEX_MAP实例数量,并非真的只有1个元素。

你的代码崩溃是因为直接通过(*ptr).Adapter[i]访问时,Rust会严格遵循编译期确定的数组长度(仅1个元素)进行边界检查,多网卡场景下索引超出范围触发panic。正确的做法是通过指针偏移来访问后续的IP_ADAPTER_INDEX_MAP元素。

修改后的测试代码如下:

#[cfg(test)]
mod tests {
    use super::*;
    use windows::{
        core::*, Win32::Foundation::*, Win32::NetworkManagement::IpHelper::*,
    };

    #[test]
    fn get_interface_info_test() {
        unsafe {
            let mut dw_out_buf_len: u32 = 0;
            // 第一次调用获取所需内存大小
            let mut dw_ret_val = GetInterfaceInfo(std::ptr::null_mut(), &mut dw_out_buf_len);
            if dw_ret_val != ERROR_INSUFFICIENT_BUFFER {
                panic!("Failed to get required buffer size: {dw_ret_val}");
            }

            // 分配并初始化足够的内存(WinAPI要求内存初始化为0,避免未定义行为)
            let mut buf = vec![0u8; dw_out_buf_len as usize];
            let info_ptr = buf.as_mut_ptr() as *mut IP_INTERFACE_INFO;

            // 第二次调用获取实际网卡数据
            dw_ret_val = GetInterfaceInfo(info_ptr, &mut dw_out_buf_len);
            if dw_ret_val != NO_ERROR {
                panic!("Failed to get interface info: {dw_ret_val}");
            }

            let num_adapters = (*info_ptr).NumAdapters as usize;
            println!("Num adapters: {}", num_adapters);

            // 获取第一个Adapter的指针,通过指针偏移访问后续元素
            let first_adapter = &(*info_ptr).Adapter[0] as *const IP_ADAPTER_INDEX_MAP;
            for i in 0..num_adapters {
                let adapter = *first_adapter.add(i);
                // 处理UTF-16名称,截断到第一个空字符避免多余空字节
                let name = String::from_utf16(&adapter.Name)
                    .unwrap_or_else(|_| String::from("Invalid UTF-16 name"))
                    .trim_matches(char::from(0))
                    .to_string();
                
                println!(
                    "\tAdapter index: {}\n\tAdapter name: {}",
                    adapter.Index,
                    name
                );
            }
        }
    }
}

关键修改说明:

  1. 内存分配与初始化:用vec![0u8; dw_out_buf_len as usize]替代Vec::with_capacity,确保内存被实际分配并初始化为0,符合WinAPI的内存要求
  2. 指针偏移访问:取第一个IP_ADAPTER_INDEX_MAP的指针,用add(i)进行偏移,绕过Rust的数组边界检查(这是处理WinAPI可变长度结构体的标准unsafe操作)
  3. 返回值检查:补充第二次调用GetInterfaceInfo的返回值检查,确保数据获取成功
  4. 名称处理优化:对Adapter.Name的UTF-16转字符串逻辑增加空字符截断,避免无效字符或多余空字节

内容的提问来源于stack exchange,提问作者Mah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 18:45:39