'Pass char* array from c dll to python
I have simple dll with function which taks callback as parameter and calls it sometimes passing unsigned char* array. I want to use it in python.
Here is dll source:
typedef void (*buffer_ready_callback_t)(unsigned char* data, int len);
extern "C" void __declspec(dllexport) example(buffer_ready_callback_t cb) {
unsigned char* data = (unsigned char*)malloc(100);
memset(data,0xAA,100);
cb(data,100);
free(data);
}
and in python I use it like this:
library = cdll.LoadLibrary("example.dll")
buffer_ready_callback_t = CFUNCTYPE(None, c_char_p, c_int, c_ulong)
def api_callback_buffer(self, data, ln, ts):
#problem here
pass
function = self.library.example
function.restype = None
function.argtypes = [buffer_ready_callback_t]
api_buffer_cb = buffer_ready_callback_t(api_callback_buffer)
function(api_buffer_cb);
So question is how to use data received in callback function? Is it possible to convert to bytearray or list or or numpy array or something else that can be used in python?
Thanks
Solution 1:[1]
Listing [Python.Docs]: ctypes - A foreign function library for Python.
The question is a bit unclear. Given existing code, you should already have the data as bytes in your callback function.
Here's an example.
dll00.c:
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
# define DLL00_EXPORT_API __declspec(dllexport)
#else
# define DLL00_EXPORT_API
#endif
typedef void (*Callback)(unsigned char *pData, int len);
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API void dll00Func00(Callback cb, unsigned int len, unsigned char fillValue);
#if defined(__cplusplus)
}
#endif
void dll00Func00(Callback cb, unsigned int len, unsigned char fillValue) {
unsigned char *pData = malloc(len);
memset(pData, fillValue, len);
cb(pData, len);
free(pData);
pData = NULL;
}
code00.py:
#!/usr/bin/env python
import sys
import ctypes as ct
CharArray = ct.POINTER(ct.c_char) # #1: Use ct.POINTER(ct.c_char) instead of ct.c_char_p as 2nd arg type
#CharArray = ct.c_char_p
Callback = ct.CFUNCTYPE(None, CharArray, ct.c_int)
DLL_NAME = "./dll00.dll"
DATA_LEN = 0x100
def func(data, length):
print("Length: {0:d}".format(length))
print(type(data))
print("Elements:")
for idx in [0, DATA_LEN - 1]:
print(" {0:3d} - {1:}".format(idx, data[idx]))
b = b"".join((data[i] for i in range(length)))
print(type(b), len(b), b)
def main(*argv):
dll00 = ct.CDLL(DLL_NAME)
dll00Func00 = dll00.dll00Func00
dll00Func00.argtypes = (Callback, ct.c_uint, ct.c_ubyte)
dll00Func00.restype = None
dll00Func00(Callback(func), DATA_LEN, 0xAA) # #2: Pass 0x00 as 3rd arg and uncomment line #8, and you'll see what I meant in the 1st note
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
Output:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q063190506]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul [prompt]> dir /b code00.py dll00.c [prompt]> cl /nologo /MD /DDLL dll00.c /link /NOLOGO /DLL /OUT:dll00.dll dll00.c Creating library dll00.lib and object dll00.exp [prompt]> dir /b code00.py dll00.c dll00.dll dll00.exp dll00.lib dll00.obj [prompt]> [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32 Length: 256 <class 'ctypes.LP_c_char'> Elements: 0 - b'\xaa' 255 - b'\xaa' <class 'bytes'> 256 b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' Done.
Notes:
- c_char_p is to be used with NUL terminated strings, using it here would be incorrect (even if apparently everything seems OK)
- The example is a bit more complex (to easily exemplify the previous bullet)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |