Python ctypes调用NI RFSA函数遇空指针错误,需获取结构体数组数据
问题解决:ctypes调用NI RFSA函数获取结构体数组数据
问题背景
调用niRFSA_FetchIQSingleRecordComplexF32时触发空指针错误:
RuntimeError: (-1074134952) IVI: (Hex 0xBFFA0058) Null pointer passed for parameter or attribute.
头文件定义
typedef struct NIComplexNumberF32_struct { ViReal32 real; ViReal32 imaginary; } NIComplexNumberF32; ViStatus _VI_FUNC niRFSA_FetchIQSingleRecordComplexF32( ViSession vi, ViConstString channelList, ViInt64 recordNumber, ViInt64 numberOfSamples, ViReal64 timeout, NIComplexNumberF32* data, niRFSA_wfmInfo* wfmInfo);
报错的Python代码
import ctypes class NIComplexNumberF32_struct_data(ctypes.Structure): _fields_ = [("real", ctypes.c_float), ("imaginary", ctypes.c_float)] class niRFSA_wfmInfo_struct_data(ctypes.Structure): _fields_ = [("absoluteInitialX", ctypes.c_double), ("relativeInitialX", ctypes.c_double), ("xIncrement", ctypes.c_double), ("actualSamples", ctypes.c_double), ("offset", ctypes.c_double), ("gain", ctypes.c_double), ("reserved1", ctypes.c_double), ("reserved2", ctypes.c_double) ] # 创建空指针,这是错误根源 data = ctypes.POINTER(NIComplexNumberF32_struct_data)() wfmInfo = ctypes.POINTER(niRFSA_wfmInfo_struct_data)() dll_path = r"C:\Program Files\IVI Foundation\IVI\bin\NiRFSA_64.dll" dll = ctypes.cdll.LoadLibrary(dll_path) # 注意:这里argtypes里的Vi类型要先定义,比如ViSession是c_longlong或c_void_p,根据实际情况 ViSession = ctypes.c_void_p ViString = ctypes.c_char_p ViInt64 = ctypes.c_longlong ViReal64 = ctypes.c_double dll.niRFSA_FetchIQSingleRecordComplexF32.argtypes =(ViSession, ViString, ViInt64, ViInt64, ViReal64, ctypes.POINTER(NIComplexNumberF32_struct_data), ctypes.POINTER(niRFSA_wfmInfo_struct_data)) dll.niRFSA_FetchIQSingleRecordComplexF32(handle, b'', 0, 1000, 1, data, wfmInfo)
错误原因
只声明了指针变量,但没有为指针分配实际的内存空间。ctypes.POINTER(...)() 创建的是空指针,函数需要一个指向已分配内存的指针来写入采样数据和波形信息。
解决步骤
- 为采样数据数组分配内存:根据传入的
numberOfSamples(这里是1000),创建对应数量的结构体实例数组,再通过ctypes.byref或取数组地址的方式传入。 - 为波形信息结构体分配内存:创建一个
niRFSA_wfmInfo_struct_data实例,传入它的指针供函数填充数据。
修正后的代码
import ctypes class NIComplexNumberF32_struct_data(ctypes.Structure): _fields_ = [("real", ctypes.c_float), ("imaginary", ctypes.c_float)] class niRFSA_wfmInfo_struct_data(ctypes.Structure): _fields_ = [("absoluteInitialX", ctypes.c_double), ("relativeInitialX", ctypes.c_double), ("xIncrement", ctypes.c_double), ("actualSamples", ctypes.c_double), ("offset", ctypes.c_double), ("gain", ctypes.c_double), ("reserved1", ctypes.c_double), ("reserved2", ctypes.c_double)] # 定义IVI类型(根据NI文档,ViSession通常是c_void_p或c_longlong,这里用c_void_p) ViSession = ctypes.c_void_p ViString = ctypes.c_char_p ViInt64 = ctypes.c_longlong ViReal64 = ctypes.c_double # 关键:分配内存 number_of_samples = 1000 # 创建结构体数组,长度为number_of_samples data_array = (NIComplexNumberF32_struct_data * number_of_samples)() # 创建波形信息结构体实例 wfm_info = niRFSA_wfmInfo_struct_data() dll_path = r"C:\Program Files\IVI Foundation\IVI\bin\NiRFSA_64.dll" dll = ctypes.cdll.LoadLibrary(dll_path) # 设置函数参数类型 dll.niRFSA_FetchIQSingleRecordComplexF32.argtypes = ( ViSession, ViString, ViInt64, ViInt64, ViReal64, ctypes.POINTER(NIComplexNumberF32_struct_data), ctypes.POINTER(niRFSA_wfmInfo_struct_data) ) # 设置返回值类型(ViStatus通常是c_long) dll.niRFSA_FetchIQSingleRecordComplexF32.restype = ctypes.c_long # 调用函数,传入数组的首地址和wfm_info的指针 status = dll.niRFSA_FetchIQSingleRecordComplexF32( handle, b'', 0, number_of_samples, 1, ctypes.byref(data_array), ctypes.byref(wfm_info) ) # 检查返回状态(非0通常表示错误) if status != 0: print(f"调用错误,状态码:{status}") else: # 提取real和imag数据 real_data = [sample.real for sample in data_array] imag_data = [sample.imaginary for sample in data_array] print(f"获取到{len(real_data)}个采样点") # 也可以直接访问单个采样 print(f"第一个采样:real={data_array[0].real}, imaginary={data_array[0].imaginary}") # 打印波形信息 print(f"实际采样数:{wfm_info.actualSamples}") print(f"X增量:{wfm_info.xIncrement}")
注意事项
- 确保
ViSession等类型的定义与NI的头文件一致,比如ViInt64对应ctypes.c_longlong,ViReal64对应ctypes.c_double,避免类型不匹配导致的错误。 - 调用函数后务必检查返回状态码,非0值表示调用失败,可以根据NI的错误码文档排查问题。
内容的提问来源于stack exchange,提问作者pyNewbie
相关产品推荐
相关产品推荐

