Я пытаюсь получить список всех доступных сетей, используя WlanGetAvailableNetworkList
. Когда я перебираю массив сетей на основе dwNumberOfItems
, мне показывается только первая сеть, а все, что за ее пределами, дает мне index out of bounds: the len is 1 but the index is 1
. Вот мой код:
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);
Тип, стоящий за available_network_list
, говорит, что он всегда бывает только один Network
(документы):
pub struct WLAN_AVAILABLE_NETWORK_LIST {
pub dwNumberOfItems: u32,
pub dwIndex: u32,
pub Network: [WLAN_AVAILABLE_NETWORK; 1],
}
Однако из официальной документации мы знаем, что на самом деле он должен быть типом, похожим на летнее время: где за dwNumberOfItems
и dwIndex
следует dwNumberOfItems
количество Network
. Он спроектирован таким образом, потому что Rust не может точно выразить этот тип должным образом (DST возможны, но не тогда, когда само значение содержит его размер), а член массива одной длины является C++ -измом, передающим то же самое (см. гибкий член массива в C, но в C++ не могут быть массивы нулевой длины).
Поэтому, когда вы это делаете, .Network[j as usize]
используется оператор индекса для массива, который «знает», что у него есть только один элемент. Чтобы обойти это, вам следует создать свой собственный фрагмент (с помощью указателей, чтобы избежать потенциальных 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}");
}
Я бы также показал .dot11Ssid.ucSSID
вместо .strProfileName
.
Вы столкнетесь с аналогичной ошибкой с wlan_interface_info_list
, если у вас более одного интерфейса WLAN. Применяется тот же принцип.
Немного больше контекста о структуре с завершающим массивом размера 1: Почему некоторые структуры заканчиваются массивом размера 1?
@ChayimFriedman отредактировал, надеюсь, так стало немного лучше
Помимо основной проблемы, существует также утечка ресурсов. Как объяснено здесь : «Память, используемая для буфера, на который указывает параметр ppAvailableNetworkList
, должна быть освобождена путем вызова функции WlanFreeMemory после того, как буфер больше не нужен».
Это UB в Stacked Borrows из-за ссылки на
Network
. Вы должны работать только с необработанными указателями.