Python ctypes返回函数指针数组 [英] Python ctypes to return an array of function pointers

查看:121
本文介绍了Python ctypes返回函数指针数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个包含单个调用的.dll,该调用返回一个函数指针数组。 GetMyApi()返回指向结构的指针,该结构是函数指针的数组。函数本身具有不同的单独输入和输出。到目前为止,我已经尝试过:

I am working with a .dll that contains a single call which returns an array of function pointers. GetMyApi() returns a pointer to a struct, which is an array of function pointers. The functions themselves have different individual inputs and outputs. What I have tried so far:

我无法轻易更改的C代码:


  • C中的代码:

  • Code in C :

typedef struct My_Api_V2
{
    int                 (__cdecl *IsValidInt)(int i);
    int                 (__cdecl *InvalidInt)();
    int                 (__cdecl *IsValidSize)(size_t i);
} my_Api_V2;

const my_Api_V2* GetMyApi(int version);   // This function is accessed from DLL


  • Python工作量访问此函数:

  • Python effort:

    from ctypes import *
    
    my_dll = cdll.LoadLibrary(path_to_my_dll)
    my_api = my_dll.GetMyApi
    my_api.argtypes[c_int]  #version number
    my_api.restypes = c_void_p
    
    firstfuncptr = my_api(2)
    firstfunc = prototype(firstfuncptr)
    firstfunc.argtypes[c_int]
    firstfunc.restypes = c_int
    
    test = firstfunc(23)
    


  • 至此,我只是想让功能列表的第一个功能恢复工作。

    At this point, I am just trying to get the first function of the function list returned to work. Any help pointing me in better direction is appreciated.

    推荐答案

    事情并不像人们想到的1 st那样容易一目了然。我将发布一个虚拟示例,该示例恰好包含使用 .dll s( .so s)中的函数的2种方式(如 [Python 3.Docs]:ctypes-Python的外部函数库 )。

    Things are not as easy as one might think at 1st glance. I'm going to post a dummy example that happens to contain the 2 ways of working with functions from .dlls (.sos) (as explained in [Python 3.Docs]: ctypes - A foreign function library for Python).

    dll00.c

    #include <stdio.h>
    
    #if defined(_WIN32)
    #  define DLL00_EXPORT __declspec(dllexport)
    #  pragma warning(disable: 4477)  // !!! Just to avoid having additional code (macro for size_t), do NOT do this !!!
    #else
    #  define DLL00_EXPORT
    #endif
    
    #define PRINT_MSG_0() printf("        [%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__)
    #define PRINT_MSG_1I(ARG0) printf("        [%s] (%d) - [%s]:  ARG0: %d\n", __FILE__, __LINE__, __FUNCTION__, ARG0)
    
    
    static int IsValidInt(int i) {
        PRINT_MSG_1I(i);
        return -i;
    }
    
    static int InvalidInt() {
        PRINT_MSG_0();
        return 0;
    }
    
    static int IsValidSize (size_t i) {
        PRINT_MSG_1I(i);
        return -i;
    }
    
    
    typedef struct DllInterfaceV2Struct {
        int (__cdecl *IsValidIntFuncPtr)(int i);
        int (__cdecl *InvalidIntFuncPtr)();
        int (__cdecl *IsValidSizeFuncPtr)(size_t i);
    } DllInterfaceV2;
    
    
    static DllInterfaceV2 intfV2 = { IsValidInt, InvalidInt, IsValidSize };
    
    
    #if defined(__cplusplus)
    extern "C" {
    #endif
    
    DLL00_EXPORT const DllInterfaceV2 *GetInterfaceV2(int version);
    
    #if defined(__cplusplus)
    }
    #endif
    
    
    DLL_EXPORT const DllInterfaceV2 *GetInterfaceV2(int version) {
        if (version == 2) {
            return &intfV2;
        } else {
            return NULL;
        }
    }
    

    code00.py

    #!/usr/bin/env python3
    
    import sys
    import ctypes
    
    
    DLL_NAME = "test00.dll"
    DLL_FUNC_NAME = "GetInterfaceV2"
    
    # "Define" the Python counterparts for C stuff in order to be able to use it
    
    IsValidIntFuncPtr = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
    InvalidIntFuncPtr = ctypes.CFUNCTYPE(ctypes.c_int)
    IsValidSizeFuncPtr = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_size_t)
    
    class DllInterfaceV2(ctypes.Structure):
        _fields_ = [
            ("is_valid_int", IsValidIntFuncPtr),
            ("invalid_int", InvalidIntFuncPtr),
            ("is_valid_size", IsValidSizeFuncPtr)
        ]
    
    
    # Now, play with C stuff
    
    def test_interface_ptr(intf_ptr):
        print("Testing returned interface: {:}\n".format(intf_ptr))
        if not intf_ptr:
            print("    NULL pointer returned from C\n")
            return
        intf = intf_ptr.contents  # Dereference the pointer
        res = intf.is_valid_int(-2718281)
        print("    `is_valid_int` member returned: {:d}\n".format(res))
        res = intf.invalid_int()
        print("    `invalid_int` member returned: {:d}\n".format(res))
        res = intf.is_valid_size(3141592)
        print("    `is_valid_size` member returned: {:d}\n\n".format(res))
    
    
    def main():
        test_dll = ctypes.CDLL(DLL_NAME)
        get_interface_v2_func = getattr(test_dll, DLL_FUNC_NAME)  # Or simpler: test_dll.GetInterfaceV2
        get_interface_v2_func.argtypes = [ctypes.c_int]
        get_interface_v2_func.restype = ctypes.POINTER(DllInterfaceV2)
    
        pintf0 = get_interface_v2_func(0)
        test_interface_ptr(pintf0)
        pintf2 = get_interface_v2_func(2)
        test_interface_ptr(pintf2)
    
    
    if __name__ == "__main__":
        print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
        main()
    

    注释


    • C 部分:


    • 我必须添加一些伪代码才能测试和说明行为

    • 尽管您提到它不可修改,但我更改了内容(主要是命名/编码样式,...):


      • 两个字母都用+ 下划线看起来不太好(至少对我来说)

      • my (或其任何变体)在(函数,类或其他任何名称)的名字简直就是我的脑子

      • I had to add some dummy code in order to test and illustrate the behavior
      • Although you mentioned that it's not modifiable, I changed stuff (mostly naming / coding style, ...):
        • Both letter cases + underscores don't look nice (at least to me)
        • "my" (or any of its variants) in (functions, classes, or any other) names simply scratches my brain

        Python 部分:


        • 正如我在评论中所述, C 东西必须在 Python

        • As I stated in my comment, the C stuff would have to be "duplicated" in Python

        中复制,尽管我认为这是主要设计缺陷,为了使问题尽可能接近问题,我只是遵循了它( GetInterfaceV2 V2 部分)考虑到其arg( version )毫无意义)

        Although I consider this a major design flaw, in order to keep things as close as possible to the question, I simply followed it (GetInterfaceV2 (the V2 part) doesn't make any sense considering its arg (version))


        • 我个人的观点(尽管没有所有上下文)是(为了确保可伸缩性),该函数应返回通用结构,并带有一个附加字段(例如版本),可以由客户端应用检查。

        • My personal opinion (without having all the context, though) is that (in order to ensure scalability) the function should return a generic structure, with an additional field (e.g. version) that could be checked by client apps.

        输出


        (py35x64_test) e:\Work\Dev\StackOverflow\q051507196>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64
        
        (py35x64_test) e:\Work\Dev\StackOverflow\q051507196>dir /b
        code00.py
        dll00.c
        
        (py35x64_test) e:\Work\Dev\StackOverflow\q051507196>cl /nologo dll00.c  /link /DLL /OUT:test00.dll
        dll00.c
           Creating library test00.lib and object test00.exp
        
        (py35x64_test) e:\Work\Dev\StackOverflow\q051507196>dir /b
        code00.py
        dll00.c
        dll00.obj
        test00.dll
        test00.exp
        test00.lib
        
        (py35x64_test) e:\Work\Dev\StackOverflow\q051507196>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code00.py
        Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
        
        Testing returned interface: <__main__.LP_DllInterfaceV2 object at 0x00000219984EBAC8>
        
           NULL pointer returned from C
        
        Testing returned interface: <__main__.LP_DllInterfaceV2 object at 0x00000219984EBB48>
        
                [dll00.c] (16) - [IsValidInt]:  ARG0: -2718281
            `is_valid_int` member returned: 2718281
        
                [dll00.c] (22) - [InvalidInt]
            `invalid_int` member returned: 0
        
                [dll00.c] (28) - [IsValidSize]:  ARG0: 3141592
            `is_valid_size` member returned: -3141592
        


        这篇关于Python ctypes返回函数指针数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆