如何访问ctypes.LP_c_char指针的值?

时间:2016-10-11 10:38:33

标签: python ctypes

我已经定义了一个结构:

Enable Full solution analysis

结构初始化:

class FILE_HANDLE(Structure):
_fields_ = [
    ("handle_bytes", c_uint),
    ("handle_type", c_int),
    ("f_handle", POINTER(c_char))
]

我通过引用它来传递它。

buf = create_string_buffer(f_handle.handle_bytes)
fh = FILE_HANDLE(c_uint(8), c_int(0), buf)

我可以用strace检查调用是否有效,但我无法弄清楚如何访问fh.f_handle的值

fh.f_handle类型为ret = libc.name_to_handle_at(dirfd, pathname, byref(fh), byref(mount_id), flags)
fh.f_handle.contents类型是<ctypes.LP_c_char object at 0x7f1a7ca17560>但是如果我尝试访问它的值,我会得到一个SIGSEGV。
我如何从f_handle获得8个字节到字符串或数组?

2 个答案:

答案 0 :(得分:1)

所有内容实际上都适合你所展示的内容,但如果没有看到你所称的结构和功能的显式C定义,很难看出问题。

这是一个与您展示的内容相关的示例。我推断出C定义应该来自你在Python中声明的内容,但是如果你遇到段错误,很可能你的定义是不同的。

C代码(Windows)

struct FILE_HANDLE
{
    unsigned int handle_bytes;
    int handle_type;
    char* f_handle;
};

__declspec(dllexport) int name_to_handle_at(int dirfd, char* pathname, struct FILE_HANDLE* fh, int* mount_id, int flags)
{
    unsigned int i;
    printf("dirfd=%d pathname=%s fh->handle_bytes=%u fh->handle_type=%d flags=%d\n", dirfd, pathname, fh->handle_bytes, fh->handle_type, flags);
    for(i = 0; i < fh->handle_bytes; ++i)
        fh->f_handle[i] = 'A' + i;
    *mount_id = 123;
    return 1;
}

Python代码(在Python 2和3中工作):

from __future__ import print_function
from ctypes import *

class FILE_HANDLE(Structure):
    _fields_ = [("handle_bytes", c_uint),
                ("handle_type", c_int),
                ("f_handle", POINTER(c_char))]

buf = create_string_buffer(8);
fh = FILE_HANDLE(8,0,buf)
libc = CDLL('test.dll')
mount_id = c_int(0)
ret = libc.name_to_handle_at(1,b'abc',byref(fh),byref(mount_id),7)
print('mount_id =',mount_id.value)
print('fh.f_handle =',fh.f_handle[:fh.handle_bytes])

输出

dirfd=1 pathname=abc fh->handle_bytes=8 fh->handle_type=0 flags=7
mount_id = 123
fh.f_handle = b'ABCDEFGH'

请注意,由于结构被声明为指向单个字符的指针,因此打印fh.f_handle.contents只会打印b'A'。使用切片,我已经指示Python将指针索引到分配的长度。

如果这对您不起作用,请提供Minimal, Complete, and Verifiable example(正如我所知)来准确再现您的错误。

答案 1 :(得分:-1)

fh.f_handle显示为LP_c_char,因为您以这种方式定义了结构。

buf = create_string_buffer(8)
print type(buf)
fh = FILE_HANDLE(c_uint(8), c_int(0), buf)
print type(fh.f_handle)

将输出

<class 'ctypes.c_char_Array_8'>
<class 'ctypes.LP_c_char'>

您已定义结构以接受指向c_char的指针。因此,当您尝试访问fh.f_handle时,它将期望该值为包含实际单个c_char的地址的内存地址。

但是通过尝试从字符串缓冲区输入c_char * 8,它会将缓冲区的第一部分转换为指针。

Python尝试取消引用您的char [0],这意味着它将查找具有您在char [0]中定义的字符值的内存地址。该内存地址无效,因此您的解释器将发出SIGSEGV信号。

现在创建一个正确处理可变长度缓冲区的类非常困难。一个更简单的选择是将缓冲区作为不透明句柄传递,然后在需要将其转换回char数组时访问它。

示例:

class FILE_HANDLE(Structure):
    _fields_ = [
            ("handle_bytes", c_uint),
            ("handle_type", c_int),
            ("f_handle", c_void_p)
        ]

buf = create_string_buffer(8)
buf = cast(buf, c_void_p)
fh = FILE_HANDLE(c_uint(8), c_int(0), buf)
f_handle_value = (c_char * fh.handle_bytes).from_address(fh.f_handle)