I am calling a native c library using ctypes, passing in a specialiezed struct and getting back another struct. I get a seg fault when gc is called, can't find much details in gdb trace:
Thread 1 "python" received signal SIGSEGV, Segmentation fault.
0x00007ffff7a5ca44 in visit_decref (op=0xf5ee10, data=0x0) at Modules/gcmodule.c:374
374 Modules/gcmodule.c: No such file or directory.
gdb) where
#0 0x00007ffff7a5ca44 in visit_decref (op=0xf5ee10, data=0x0) at Modules/gcmodule.c:374
#1 0x00007ffff798bb0c in subtype_traverse (self=0x7fff7744fd90, visit=0x7ffff7a5ca40 <visit_decref>, arg=0x0) at Objects/typeobject.c:1011
#2 0x00007ffff7a5bd77 in subtract_refs (containers=<optimized out>) at Modules/gcmodule.c:399
#3 collect (generation=generation#entry=0, n_collected=n_collected#entry=0x7fffffffb708, n_uncollectable=n_uncollectable#entry=0x7fffffffb710, nofail=nofail#entry=0) at Modules/gcmodule.c:956
#4 0x00007ffff7a5c95d in collect_with_callback (generation=0) at Modules/gcmodule.c:1128
#5 0x00007ffff7a5d1eb in collect_generations () at Modules/gcmodule.c:1151
#6 _PyObject_GC_Alloc (basicsize=<optimized out>, use_calloc=0) at Modules/gcmodule.c:1726
#7 _PyObject_GC_Malloc (basicsize=<optimized out>) at Modules/gcmodule.c:1736
The code sample is this:
_C_DOUBLE_P = POINTER(c_double)
_C_INT_P = POINTER(c_int)
class _IN_DATA(Structure):
_fields_ = [('in_kps', _C_DOUBLE_P),
('in_desc', _C_DOUBLE_P)
]
def __init__(self):
t = c_int
self.test = ctypes.cast(t, POINTER(c_int))
class _OUT_DATA(Structure):
_fields_ = [
('num_out_kps', ctypes.c_int),
('out_kps', _C_DOUBLE_P),
('out_desc', _C_DOUBLE_P)
]
class _IN_DATA_LIST(ctypes.Structure):
_fields_ = [
('num_crops', c_int),
('crops', POINTER(_IN_DATA))
]
def __init__(self, crops_raw_kps: List[np.ndarray], crops_raw_descriptors: List[np.ndarray]):
num_crops = len(crops_raw_kps)
self.num_crops = num_crops
crops = (POINTER(_IN_DATA) * num_crops)()
self.crops = ctypes.cast(crops, POINTER(_IN_DATA))
for i in range(num_crops):
self.crops[i].in_kps = crops_raw_kps[i].ctypes.data_as(_C_DOUBLE_P)
self.crops[i].in_desc = crops_raw_descriptors[i].ctypes.data_as(_C_DOUBLE_P)
class _OUT_DATA_LIST(ctypes.Structure):
_fields_ = [
('crops_data', ctypes.POINTER(_OUT_DATA)),
('num_results', c_int)
]
class SPPostWrapper:
def __init__(self):
self._post_processor_lib = ctypes.cdll.LoadLibrary("multitracker/custom_features/build/ffme/libffme.so")
self._post_processor_lib.py_postprocess.restype = _OUT_DATA_LIST
self._post_processor_lib.py_postprocess.argtypes = [POINTER(_IN_DATA_LIST)]
def post_process_multi(self, crops_raw_kps: List[np.ndarray], crops_raw_descriptors: List[np.ndarray]):
num_crops = len(crops_raw_kps)
adjusted_kps = [np.asarray(np.squeeze(kp), np.double) for kp in crops_raw_kps]
adjusted_desc = [np.asarray(np.squeeze(desc), np.double) for desc in crops_raw_descriptors]
crops_struct = _IN_DATA_LIST(adjusted_kps, adjusted_desc)
out_result= self._post_processor_lib.py_postprocess(ctypes.byref(crops_struct))
I allocate the IN_DATA in python, and the OUT_DATA is allocated in the c code , I tried using cache in c (assuming python will not clean the memory) or allocating new memory for each call (assuming python does free the out_data memory) - both methods fail when gc of python is called.
Update:
To better isolate the problem I removed the usage of out_data, setting the method to void, and still the problem happens. I also tried keeping the in data as a member and i think this prevents the data from happening, until the process shutdown. So it must be something to do with the memory I allocated to input/input list.
Update 2:
I was able to verify that the problem only happens if I pass more than 1 item in the IN_DATA_LIST (more than 1 crop). This is very strange indeed...
After validating that the problem happens even if the c code does nothing and returns nothing, and not being able to solve it, I ended up using a more shallow input (and output) , this seems to run ok, without seg faults:
class _IN_DATA(Structure):
_fields_ = [
('num_crops', c_int),
('in_kps', _C_DOUBLE_P),
('in_desc', _C_DOUBLE_P)
]
def __init__(self, kps_flat, desc_falt, num_crops):
self.num_crops = num_crops
self.in_kps = kps_flat.ctypes.data_as(_C_DOUBLE_P)
self.in_desc = desc_falt.ctypes.data_as(_C_DOUBLE_P)
class _OUT_DATA(Structure):
_fields_ = [
('num_results', ctypes.c_int),
('results_per_crop', _C_INT_P),
('out_kps', _C_DOUBLE_P), # x,y, score
('out_desc', _C_DOUBLE_P)
]
Related
def writepmc(self, type_a, type_d, datano_s, datano_e, value):
# datano_s & datano_e = start & end number
length = 8 + (datano_e - datano_s + 1)
# c union datatype
class UnionC(ctypes.Union):
_fields_ = [
('cdata[N]', ctypes.c_char),
('idata[N]', ctypes.c_short),
('ldata[N]', ctypes.c_long),
('fdata[N]', ctypes.c_float),
('dfdata[N]', ctypes.c_double),
]
union = UnionC()
union.ldata[0] = value
class Iodbpmc(ctypes.Structure):
_fields_ = [("type_a", ctypes.c_short),
("type_d", ctypes.c_short),
("datano_s", ctypes.c_short),
("datano_e", ctypes.c_short),
("u", union)]
iodbpmc = Iodbpmc()
iodbpmc.type_a = type_a
iodbpmc.type_d = type_d
iodbpmc.datano_s = datano_s
iodbpmc.datano_e = datano_e
ret = focas.pmc_wrpmcrng(libh, length, ctypes.byref(iodbpmc))
self.wrpmc_errorcheck(ret)
return ret
I'm working on a Universal Robot and have to use Python 2.7. I am also using the CTypes lib. I keep getting the error in the title and am not sure what I'm doing wrong.
I don't have a lot of experience with CTypes or OOP in Python. Does anyone know what's causing the error?
I managed to solve the issue myself.
I wrote:
class UnionC(ctypes.Union):
_fields_ = [
('cdata', ctypes.c_char),
('idata', ctypes.c_short),
('ldata', ctypes.c_long),
('fdata', ctypes.c_float),
('dfdata', ctypes.c_double),
]
And I removed :
union = UnionC()
union.ldata[0] = value
These changes solved the problem for me. I'm pretty sure the line two lines above were causing the issue.
I am trying to get the SendInput function from user32.dll to work in python using ctypes.I am a noob but from what I read from the docs you have to create the structs the function requires in python and then pass it to the function.
import ctypes
import keyboard
from ctypes import *
lib = windll.user32
KEYEVENTF_SCANCODE = 0x8
KEYEVENTF_KEYUP = 0x2
SPACEBAR = 57 # 0x39
INPUT_KEYBOARD = 1
class KEYBDINPUT(Structure):
_fields_ = [('wVk' , c_ushort) , ('wScan' , c_ushort)
, ('dwFlags' , c_ulong) , ('time' , c_ulong) , ('dwExtraInfo' , c_ulong)]
class INPUT(Structure):
_fields_ = [('type' , c_ulong) ,('ki' , KEYBDINPUT)]
lib.SendInput.restype = c_uint
lib.SendInput.argtypes = [c_uint , INPUT , c_int]
keybdinput_obj = KEYBDINPUT(0 , SPACEBAR , KEYEVENTF_SCANCODE , 0 , 0)
input_obj = INPUT(INPUT_KEYBOARD , keybdinput_obj)
keyboard.wait('u')
lib.SendInput(1 , byref(input_obj) , sizeof(INPUT))
keybdinput_obj = KEYBDINPUT(0 , SPACEBAR , KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP , 0 , 0)
input_obj = INPUT(INPUT_KEYBOARD , keybdinput_obj)
lib.SendInput(1 , byref(input_obj) , sizeof(INPUT))
In the microsoft docs at which I guided myself from the INPUT struct had an union but i figured if I would only need the KEYBDINPUT then it's the same thing as if i had an union.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
I pretty much got stuck because i can't see what's going wrong here so I am asking for help.The program is supposed to send a space after i press 'u' on the keyboard (this was for debugging purposes) and i do it this way because i want it to send as a scancode instead of a virtual keypress.
So if there is another way in python with with which you can send scancodes , that'll work and I would appreciate it a lot .
Define the whole union, or at least MOUSEINPUT, which is the largest member of the union. You can test that your definition is the correct size by print(sizeof(INPUT)) and it should agree with printing the size of an INPUT structure in a C program. I got size 28 for 32-bit and 40 for 64-bit C structure.
Also, your second parameter to SendInput is POINTER(INPUT), not INPUT, and ULONG_PTR is not necessarily c_ulong because it depends on running 32-bit or 64-bit Python.
Here's a tested example:
import ctypes
from ctypes import *
from ctypes import wintypes as w
KEYEVENTF_SCANCODE = 0x8
KEYEVENTF_UNICODE = 0x4
KEYEVENTF_KEYUP = 0x2
SPACE = 0x39
INPUT_KEYBOARD = 1
# not defined by wintypes
ULONG_PTR = c_ulong if sizeof(c_void_p) == 4 else c_ulonglong
class KEYBDINPUT(Structure):
_fields_ = [('wVk' ,w.WORD),
('wScan',w.WORD),
('dwFlags',w.DWORD),
('time',w.DWORD),
('dwExtraInfo',ULONG_PTR)]
class MOUSEINPUT(Structure):
_fields_ = [('dx' ,w.LONG),
('dy',w.LONG),
('mouseData',w.DWORD),
('dwFlags',w.DWORD),
('time',w.DWORD),
('dwExtraInfo',ULONG_PTR)]
class HARDWAREINPUT(Structure):
_fields_ = [('uMsg' ,w.DWORD),
('wParamL',w.WORD),
('wParamH',w.WORD)]
class DUMMYUNIONNAME(Union):
_fields_ = [('mi',MOUSEINPUT),
('ki',KEYBDINPUT),
('hi',HARDWAREINPUT)]
class INPUT(Structure):
_anonymous_ = ['u']
_fields_ = [('type',w.DWORD),
('u',DUMMYUNIONNAME)]
print(sizeof(INPUT))
lib = WinDLL('user32')
lib.SendInput.argtypes = w.UINT,POINTER(INPUT),c_int
lib.SendInput.restype = w.UINT
def send_scancode(code):
i = INPUT()
i.type = INPUT_KEYBOARD
i.ki = KEYBDINPUT(0,code,KEYEVENTF_SCANCODE,0,0)
lib.SendInput(1,byref(i),sizeof(INPUT))
i.ki.dwFlags |= KEYEVENTF_KEYUP
lib.SendInput(1,byref(i),sizeof(INPUT))
def send_unicode(s):
i = INPUT()
i.type = INPUT_KEYBOARD
for c in s:
i.ki = KEYBDINPUT(0,ord(c),KEYEVENTF_UNICODE,0,0)
lib.SendInput(1,byref(i),sizeof(INPUT))
i.ki.dwFlags |= KEYEVENTF_KEYUP
lib.SendInput(1,byref(i),sizeof(INPUT))
send_scancode(SPACE)
send_unicode('The quick brown fox jumped over the lazy dog')
Output on terminal running 64-bit Python 3.6:
C:\>example
40
C:\> The quick brown fox jumped over the lazy dog
I am having trouble retrieving memory usage of my system using psutil.
When I run my code it retrieves the cpu usage correctly, but when it tries to get the memory it throws an AccessDenied error.
code:
if(pi['name']=='java.exe'):
pro=psutil.Process(0)
cpu=pro.cpu_percent(1)
memory=pro.memory_full_info().uss/(1024*1024)
return memory,cpu
Error message:
psutil.AccessDenied (pid=0)
How can i get memory usage?
psutil.Process() takes one argument - a process identifier (PID). If it is called without an argument or with None, then the pid of the current process is used.
On Window, pid 0 is the The System Idle Process, which requires SYSTEM privileges.
pro=psutil.Process(0) # here's your problem
If you want to get the memory/cpu usage of java.exe, you'll have to get the pid, then use psutil. In the example below, I have used firefox.exe:
import psutil
from ctypes import *
from ctypes.wintypes import *
class PROCESSENTRY32W(Structure):
_fields_ = [("dwSize", DWORD),
("cntUsage", DWORD),
("th32ProcessID", DWORD),
("th32DefaultHeapID", POINTER(ULONG)),
("th32ModuleID", DWORD),
("cntThreads", DWORD),
("th32ParentProcessID", DWORD),
("pcPriClassBase", LONG),
("dwFlags", DWORD),
("szExeFile", c_wchar * MAX_PATH)]
def get_processes():
TH32CS_SNAPPROCESS = 0x00000002
INVALID_HANDLE_VALUE = -1
CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot
CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD]
CreateToolhelp32Snapshot.restype = HANDLE
Process32FirstW = windll.kernel32.Process32FirstW
Process32FirstW.argtypes = [HANDLE, POINTER(PROCESSENTRY32W)]
Process32FirstW.restype = BOOL
Process32NextW = windll.kernel32.Process32NextW
Process32NextW.argtypes = [HANDLE, POINTER(PROCESSENTRY32W)]
Process32NextW.restype = BOOL
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [HANDLE]
CloseHandle.restype = BOOL
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
if snapshot == INVALID_HANDLE_VALUE:
return []
try:
process_entry = PROCESSENTRY32W()
process_entry.dwSize = sizeof(PROCESSENTRY32W)
if not Process32FirstW(snapshot, byref(process_entry)):
return []
processes = []
while Process32NextW(snapshot, byref(process_entry)):
processes.append((process_entry.th32ProcessID, process_entry.szExeFile))
finally:
CloseHandle(snapshot)
#print("Closed handle")
return processes
for pid, name in get_processes():
if name == "firefox.exe":
pro = psutil.Process(pid)
cpu = pro.cpu_percent(1.0)
memory = pro.memory_full_info().uss/(1024*1024)
print(memory, cpu)
Output:
471.2890625 1.6
127.15234375 25.0
18.50390625 0.0
668.09375 0.0
706.6875 0.0
512.671875 0.0
648.953125 4.7
1037.0859375 4.7
1160.1015625 98.4
337.46484375 0.0
1418.72265625 1.6
0.90625 0.0
0.90625 0.0
I've got some troubles with using "GetExtendedTcpTable". When I tried to run my script, i've got message like this:
AssertionError: [Error 0] The operation completed successfully
Rarely script working normally, I dont understand this message, Operation completed, what`s wrong?
This is code, i tried to execute:
from ctypes import *
from ctypes.wintypes import *
from socket import inet_aton, inet_ntoa, htons
AF_INET = 2
TCP_TABLE_BASIC_LISTENER = 0
TCP_TABLE_BASIC_CONNECTIONS = 1
TCP_TABLE_BASIC_ALL = 2
TCP_TABLE_OWNER_PID_LISTENER = 3
TCP_TABLE_OWNER_PID_CONNECTIONS = 4
TCP_TABLE_OWNER_PID_ALL = 5
TCP_TABLE_OWNER_MODULE_LISTENER = 6
TCP_TABLE_OWNER_MODULE_CONNECTIONS = 7
TCP_TABLE_OWNER_MODULE_ALL = 8
# for storing socket info python style.
class socket_info:
State = None
LocalAddr = None
LocalPort = None
RemoteAddr = None
RemotePort = None
def __init__ (self, **kwargs):
for key, word in kwargs.items():
setattr(self, key, word)
def formatip (ip):
ip = inet_aton (str(ip))
return inet_ntoa (ip[::-1])
states = {
1 : "TCP_STATE_CLOSED",
2 : "TCP_STATE_LISTEN",
3 : "TCP_STATE_SYN_SENT",
4 : "TCP_STATE_SYN_RCVD",
5 : "TCP_STATE_ESTAB",
6 : "TCP_STATE_FIN_WAIT",
7 : "TCP_STATE_FIN_WAIT2",
8 : "TCP_STATE_CLOSE_WAIT",
9 : "TCP_STATE_CLOSING",
10 : "TCP_STATE_LAST_ACK",
11 : "TCP_STATE_TIME_WAIT",
12 : "TCP_STATE_DELETE_TCB",
"TCP_STATE_CLOSED" : 1,
"TCP_STATE_LISTEN" : 2,
"TCP_STATE_SYN_SENT" : 3,
"TCP_STATE_SYN_RCVD" : 4,
"TCP_STATE_ESTAB" : 5,
"TCP_STATE_FIN_WAIT" : 6,
"TCP_STATE_FIN_WAIT2" : 7,
"TCP_STATE_CLOSE_WAIT" : 8,
"TCP_STATE_CLOSING" : 9,
"TCP_STATE_LAST_ACK" :10,
"TCP_STATE_TIME_WAIT" : 11,
"TCP_STATE_DELETE_TCB" : 12 }
class MIB_TCPROW_OWNER_PID(Structure):
_fields_ = [
("dwState", DWORD),
("dwLocalAddr", DWORD),
("dwLocalPort", DWORD),
("dwRemoteAddr", DWORD),
("dwRemotePort", DWORD),
("dwOwningPid", DWORD)
]
class MIB_TCPTABLE_OWNER_PID(Structure):
_fields_ = [
("dwNumEntries", DWORD),
("MIB_TCPROW_OWNER_PID", MIB_TCPROW_OWNER_PID * 100)
]
def GetExtendedTcpTable (vip=AF_INET):
table = MIB_TCPTABLE_OWNER_PID ()
so = sizeof (table)
size = DWORD (so)
order = c_int(1)
failure= windll.iphlpapi.GetExtendedTcpTable (
byref (table),
addressof (size),
order,
vip,
TCP_TABLE_OWNER_PID_ALL,
0 )
assert not failure, WinError (GetLastError ())
pytables = []
tables = table.MIB_TCPROW_OWNER_PID
for index in range(table.dwNumEntries):
table = tables [index]
pytables.append (
socket_info (
State=states.get (table.dwState, "UNKNOWN_STATE_%s" %(str(table.dwState))),
LocalAddr=formatip (table.dwLocalAddr),
LocalPort=htons(table.dwLocalPort),
RemoteAddr=formatip (table.dwRemoteAddr),
RemotePort=htons(table.dwRemotePort),
OwningPid = int (table.dwOwningPid)
)
)
return pytables
def GetTcpTableForPid (pid):
tables = GetExtendedTcpTable ()
for table in tables:
if table.OwningPid == pid: return table
raise "Cannot find tcp table for pid %s" %pid
dict_process = {}
pid_set =set()
pid_list = []
tcp_info_list = []
tcp_info = GetExtendedTcpTable()
for item in tcp_info:
LocalAddr = item.LocalAddr
LocalPort = item.LocalPort
RemoteAddr = item.RemoteAddr
RemotePort = item.RemotePort
OwningPid = item.OwningPid
print('local Addr: '+ LocalAddr,'local port: '+ str(LocalPort),'remote Addr: ' + RemoteAddr, 'Remote Port: ' + str(RemotePort), OwningPid)
The script is run from time to time. It can run for 5 minutes and then don't work about an hour with this stupid mistake. How to get around it?
I really dont know, what's with it. Please, help me, what i do wrong?
I use python 3.2 on Win7 SP1 x64
Thank you a lot!
You shouldn't use addressof(size). That returns a Python integer which will be cast as a 32-bit C int. Use byref(size) to create a pointer, which will be a 64-bit value if you're using 64-bit Python.
GetExtendedTcpTable doesn't call SetLastError. It returns a DWORD with one of the following codes:
NO_ERROR = 0
ERROR_INVALID_PARAMETER = 87
ERROR_INSUFFICIENT_BUFFER = 122
The pdwSize argument has the required size if the buffer was too small. One option here is to start with a length 0 array; then resize the struct; and finally cast the array to the correct size:
class MIB_TCPTABLE_OWNER_PID(Structure):
_fields_ = [
("dwNumEntries", DWORD),
("MIB_TCPROW_OWNER_PID", MIB_TCPROW_OWNER_PID * 0),
]
_GetExtendedTcpTable = windll.iphlpapi.GetExtendedTcpTable
def GetExtendedTcpTable(vip=AF_INET):
table = MIB_TCPTABLE_OWNER_PID()
size = DWORD()
order = 1
failure = _GetExtendedTcpTable(
byref(table),
byref(size),
order,
vip,
TCP_TABLE_OWNER_PID_ALL,
0)
if failure == ERROR_INSUFFICIENT_BUFFER:
resize(table, size.value)
memset(byref(table), 0, sizeof(table))
failure = _GetExtendedTcpTable(
byref(table),
byref(size),
order,
vip,
TCP_TABLE_OWNER_PID_ALL,
0)
if failure:
raise WinError(failure)
ptr_type = POINTER(MIB_TCPROW_OWNER_PID * table.dwNumEntries)
tables = cast(table.MIB_TCPROW_OWNER_PID, ptr_type)[0]
pytables = []
for table in tables:
# rest unchanged
Regarding the Win32 LastError value, in general you shouldn't rely on GetLastError in Python. You don't know if you're seeing an old error code from a previous call or if an intervening call modified the LastError value. If you're checking a single API call that uses LastError, then it should be OK to check GetLastError immediately afterward if the call failed. But more generally you may need to load the DLL with use_last_error=True:
iphlpapi = WinDLL('iphlpapi', use_last_error=True)
Function pointers created from this WinDLL instance will save LastError to thread local storage immediately after the call returns. Calling get_last_error returns the saved error code. Beforehand you can call set_last_error(0) to have 0 swapped in to LastError before the function is called.
I'm working on a captive portal projet on Windows. I've written this piece of code (similar to this) :
from ctypes import wintypes
import ctypes
WlanApi = ctypes.windll.wlanapi
hClientHandle = wintypes.HANDLE()
phClientHandle = ctypes.pointer(hClientHandle)
dwNegotiatedVersion = wintypes.DWORD()
pdwNegotiatedVersion = ctypes.pointer(dwNegotiatedVersion)
dwClientVersion = wintypes.DWORD()
dwClientVersion.value = 2L
rc = WlanApi.WlanOpenHandle(dwClientVersion, None, pdwNegotiatedVersion, phClientHandle)
print rc
class GUID(ctypes.Structure):
_fields_ = [("Data1", wintypes.DWORD),
("Data2", wintypes.WORD),
("Data3", wintypes.WORD),
("Data4", wintypes.BYTE * 8)]
class WLAN_INTERFACE_INFO (ctypes.Structure):
_fields_ = [('InterfaceGuid', GUID),
('strInterfaceDescription', wintypes.WCHAR * 256),
('isState', wintypes.????)]
class WLAN_INTERFACE_INFO_LIST(ctypes.Structure):
_fields_ = [('dwNumberOfItems', wintypes.DWORD),
('dwIndex', wintypes.DWORD),
('InterfaceInfo', WLAN_INTERFACE_INFO * 10)]
IfList = WLAN_INTERFACE_INFO_LIST()
pIfList = ctypes.pointer(IfList)
rc = WlanApi.WlanEnumInterfaces(hClientHandle, None, pIfList)
print rc
print "Num Entries: %s" % IfList.dwNumberOfItems
I can't find how to structure "WLAN_INTERFACE_STATE enumeration" and when I try with a WCHAR array or anything else, this script return my 6000000 wireless interfaces !!!
Can somebody help me?
It's just an integer, there is no structure 0 = Not ready, 1 = connected etc.
Hmm it starts to make sense,as most of these structs have a corresponding pointer.
According to the boys at PInvoke
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WLAN_INTERFACE_INFO
{
/// GUID->_GUID
public Guid InterfaceGuid;
/// WCHAR[256]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strInterfaceDescription;
/// WLAN_INTERFACE_STATE->_WLAN_INTERFACE_STATE
public WLAN_INTERFACE_STATE isState;
}
Where WLAN_INTERFACE_STATE is
public enum WLAN_INTERFACE_STATE
{
wlan_interface_state_not_ready = 0,
...
// 1 to 6
...
wlan_interface_state_authenticating = 7,
}
PInvoke on WLAN...