Home > database >  How do I allocate space to call GetInterfaceInfo using the windows crate?
How do I allocate space to call GetInterfaceInfo using the windows crate?

Time:09-13

I'm trying to fetch information regarding the network interfaces available on the system via GetInterfaceInfo using Microsoft's windows crate. This requires me to do some unsafe operations, and I get it to work for one interface, but not two:

#[cfg(test)]
mod tests {
    use super::*;
    use windows::{
        core::*, Data::Xml::Dom::*, Win32::Foundation::*, Win32::NetworkManagement::IpHelper::*,
        Win32::System::Threading::*, Win32::UI::WindowsAndMessaging::*,
    };

    #[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!();
            }

            println!("Size: {}", dw_out_buf_len);
            // allocate that amount of memory, which will be used as a buffer
            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()
                );
            }
        }
    }
}

It crashes when I'm trying to access the second entry (even though there should be two available):

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

The struct IP_INTERFACE_INFO containing all data has a field called Adapter which seems to be limited to only be array size of 1. Am I reading this correctly? How is it then supposed to hold multiple adapters?

#[repr(C)]
#[doc = "*Required features: `\"Win32_NetworkManagement_IpHelper\"`*"]
pub struct IP_INTERFACE_INFO {
    pub NumAdapters: i32,
    pub Adapter: [IP_ADAPTER_INDEX_MAP; 1],
}

CodePudding user response:

It appears that IP_INTERFACE_INFO uses a C flexible array member, which often uses the [1] syntax. The C example in Managing Interfaces Using GetInterfaceInfo corroborates this usage:

for (i = 0; i < (unsigned int) pInterfaceInfo->NumAdapters; i  ) {
    printf("  Adapter Index[%d]: %ld\n", i,
           pInterfaceInfo->Adapter[i].Index);
    printf("  Adapter Name[%d]:  %ws\n\n", i,
           pInterfaceInfo->Adapter[i].Name);
}

The equivalent in Rust would be to take the single-element array, get the raw pointer to it, then iterate over that (untested as I'm not currently near Windows):

for i in 0..(*ptr).NumAdapters as usize {
    let adapter = (*ptr).Adapter.as_ptr().add(i);
}

I might even use a tool like slice::from_raw_parts to convert the raw pointer and runtime length into a nice Rust slice.

  • Related