Reputation: 25
In my C header file I have:
long TEST_API test(
_IN____ const char arg1,
_INOUT_ char arg2[512]
);
I have imported ctypes
in my python code and I am passing "kcOpCode
" and "szXMLAdditionalParameters
" to eftUtility
function as following:
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"
But I keep getting invalid input type error for "arg1
". what am I doing wrong here? could it be encoding problem? am I actually passing s
to my function?
Upvotes: 0
Views: 1365
Reputation: 23741
The signature of your function is (const char arg1 ... )
so you must pass a char as first argument and not a character. That means a byte and not a single char string. To test it I wrote my lib
foo.c
#include <stdio.h>
void foo(const char v)
{
printf("Hello, I'm a shared library %c\n",v);
}
And compile it by
gcc -c -Wall -Werror -fpic foo.c
gcc -shared -o libfoo.so foo.o
And now call it by python console:
My python script is:
>>> 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
It is clear that what you need is ord("s")
.
One more thing: it is just a guess but maybe the error that you read came from the library that read an incorrect char as first argument.
Follow a lot of edits where we can see how many wrong assumptions or silly errors we can do if we don't use a correct cast by use ctypes
to call a C library function. Now what I wrote above it was useful just to understand where the issue was but the real production code that should work either in Python 2 and Python 3 should declare the correct function interface and use b"s"
as argument:
>>> lib.foo.argtypes = (c_char,)
>>> lib.foo.restype = None
>>> lib.foo(b"s")
Hello, I'm a shared library s
Just for the record:
[EDIT]
As @eryksun has correctly pointed me in the comments it is very unsafe call C function without use a correct cast: it is like in C don't use a prototype and everything become int
. So if you don't set function argtype
is better use explicit cast like:
>>> 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
[EDIT]
Follow from your comment: you can also hardcode ord("s")
by use either 105
or hex value 0x73
>>> a=lib.foo(105)
Hello, I'm a shared library s
>>> a=lib.foo(0x73)
Hello, I'm a shared library s
[EDIT]
As @Dunes pointed out there is a way to tell to python how do that conversion implicitly by lib.foo.argtypes = (c_char,)
. But how you build your char by b"s"
or simply "s"
doesn't mater (on 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
In Python 3 use b"s"
if you specify argtypes
is mandatory:
>>> 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
Upvotes: 1
Reputation: 40778
It's because you're not stating that the first arg is a byte string -- b's'
vs 's'
. eg.
eftUtilityRes = lib.eftUtility(b"s", utilityXMLparams)
You also need to tell ctypes what arguments the function takes. Sometimes there is ambiguity to how a python object should be converted. In your case, is a single character string meant to be interpreted as char
or char*
(both of which would be valid interpretations).
lib.eftUtility.argtypes = (ctypes.c_char, ctypes.c_char_p)
In addition your Structure
is unused and un-needed, and your way of creating a mutable byte array is long-winded. The following is all that is needed.
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"
Upvotes: 1