python中c_char ctypes的输入类型无效

时间:2015-01-28 21:24:36

标签: python c header ctypes

在我的C头文件中,我有:

long TEST_API test( 
            _IN____ const char arg1,
            _INOUT_ char arg2[512]
    );

我已经在我的python代码中导入了ctypes,我正在传递" kcOpCode"和" szXMLAdditionalParameters"到eftUtility的功能如下:

utilityXml_bytes = bytearray(b'<xml><test>20</test></xml>')
utilityXMLparams = (ctypes.c_byte*512)(*utilityXml_bytes)

def test():
    eftUtilityRes = lib.test("s", utilityXMLparams)
    if (eftUtilityRes == 10):
        "success"
    elif(eftUtilityRes == -10):
        "type 2 error"
    else:
        "type 3 error"

但是我继续为&#34; arg1&#34;收到无效的输入类型错误。我在这做错了什么?它可能是编码问题吗?我实际上是将s传递给我的函数吗?

2 个答案:

答案 0 :(得分:1)

这是因为您没有说明第一个arg是字节字符串 - b's' vs 's'。例如

eftUtilityRes = lib.eftUtility(b"s", utilityXMLparams)

你还需要告诉ctypes该函数需要什么参数。有时候如何转换python对象会有歧义。在您的情况下,单个字符串应该被解释为charchar*(两者都是有效的解释)。

lib.eftUtility.argtypes = (ctypes.c_char, ctypes.c_char_p)

此外,您的Structure未使用且不需要,并且您创建可变字节数组的方式是冗长的。以下是所有需要的。

xml_data = b'<xml><DisplayWidthInCharacters>20</DisplayWidthInCharacters><JournalKeepDurationInDays>30</JournalKeepDurationInDays></xml>'
utilityXml = ctypes.create_string_buffer(xml_data, 512) 

def eftUtilityPepper():
    eftUtilityRes = lib.eftUtility(b"s", utilityXml)
    if (eftUtilityRes == 10):
        "success"
    elif(eftUtilityRes == -10):
        "type 2 error"
    else:
        "type 3 error"

答案 1 :(得分:1)

您的函数的签名是(const char arg1 ... ),因此您必须传递char作为第一个参数而不是字符。这意味着一个字节而不是一个char字符串。为了测试它我写了我的lib

<强> foo.c的

#include <stdio.h>
void foo(const char v)
{
    printf("Hello, I'm a shared library %c\n",v);
}

通过

编译
gcc -c -Wall -Werror -fpic foo.c
gcc -shared -o libfoo.so foo.o

现在通过python console调用它:

我的python脚本是:

>>> from ctypes import *
>>> lib = cdll.LoadLibrary("./libfoo.so")
>>> a=lib.foo(ord("s"))
Hello, I'm a shared library s
>>> a=lib.foo("s")
Hello, I'm a shared library D
>>> a=lib.foo(b"s")
Hello, I'm a shared library D

很明显,您需要的是ord("s")


还有一件事:它只是一个猜测,但也许你读到的错误来自库,它读取一个不正确的char作为第一个参数。


如果我们不使用正确的强制转换来使用ctypes来调用C库函数,那么我们可以看到很多错误的假设或愚蠢的错误。现在我在上面写的内容只是为了理解问题所在,但是在Python 2和Python 3中应该工作的真实生产代码应该声明正确的函数接口并使用b"s"作为参数:

>>> lib.foo.argtypes = (c_char,)
>>> lib.foo.restype = None
>>> lib.foo(b"s") 
Hello, I'm a shared library s 

仅供记录:

<强> [编辑]

由于@eryksun在评论中正确地指出了我非常不安全的调用C函数而没有使用正确的强制转换:它就像在C中不使用原型而一切都变成int。因此,如果你没有设置函数argtype,最好使用显式转换,如:

>>> a=lib.foo(c_char(ord("s"))) #Work Just on Python 3
Hello, I'm a shared library s
>>> a=lib.foo(c_char("s")) # Just Python 2.7
Hello, I'm a shared library s
>>> a=lib.foo(c_char(b"s")) # Python 2 and 3
Hello, I'm a shared library s

<强> [编辑]

关注您的评论:您还可以使用ord("s")或十六进制值105

0x73进行硬编码
>>> a=lib.foo(105)
Hello, I'm a shared library s
>>> a=lib.foo(0x73)
Hello, I'm a shared library s

<强> [编辑]

正如@Dunes指出的那样,有一种方法可以告诉python lib.foo.argtypes = (c_char,)隐式地如何进行转换。但是你如何通过b"s"或简单地"s"构建你的char并不会(在python 2.7上)。

>>> lib.foo.argtypes = (c_char,)
>>> a=lib.foo(b"s") 
Hello, I'm a shared library s
>>> a=lib.foo("s")  #Python 2.7
Hello, I'm a shared library s

在Python 3中,如果指定b"s"是强制性的,则使用argtypes

>>> lib.foo.argtypes = (c_char,)
>>> a=lib.foo(b"s") 
Hello, I'm a shared library s
>>> a=lib.foo("s")  #Python 3.4
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type