我尝试通过DLL(由PLC制造商分发的C API接口)与PLC通信。我正在使用Python 3.1作为脚本环境嵌入到其他软件中(x64 - Windows 7)。
我设法让一些DLL函数正常工作,但现在得到了一个“无法解决的访问违规读取”。
有关DLL函数的信息:
LONG AdsSyncReadReq(
PAmsAddr pAddr,
ULONG nIndexGroup,
ULONG nIndexOffset,
ULONG nLength,
PVOID pData
);
参数:
结构AmsAddr:
typedef struct {
AmsNetId netId;
USHORT port;
} AmsAddr, *PAmsAddr;
结构AmsNetId
typedef struct {
UCHAR b[6];
} AmsNetId, *PAmsNetId;
Python实现:
# -*- coding: utf-8 -*-
from ctypes import *
#I've tried OleDll and windll as wel..
ADS_DLL = CDLL("C:/Program Files/TwinCAT/Ads Api/TcAdsDll/x64/TcAdsDll.dll")
class AmsNetId(Structure):
_fields_ = [('NetId', c_ubyte*6)]
class AmsAddr(Structure):
_fields_=[('AmsNetId',AmsNetId),('port',c_ushort)]
# DLL function working fine
version = ADS_DLL.AdsGetDllVersion()
print(version)
#DLL function working fine
errCode = ADS_DLL.AdsPortOpen()
print(errCode)
#DLL function using the AmsAddr() class, working fine
amsAddress = AmsAddr()
pointer_amsAddress = pointer(amsAddress)
errCode = ADS_DLL.AdsGetLocalAddress(pointer_amsAddress)
print(errCode)
contents_amsAddres = pointer_amsAddress.contents
#Function that doens't work:
errCode = ADS_DLL.AdsSyncReadReq()
print(errCode) # --> errCode = timeout error, normal because I didn't pass any arguments
# Now with arguments:
plcNetId = AmsNetId((c_ubyte*6)(5,18,18,27,1,1)) #correct adress to the PLC
plcAddress = AmsAddr(plcNetId,801) #correct port to the PLC
nIndexGroup = c_ulong(0xF020)
nIndexOffset = c_ulong(0x0)
nLength = c_ulong(0x4)
data = c_void_p()
pointer_data = pointer(data)
#I tried with an without the following 2 lines, doesn't matters
ADS_DLL.AdsSyncReadReq.argtypes=[AmsAddr,c_ulong,c_ulong,c_ulong,POINTER(c_void_p)]
ADS_DLL.AdsSyncReadReq.restype=None
#This line crashes
errCode = ADS_DLL.AdsSyncReadReq(plcAddress,nIndexGroup,nIndexOffset,nLength,pointer_data)
print(errCode)
>>>> Error in line 57: exception: access violation reading 0xFFFFFFFFFFFFFFFF
我希望有人无法弄清楚出了什么问题。我只是Python编程方面的高级新手,在C
中完全没有经验提前致谢
答案 0 :(得分:2)
您正在传递无效指针,而是提供有效的内存缓冲区:
data = create_string_buffer(nLength)
如果c_void_p
表示POINTER(c_void_p)
,则参数应该只是PVOID
而不是void *
。不要将restype设置为None
(函数返回LONG
)。
同时传递pointer(plcAddress)
(在argtypes中指定POINTER(AmsAddr)
。)
使用正确的调用约定(在cdll,windll,oledll之间选择)。