I'm trying to open a dll written in c with python ctypes and run the function in it, but it comes as a char, not a string

Viewed 180

These are the codes I am working on:

#include <stdio.h>

void test_print(char test[100])
{
        printf("%s", test);
}

from ctypes import *
libcdll = CDLL("test.dll")

libcdll.test_print("test")

But when i run the program i get "t" instead of "test".

1 Answers

ALWAYS set .argtypes and .restype for your functions to save headache. ctypes can verify the arguments are passed correctly.

Example:

test.c

#include <stdio.h>

__declspec(dllexport)        // required for exporting a function on Windows
void test_print(char* test)  // decays to pointer, so char test[100] is misleading.
{
        printf("%s", test);
}

test.py

from ctypes import *
libcdll = CDLL("./test")
libcdll.test_print.argtypes = c_char_p,  # for (char*) arguments, comma makes a tuple
libcdll.test_print.restype = None       # for void return
libcdll.test_print(b"test")

output:

test

If called with "test" as in the OP question, now it will tell you the parameter is wrong:

Traceback (most recent call last):
  File "C:\test.py", line 5, in <module>
    libcdll.test_print("test")
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type

Without .argtypes, ctypes defaults to converting "test" from a Python str to a wchar_t* encoded as UTF-16LE on Windows, so it would look like the following, and printf will stop at the first null byte (\x00), explaining t as the output.

>>> 'test'.encode('utf-16le')
b't\x00e\x00s\x00t\x00'

Note that if you want to pass Python str instead of bytes, declare the C function as the following and use .argtypes = c_wchar_p, instead:

void test_print(wchar_t* test) {
        wprintf(L"%s", test);  // wide version of printf and format string
}
Related