Как я могу получить список сетей с помощью WlanGetAvailableNetworkList, используя windows-rs в Rust

Я пытаюсь получить список всех доступных сетей, используя 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);
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Тип, стоящий за 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. Применяется тот же принцип.

Это UB в Stacked Borrows из-за ссылки на Network. Вы должны работать только с необработанными указателями.

Chayim Friedman 22.07.2024 17:11

Немного больше контекста о структуре с завершающим массивом размера 1: Почему некоторые структуры заканчиваются массивом размера 1?

IInspectable 22.07.2024 17:21

@ChayimFriedman отредактировал, надеюсь, так стало немного лучше

kmdreko 22.07.2024 18:08

Помимо основной проблемы, существует также утечка ресурсов. Как объяснено здесь : «Память, используемая для буфера, на который указывает параметр ppAvailableNetworkList, должна быть освобождена путем вызова функции WlanFreeMemory после того, как буфер больше не нужен».

IInspectable 23.07.2024 12:22

Другие вопросы по теме