I am trying to get the list of all available networks using WlanGetAvailableNetworkList
. When I loop over the array of networks based on dwNumberOfItems
it only shows me the first network and anything beyond that gives me index out of bounds: the len is 1 but the index is 1
. Here's my code:
let mut w_handle: HANDLE = HANDLE::default();
let mut current_version: u32 = 0;
if WIN32_ERROR(WlanOpenHandle(2, None, &mut current_version, &mut w_handle)) != ERROR_SUCCESS {
return;
}
let mut wlan_interface_info_list = std::ptr::null_mut();
if WIN32_ERROR(WlanEnumInterfaces(w_handle, None, &mut wlan_interface_info_list)) != ERROR_SUCCESS {
return;
}
for i in 0..wlan_interface_info_list.as_ref().unwrap().dwNumberOfItems {
let interface_info = wlan_interface_info_list.as_ref().unwrap().InterfaceInfo[i as usize];
let interface_name = interface_info.strInterfaceDescription;
let mut available_network_list = std::mem::zeroed();
if WIN32_ERROR(WlanGetAvailableNetworkList(w_handle, &interface_info.InterfaceGuid, 0, None, &mut available_network_list)) != ERROR_SUCCESS {
continue;
}
let list = available_network_list.as_ref().unwrap();
println!("Number of available networks on {}: {}", String::from_utf16_lossy(&interface_name), list.dwNumberOfItems);
for j in 0..list.dwNumberOfItems {
let network_name = (*list).Network[j as usize].strProfileName; // <- Panicked
println!("Network name: {}", String::from_utf16_lossy(&network_name));
}
}
WlanCloseHandle(w_handle, None);
The type behind available_network_list
says that it only ever has one Network
(docs):
pub struct WLAN_AVAILABLE_NETWORK_LIST {
pub dwNumberOfItems: u32,
pub dwIndex: u32,
pub Network: [WLAN_AVAILABLE_NETWORK; 1],
}
However, we know from the official documentation that it is actually intended to be a DST-like type: where dwNumberOfItems
and dwIndex
are followed by dwNumberOfItems
number of Network
s. It is designed like this because Rust can't exactly express this type properly (DSTs are possible but not when the value itself contains its size) and a one-length array member is a C++ -ism to convey the same thing (see flexible array member in C but C++ can't have zero-length arrays).
So when you do .Network[j as usize]
it is using the index operator for the array which "knows" it only has one element. To circumvent this you should make your own slice (via pointers to avoid potential UB):
let networks_len = (*available_network_list).dwNumberOfItems;
let networks_ptr = std::ptr::addr_of!((*available_network_list).Network);
let networks = std::slice::from_raw_parts(
networks_ptr.cast::<WLAN_AVAILABLE_NETWORK>(),
networks_len as usize,
);
for network in networks {
let network_name = str::from_utf8(&network.dot11Ssid.ucSSID).unwrap();
println!("Network name: {network_name}");
}
I would also show the .dot11Ssid.ucSSID
instead of the .strProfileName
.
You will encounter a similar error with wlan_interface_info_list
if you were to have more than one WLAN interface. The same principle applies.