英文:
Python ctypes, char** and DLL
问题
import ctypes
libc = ctypes.CDLL("AxECR.so")
ecr = libc.create_object() # return CAxClass* obj
print(libc.get_DLLVersion)
dll = ctypes.POINTER(ctypes.POINTER(ctypes.c_char))
libc.get_DLLVersion.argtypes = [ctypes.c_void_p, dll]
libc.get_DLLVersion(ecr, dll) # This line might need adjustments, but the key part is creating the correct 'dll' variable.
英文:
I have a dll function in c++:
void get_DLLVersion(CAxClass* obj, char ** pVal);
In pVal get_DLLVersion write c string like "1.0.0.1"
In c++ its like:
char *strdll = (char*)malloc(50);
get_DLLVersion(tst, &strdll);
cout << "strdll = "<<strdll<<endl;
I need to use this function in python.
The main problem is how to create char**
and put as 2nd argument of dll function.
I use next code:
import ctypes
libc = ctypes.CDLL("AxECR.so")
ecr = libc.create_object() #return CAxClass* obj
print (libc.get_DLLVersion)
libc.get_DLLVersion.argtypes = [c_void_p, ctypes.POINTER(ctypes.POINTER(c_char))]
dll = ctypes.POINTER(ctypes.POINTER(c_char))
libc.get_DLLVersion(ecr,dll) #don`t work Segmentation fault (core dumped)
答案1
得分: 1
以下是翻译的内容:
[Python.Docs]: ctypes - 用于Python的外部函数库
注意:
-
为了修复这个问题,可以通过create_string_buffer创建一个缓冲区(数组),然后通过byref传递它的地址给函数。
- 需要进行显式类型转换(从char数组到char指针)。
-
对于第1个参数,我创建了一个单例的CAxClass对象,它由每次createObject调用返回。我也可以让函数创建新实例,但这样就需要另一个函数来销毁它,以防止内存泄漏 (1)。
-
从C++中调用该函数的方式,它只是填充作为参数给定地址的内存(如果不是NULL的话,希望如此)。在这种情况下,使用双指针没有太多意义,因为可以使用一个简单的指针来实现相同的目标(在下面的示例中,我添加了另一个函数来证明这一点)。
示例:
-
dll00.cpp:
#include <cstring> #include <iostream> #if defined(_WIN32) # define DLL00_EXPORT_API __declspec(dllexport) #else # define DLL00_EXPORT_API #endif #define BUF_LEN 50 class CAxClass {}; static CAxClass *gObj = new CAxClass(); // nullptr; #if defined(__cplusplus) extern "C" { #endif DLL00_EXPORT_API void* createObject(); DLL00_EXPORT_API void dllVersion(CAxClass *pObj, char **ppVer); DLL00_EXPORT_API void dllVersionSinglePtr(CAxClass *pObj, char *pVer); #if defined(__cplusplus) } #endif void* createObject() { return gObj; } void dllVersion(CAxClass *pObj, char **ppVer) { if ((ppVer) && (*ppVer)) { strncpy(*ppVer, "1.22.333.4444", BUF_LEN); } else { std::cout << "C - NULL pointer\n"; } } void dllVersionSinglePtr(CAxClass *pObj, char *pVer) { if (pVer) { strncpy(pVer, "55555.666666.7777777.88888888", BUF_LEN); } else { std::cout << "C - NULL pointer\n"; } }
-
code00.py:
#!/usr/bin/env python import ctypes as cts import sys CharPtr = cts.c_char_p # 更通用:cts.POINTER(cts.c_char) ? CharPtrPtr = cts.POINTER(CharPtr) BUF_LEN = 50 DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so") def main(*argv): dll = cts.CDLL(DLL_NAME) createObject = dll.createObject createObject.argtypes = () createObject.restype = cts.c_void_p dllVersion = dll.dllVersion dllVersion.argtypes = (cts.c_void_p, CharPtrPtr) dllVersion.restype = None # @TODO - cfati: Testing purposes dllVersionSinglePtr = dll.dllVersionSinglePtr dllVersionSinglePtr.argtypes = (cts.c_void_p, CharPtr) dllVersionSinglePtr.restype = None obj = createObject() print("Object: {:}".format(obj)) buf = cts.create_string_buffer(BUF_LEN) dllVersion(obj, cts.byref(cts.cast(buf, CharPtr))) print("Version: {:}".format(buf.value)) dllVersionSinglePtr(obj, cts.cast(buf, CharPtr)) print("Version: {:}".format(buf.value)) if __name__ == "__main__": print("Python {:s} {:03d}位在{:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform)) rc = main(*sys.argv[1:]) print("\n完成。\n") sys.exit(rc)
输出:
(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q075446745]> ~/sopr.sh ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [064位提示]> ls code00.py dll00.cpp [064位提示]> [064位提示]> g++ -fPIC -shared -o dll00.so dll00.cpp [064位提示]> [064位提示]> ls code00.py dll00.cpp dll00.so [064位提示]> [064位提示]> python ./code00.py Python 3.8.10 (default, Nov 14 2022, 12:59:47) 064位在linux Object: 34716928 Version: b'1.22.333.4444' Version: b'55555.666666.7777777.88888888' 完成。
英文:
Listing [Python.Docs]: ctypes - A foreign function library for Python.
Notes:
-
To fix this, a buffer (array) can be created via create_string_buffer, then its address passed (via byref) to the function
- An explicit cast (from char array to char pointer) is required
-
For the 1<sup>st</sup> argument, I create singleton CAxClass object that is returned by every createObject call. I could also have the function creating the new instance, but another one would be then required to destroy it, in order to prevent memory leaks <sup>(1)</sup>
-
Looking at the way the function is called from C++, it just populates the memory at the address given as an argument (if not NULL, hopefully).<br>
In this case, using a double pointer doesn't make much sense, as the same goal could be achieved using a simple one (I added another function in the example below to prove this)
Example:
-
dll00.cpp:
#include <cstring> #include <iostream> #if defined(_WIN32) # define DLL00_EXPORT_API __declspec(dllexport) #else # define DLL00_EXPORT_API #endif #define BUF_LEN 50 class CAxClass {}; static CAxClass *gObj = new CAxClass(); // nullptr; #if defined(__cplusplus) extern "C" { #endif DLL00_EXPORT_API void* createObject(); DLL00_EXPORT_API void dllVersion(CAxClass *pObj, char **ppVer); DLL00_EXPORT_API void dllVersionSinglePtr(CAxClass *pObj, char *pVer); #if defined(__cplusplus) } #endif void* createObject() { return gObj; } void dllVersion(CAxClass *pObj, char **ppVer) { if ((ppVer) && (*ppVer)) { strncpy(*ppVer, "1.22.333.4444", BUF_LEN); } else { std::cout << "C - NULL pointer\n"; } } void dllVersionSinglePtr(CAxClass *pObj, char *pVer) { if (pVer) { strncpy(pVer, "55555.666666.7777777.88888888", BUF_LEN); } else { std::cout << "C - NULL pointer\n"; } }
-
code00.py:
#!/usr/bin/env python import ctypes as cts import sys CharPtr = cts.c_char_p # More generic: cts.POINTER(cts.c_char) ? CharPtrPtr = cts.POINTER(CharPtr) BUF_LEN = 50 DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so") def main(*argv): dll = cts.CDLL(DLL_NAME) createObject = dll.createObject createObject.argtypes = () createObject.restype = cts.c_void_p dllVersion = dll.dllVersion dllVersion.argtypes = (cts.c_void_p, CharPtrPtr) dllVersion.restype = None # @TODO - cfati: Testing purposes dllVersionSinglePtr = dll.dllVersionSinglePtr dllVersionSinglePtr.argtypes = (cts.c_void_p, CharPtr) dllVersionSinglePtr.restype = None obj = createObject() print("Object: {:}".format(obj)) buf = cts.create_string_buffer(BUF_LEN) dllVersion(obj, cts.byref(cts.cast(buf, CharPtr))) print("Version: {:}".format(buf.value)) dllVersionSinglePtr(obj, cts.cast(buf, CharPtr)) print("Version: {:}".format(buf.value)) if __name__ == "__main__": print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform)) rc = main(*sys.argv[1:]) print("\nDone.\n") sys.exit(rc)
output:
>
> (qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q075446745]> ~/sopr.sh
> ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
>
> [064bit prompt]> ls
> code00.py dll00.cpp
> [064bit prompt]>
> [064bit prompt]> g++ -fPIC -shared -o dll00.so dll00.cpp
> [064bit prompt]>
> [064bit prompt]> ls
> code00.py dll00.cpp dll00.so
> [064bit prompt]>
> [064bit prompt]> python ./code00.py
> Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] 064bit on linux
>
> Object: 34716928
> Version: b'1.22.333.4444'
> Version: b'55555.666666.7777777.88888888'
>
> Done.
>
Might also check:
-
[SO]: C++ & Python: Pass and return a 2D double pointer array from python to c++ (@CristiFati's answer) for the use of a double pointer when passing a 2D array
-
[SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions)
-
[SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer) for an example of footnote #1
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论