Как подключиться к сети Wi-Fi в Windows, используя Python 3? - PullRequest
2 голосов
/ 23 июня 2019

Я пытаюсь написать скрипт на Python 3, но все модули, доступные сегодня, работают на Python 2, что позволит мне искать беспроводные сети и подключаться к ним. Есть ли для этого библиотека Python 3?

Код, который я пробовал для python 2

from wireless import Wireless
wireless = Wireless()
wireless.connect(ssid='ssid', password='password')

что дает мне ошибку

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 23, in __init__
    self._driver_name = self._detectDriver()
  File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 50, in _detectDriver
    compare = self.vercmp(ver, "0.9.9.0")
  File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 71, in vercmp
    return cmp(normalize(actual), normalize(test))
NameError: name 'cmp' is not defined

Но это не работает, так как он основан на Python 2. Есть ли способ подключиться к Wi-Fi, используя Python 3

1 Ответ

1 голос
/ 23 июня 2019

Примечания (около [PyPI]: беспроводной 0.3.2 ]):

  • Поддерживает ли не (пока) Python 3 : где-то в коде используется функция cmp (которая доступно в Python 2 )
    • Я хотел отправить запрос на извлечение (так как исправление тривиально), но, по-видимому, на GitHub он уже был исправлен, но хранилище PyPI не обновлялось (с 2016 года)
  • Работает ли не на Win (на домашней странице перечислены только драйверы Nix - в основном запускаются только команды shell )

Как следствие, я бы предложил поискать альтернативы:

Хорошо, после долгого просмотра:

, я смог что-то придумать.

code.py

#!/usr/bin/env python3

import sys
import time
import ctypes
import comtypes
import traceback
from win32wifi import Win32Wifi as ww


ERROR_SUCCESS = 0

WLAN_CONNECTION_HIDDEN_NETWORK = 0x00000001


class WLANException(Exception): pass


class ConnectCallbackContext(ctypes.Structure):
    _fields_ = [
        ("guid", ctypes.c_wchar_p),
        ("start", ctypes.c_byte),
        ("end", ctypes.c_byte),
        ("fail", ctypes.c_byte),
    ]


def _wlan_connect_callback(data, context_addr):
    if context_addr:
        context = ConnectCallbackContext.from_address(context_addr)
        if str(data.interfaceGuid) == context.guid and data.notificationSource == ww.WLAN_NOTIFICATION_SOURCE_DICT[ww.WLAN_NOTIFICATION_SOURCE_ACM]:
            if data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_start.name:
                context.start += 1
            elif context.start:
                if data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_complete.name:
                    context.end += 1
                elif data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_attempt_fail.name:
                    context.fail += 1


def wireless_connect(
        ssid,
        password,
        timeout=15,  # secs
        authentication="WPA2PSK",  # "open", 
        encryption="AES",  # "WEP",
        key_type="passPhrase",  # "networkKey", 
        interface_index=0,  # Don't modify this (until PCs with more than 1 WLAN adapter arise :) )
    ):
    interfaces = ww.getWirelessInterfaces()
    if interface_index < 0 or len(interfaces) < interface_index:
        raise WLANException(-1, "No WLAN interface for given index")
    interface = interfaces[interface_index]
    profile_name = ssid + "_profile_tmp"
    ssid_hex = "".join((hex(ord(c))[2:] for c in ssid)).upper()
    profile_string = f"""<?xml version=\"1.0\"?>
        <WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">
            <name>{profile_name}</name>
            <SSIDConfig>
                <SSID>
                    <hex>{ssid_hex}</hex>
                    <name>{ssid}</name>
                </SSID>
            </SSIDConfig>
            <connectionType>ESS</connectionType>
            <connectionMode>manual</connectionMode>
            <MSM>
                <security>
                    <authEncryption>
                        <authentication>{authentication}</authentication>
                        <encryption>{encryption}</encryption>
                        <useOneX>false</useOneX>
                    </authEncryption>
                    <sharedKey>
                        <keyType>{key_type}</keyType>
                        <protected>false</protected>
                        <keyMaterial>{password}</keyMaterial>
                    </sharedKey>
                </security>
            </MSM>
        </WLANProfile>
    """
    connection_params = {
        "connectionMode": "wlan_connection_mode_temporary_profile",
        "profile": profile_string,
        "ssid": None,
        "bssidList": None,
        "bssType": "dot11_BSS_type_infrastructure",
        "flags": WLAN_CONNECTION_HIDDEN_NETWORK,
    }

    ctx = ConnectCallbackContext(interface.guid_string, 0, 0, 0)
    notification_obj = ww.registerNotification(_wlan_connect_callback, context=ctypes.pointer(ctx))

    try:
        res = ww.connect(interface, connection_params)
    except Exception as e:
        ww.unregisterNotification(notification_obj)
        raise WLANException("WlanConnect failed") from e

    end_time = time.time() + timeout;
    while time.time() < end_time:
        time.sleep(0.5)
        if ctx.end:
            break
    ww.unregisterNotification(notification_obj)
    if ctx.end:
        if ctx.fail:
            raise WLANException(-2, "Connection failed")
    else:
        raise WLANException(-3, "Connection timed out")
    return interface.guid_string


def wireless_disconnect(interface_guid):  # Borrowed (and improved) this func from win32wifi.Win32Wifi, to avoid creting the interface when only its guid is required
    handle = ww.WlanOpenHandle()
    try:
        ww.WlanDisconnect(handle, comtypes.GUID(interface_guid))
    except Exception as e:
        raise WLANException("WlanDisconnect failed") from e
    finally:
        ww.WlanCloseHandle(handle)


def main(argv):
    if argv:
        try:
            guid = argv[0]
            print("Disconnecting wireless interface {:s} ...".format(guid))
            wireless_disconnect(guid)
        except:
            traceback.print_exc()
    else:
        try:
            print("Connecting to wireless network ...")
            ssid = "Network SSID"  # ssid and pwd here are (deliberately) dummy
            pwd = "Network password"
            guid = wireless_connect(ssid, pwd)
            print("Connected interface {:s}".format(guid))
        except:
            traceback.print_exc()


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main(sys.argv[1:])
    print("\nDone.")

script.bat

time <nul
ping www.google.com

"e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
ping www.google.com

"e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
ping www.google.com

time <nul

выход

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056721759]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> script.bat

[prompt]> time  0<nul
The current time is:  1:45:08.31
Enter the new time:
[prompt]> ping www.google.com
Ping request could not find host www.google.com. Please check the name and try again.

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32

Connecting to wireless network ...
Connected interface {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}

Done.

[prompt]> ping www.google.com

Pinging www.google.com [2a00:1450:400d:809::2004] with 32 bytes of data:
Reply from 2a00:1450:400d:809::2004: time=11ms
Reply from 2a00:1450:400d:809::2004: time=12ms
Reply from 2a00:1450:400d:809::2004: time=12ms
Reply from 2a00:1450:400d:809::2004: time=19ms

Ping statistics for 2a00:1450:400d:809::2004:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 11ms, Maximum = 19ms, Average = 13ms

[prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32

Disconnecting wireless interface {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806} ...

Done.

[prompt]> ping www.google.com
Ping request could not find host www.google.com. Please check the name and try again.

[prompt]> time  0<nul
The current time is:  1:45:12.82
Enter the new time:

Примечания

  • Чтобы создать POC , мне пришлось добавить некоторый код (например, wireless_disconnect ), не обязательно связанный с вопросом, что добавляет сложности.
    КСТАТИ , код более сложный, чем я изначально ожидал (поэтому я не удосужился объяснить это - это было бы излишним), но я не вижу никакого способа обрезать его
  • script.bat time <nul) просто для того, чтобы доказать в консоли, что код подключается / отключается от беспроводной сети (и что я не подключаюсь от Win в параллели)
    • Я не знаю, откуда берется часть " 0 " из time 0<nul (на выходе)
  • Как я указывал, это больше похоже на POC , в коде (my и Win32Wifi ) есть некоторые ограничения. Некоторые сценарии (сети) могут не работать без (небольших) изменений кода
  • Хотя подключение к сети успешно (и работает ), в системном трее 1137 * состояние сети по-прежнему отображается как отключенное (фактически в течение доли секунды оно кажется подключенным, но тогда это автоматически меняется). Кроме того, значок сети System Tray отображается как Подключено . Я не уверен, что это на моей стороне (я забыл как-то уведомить Win - хотя это не имеет особого смысла), или Win не любит "кого-то другого" «для подключения к беспроводным сетям
  • САМОЕ ВАЖНОЕ ОДИН : вышеуказанный код не будет работать OOTB , поскольку Win32Wifi глючит . Я нашел 2 ошибки, которые фатальные (критические) для этого сценария , и кучу других более мелких ошибок.
    Я только что отправил [GitHub]: kedos / win32wifi - Исправления (некоторые критические) и улучшения . Не уверен, каким будет его исход (учитывая период бездействия).

    В качестве альтернативы вы можете скачать патч и применить изменения локально. Проверьте [SO]: запускать / отлаживать UnitTests приложения Django из контекстного меню, вызываемого правой кнопкой мыши, в PyCharm Community Edition? (@ Ответ CristiFati) ( Patching utrunner section) о том, как применять патчи к Win (в основном, каждая строка, начинающаяся с one) "+" знак входит, и каждая строка, которая начинается с one "-" знак гаснет). Я использую Cygwin , кстати .

...