C struct => ctypes struct ...这个映射是否正确?

时间:2012-03-22 17:22:29

标签: python struct ctypes

我正在尝试从Python中访问两个用C语言编写的传统解压缩函数,这些函数目前可以通过DLL获得(我有C源代码)。

函数传递(部分)填充的C结构,并使用此信息压缩或解压缩提供的缓冲区中的数据。

这是函数的调用方式。我为Python兼容性添加了__cdecl。

// Both functions return 0 on success and nonzero value on failure
int __cdecl pkimplode(struct pkstream *pStr);
int __cdecl pkexplode(struct pkstream *pStr);

这是C:中定义的pkstream结构

struct pkstream {
   unsigned char *pInBuffer;           // Pointer to input buffer
   unsigned int nInSize;               // Size of input buffer
   unsigned char *pOutBuffer;          // Pointer to output buffer
   unsigned int nOutSize;              // Size of output buffer upon return
   unsigned char nLitSize;             // Specifies fixed or var size literal bytes
   unsigned char nDictSizeByte;        // Dictionary size; either 1024, 2048, or 4096
   // The rest of the members of this struct are used internally,
   // so setting these values outside pkimplode or pkexplode has no effect
   unsigned char *pInPos;              // Current position in input buffer
   unsigned char *pOutPos;             // Current position in output buffer
   unsigned char nBits;                // Number of bits in bit buffer
   unsigned long nBitBuffer;           // Stores bits until enough to output a byte
   unsigned char *pDictPos;            // Position in dictionary
   unsigned int nDictSize;             // Maximum size of dictionary
   unsigned int nCurDictSize;          // Current size of dictionary
   unsigned char Dict[0x1000];         // Sliding dictionary used for compdecomp
};

这是我在Python中镜像这个结构的尝试。

# Define the pkstream struct
class PKSTREAM(Structure):
   _fields_ = [('pInBuffer', c_ubyte),
               ('nInSize', c_uint),
               ('pOutBuffer', c_ubyte),
               ('nOutSize', c_uint),
               ('nLitSize', c_ubyte),
               ('nDictSizeByte', c_ubyte),
               ('pInPos', c_ubyte),
               ('pOutPos', c_ubyte),
               ('nBits', c_ubyte),
               ('nBitBuffer', c_ulong),
               ('pDictPos', c_ubyte),
               ('nDictSize', c_uint),
               ('nCurDictSize', c_uint),
               ('Dict', c_ubyte)]

我真的很感谢以下问题提供了一些帮助(我选择在前端提问,而不仅仅是“wing”它,希望显而易见的原因):

  1. 我不确定是否将c_ubyte,c_char或c_char_p用于 unsigned char 类型的成员。 c_ubyte最接近地映射到unsigned char的ctypes(至少根据文档),但实际上是?int / long?在Python中。

  2. 有时成员是一个指针到一个unsigned char ...这会映射到c_char_p吗? ctypes docs说ALL byte& unicode字符串无论如何都作为指针传递,那么我需要为此做些什么规定呢?

  3. 我需要为函数提供pOutBuffer,该函数应该是指向函数可以复制de /压缩数据的已分配内存位置的指针。我相信我应该使用create_string_buffer()为它创建一个适当大小的缓冲区?

  4. 我还需要知道如何定义成员 Dict [0x1000] ,它看起来(对我来说)创建一个4096字节的缓冲区供内部使用。我知道我的定义显然是错误的,但不知道应该如何定义?

  5. C函数是否应该被装饰为__stdcall或__cdecl? (我已经在一些测试DLL上使用了后者,因为到目前为止我一直在努力)。

  6. 任何反馈都会非常感激!

    提前致谢,

    詹姆斯

1 个答案:

答案 0 :(得分:2)

如果结构中的数据是指针,则必须将其声明为Python端的指针。

这样做的一种方法是在ctypes中使用POINTER实用程序 - 它是一个比ctypes.c_char_p更高级别的对象(并且与此不完全兼容) - 但是您的代码将变为更具可读性。此外,为了模拟C数组,基本ctypes类型可以乘以标量,返回的对象是可以用作相同大小的基本类型的C向量的对象 - (因此Dict字段可以定义为吼叫,c_ubyte * 4096

请注意,虽然char相当于c_ubyte,但int相当于c_int而非c_uint,同样适用于long。< / p>

您的结构定义未声明指向的缓冲区为const。如果你传递一个python字符串(不可变),你的库试图改变它,你将得到错误。相反,你应该传递从create_string_buffer返回的可变内存,由你的字符串初始化。

POINTER = ctypes.POINTER
# Define the pkstream struct
class PKSTREAM(Structure):
   _fields_ = [('pInBuffer', POINTER(c_char)),
               ('nInSize', c_int),
               ('pOutBuffer', POINTER(c_char)),
               ('nOutSize', c_int),
               ('nLitSize', c_char),
               ('nDictSizeByte', c_char),
               ('pInPos', POINTER(c_char)),
               ('pOutPos', POINTER(c_char)),
               ('nBits', c_char),
               ('nBitBuffer', c_long),
               ('pDictPos', POINTER(c_char)),
               ('nDictSize', c_int),
               ('nCurDictSize', c_int),
               ('Dict', c_char * 0x1000)]

至于(5),我不知道你应该如何装饰你的C函数 - 使用任何有用的东西。