Python ctypes, char** and DLL

huangapple go评论73阅读模式
英文:

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 &lt;cstring&gt;
    #include &lt;iostream&gt;
    
    #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 &quot;C&quot; {
    #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) &amp;&amp; (*ppVer)) {
            strncpy(*ppVer, &quot;1.22.333.4444&quot;, BUF_LEN);
        } else {
            std::cout &lt;&lt; &quot;C - NULL pointer\n&quot;;
        }
    }
    
    
    void dllVersionSinglePtr(CAxClass *pObj, char *pVer)
    {
        if (pVer) {
            strncpy(pVer, &quot;55555.666666.7777777.88888888&quot;, BUF_LEN);
        } else {
            std::cout &lt;&lt; &quot;C - NULL pointer\n&quot;;
        }
    }
    
  • 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 = &quot;./dll00.{:s}&quot;.format(&quot;dll&quot; if sys.platform[:3].lower() == &quot;win&quot; else &quot;so&quot;)
    
    
    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(&quot;Object: {:}&quot;.format(obj))
    
        buf = cts.create_string_buffer(BUF_LEN)
        dllVersion(obj, cts.byref(cts.cast(buf, CharPtr)))
        print(&quot;Version: {:}&quot;.format(buf.value))
    
        dllVersionSinglePtr(obj, cts.cast(buf, CharPtr))
        print(&quot;Version: {:}&quot;.format(buf.value))
    
    
    if __name__ == &quot;__main__&quot;:
        print(&quot;Python {:s} {:03d}bit on {:s}\n&quot;.format(&quot; &quot;.join(elem.strip() for elem in sys.version.split(&quot;\n&quot;)),
                                                       64 if sys.maxsize &gt; 0x100000000 else 32, sys.platform))
        rc = main(*sys.argv[1:])
        print(&quot;\nDone.\n&quot;)
        sys.exit(rc)
    

output:

>
&gt; (qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q075446745]&gt; ~/sopr.sh
&gt; ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
&gt;
&gt; [064bit prompt]&gt; ls
&gt; code00.py dll00.cpp
&gt; [064bit prompt]&gt;
&gt; [064bit prompt]&gt; g++ -fPIC -shared -o dll00.so dll00.cpp
&gt; [064bit prompt]&gt;
&gt; [064bit prompt]&gt; ls
&gt; code00.py dll00.cpp dll00.so
&gt; [064bit prompt]&gt;
&gt; [064bit prompt]&gt; python ./code00.py
&gt; Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] 064bit on linux
&gt;
&gt; Object: 34716928
&gt; Version: b&#39;1.22.333.4444&#39;
&gt; Version: b&#39;55555.666666.7777777.88888888&#39;
&gt;
&gt; Done.
&gt;

Might also check:

huangapple
  • 本文由 发表于 2023年2月14日 18:47:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75446745.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定