Python和Ctypes:没有得到预期的偏移

时间:2016-08-09 23:18:45

标签: python ctypes

我正在将一个应用程序从VB.NET转换为Python 3.4,并且遇到了使用ctypes调用DLL文件中的函数的问题。对于该特定函数,两个结构由inref传递。对于一种结构,字段偏移不能根据需要进行。最后一个字段的偏移最终关闭。这使得该结构中的所有字段都具有不正确的值。

对于下面的PPNChartList结构,字段偏移量应为0,8,12和76(基于工作的VB.NET代码),但最终为0,8,12和72.那么,我该如何将最后一个字段移位或以其他方式获得该结构的正确值?

任何指导都将不胜感激。

以下是Python代码:

import os
from ctypes import *

_sFile = 'ppn.dll'
_sPath = os.path.join(*(os.path.split(__file__)[:-1] + (_sFile,)))
_ppn =  windll.LoadLibrary(_sPath)

class PPNChartSpec(Structure):
    _fields_ = [("dStructVer", c_double),
                ("iNumLanes", c_long),
                ("iNumCars", c_long),
                ("iNumRounds", c_long),
                ("iOptHeatCountEven", c_long),
                ("iOptAvoidConsecRaces", c_long),
                ("iOptAvoidRepeatLanes", c_long)]

class PPNChartList(Structure):  # This is the problem structure
    _fields_ = [("dStructVer", c_double),
                ("iUsedAlts", c_long),
                ("aiChartType", c_long * 15),
                ("audtChartSpec", PPNChartSpec * 15)] # The offset for this last field is off by 4

class PPNChart(Structure):
    _fields_ = [("dStructVer", c_double),
                ("udtSpec", PPNChartSpec),
                ("iChartType", c_long),
                ("iNumLanes", c_long),
                ("iNumHeats", c_long),
                ("aiCar", c_long * 2399)]

makePPNChart = _ppn.makePPNChart
ptrChartSpec = POINTER(PPNChartSpec)
ptrChart = POINTER(PPNChart)
makePPNChart.argtypes = [ptrChartSpec, ptrChart]
makePPNChart.restype = c_int

altCharts = _ppn.altCharts
ptrChartSpec = POINTER(PPNChartSpec)
ptrChartList = POINTER(PPNChartList)
altCharts.argtypes = [ptrChartSpec, ptrChartList]
altCharts.restype = c_int

编辑:如果它有帮助,下面是显示结构和函数定义的工作VB.NET代码的适用部分。这显示了结构的适当偏移。在Python中,我能够调用makePPNChart()函数并将它传递给它需要的两个结构byref。结构应该回归。所以,我在Python中部分工作。

<StructLayout(LayoutKind.Explicit)> _
Friend Structure PPNChartSpec
  <FieldOffset(0)> Dim dStructVer As Double
  <FieldOffset(8)> Dim iNumLanes As Integer
  <FieldOffset(12)> Dim iNumCars As Integer
  <FieldOffset(16)> Dim iNumRounds As Integer
  <FieldOffset(20)> Dim iOptHeatCountEven As Integer
  <FieldOffset(24)> Dim iOptAvoidConsecRaces As Integer
  <FieldOffset(28)> Dim iOptAvoidRepeatLanes As Integer
End Structure

<StructLayout(LayoutKind.Explicit)> _
Friend Structure PPNChartList
  <FieldOffset(0)> Dim dStructVer As Double
  <FieldOffset(8)> Dim iUsedAlts As Integer
  <FieldOffset(12)> <MarshalAs(UnmanagedType.ByValArray, SizeConst:=15)> _
  Dim aiChartType() As Integer
  <FieldOffset(76)> <MarshalAs(UnmanagedType.ByValArray, SizeConst:=15)> _
  Dim audtChartSpec() As PPNChartSpec
End Structure

<StructLayout(LayoutKind.Explicit)> _
Friend Structure PPNChart
  <FieldOffset(0)> Dim dStructVer As Double
  <FieldOffset(8)> Dim udtSpec As PPNChartSpec
  <FieldOffset(40)> Dim iChartType As Integer
  <FieldOffset(44)> Dim iNumLanes As Integer
  <FieldOffset(48)> Dim iNumHeats As Integer
  <FieldOffset(52)> <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2399)> _
  Dim aiCar() As Integer
End Structure

<DllImport("ppn.dll")> _
Friend Shared Function makePPNChart(<MarshalAs(UnmanagedType.Struct)> ByRef spec As PPNChartSpec, <MarshalAs(UnmanagedType.Struct)> ByRef Chart As PPNChart) As Short
End Function

<DllImport("ppn.dll")> _
Friend Shared Function altCharts(<MarshalAs(UnmanagedType.Struct)> ByRef spec As PPNChartSpec, <MarshalAs(UnmanagedType.Struct)> ByRef List As PPNChartList) As Short
End Function
编辑#2:我使用下面的循环检查偏移量。在该结构的定义中添加一个额外的c_long字段,我认为这会使事情恰到好处。但是,如下所示,audtChartSpec结构的偏移量跳至80,而不是预期的76.

for f, t in ppn.PPNChartList._fields_:
    a = getattr(ppn.PPNChartList, f)
    print(f, a)

dStructVer <Field type=c_double, ofs=0, size=8>
iUsedAlts <Field type=c_long, ofs=8, size=4>
aiChartType <Field type=c_long_Array_15, ofs=12, size=60>
unknown <Field type=c_long, ofs=72, size=4>
audtChartSpec <Field type=PPNChartSpec_Array_15, ofs=80, size=480>

1 个答案:

答案 0 :(得分:0)

12 + 15 * 4 72,因此VB.NET结构跳过这4个字节,可能是为了保证数组为空终止。在不知道被调用库的实际结构定义的情况下,很难说。您可以添加一个额外的4字节填充字段。

要控制成员的对齐方式,您可以设置_pack_ = 4以使它们对齐4字节(the equivalent of #pragma pack(4)):

class PPNChartList(Structure):
    _pack_ = 4
    _fields_ = [("dStructVer", c_double),
                ("iUsedAlts", c_long),
                ("aiChartType", c_long * 15),
                ("padding", c_long),
                ("audtChartSpec", PPNChartSpec * 15)]

现在你应该得到预期的结果,至少在c_long是32位的系统上。