当前位置: 首页 > 面试题库 >

Python ctypes返回函数指针数组

侯英达
2023-03-14
问题内容

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

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

  • 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的努力:

    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)

此时,我只是想让功能列表中的第一个功能恢复工作。任何帮助我指出更好方向的帮助都将受到赞赏。


问题答案:

事情并不像人们想象的在1个那样容易ST一目了然。我将发布一个虚拟示例,该示例恰好包含使用 .dll s( .so
s)中的函数的2种方式(如[Python 3.Docs]中所述:ctypes-
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 ”(或其任何变体)简直是伤脑筋
    • Python 部分:

    • 正如我在评论中所述,必须在 Python中 “复制” C 内容 __

    • 尽管我认为这是一个 主要的 设计缺陷,但为了使问题尽可能接近问题,我只是遵循了它( GetInterfaceV2 V2 部分)考虑到其arg( 版本 )没有任何意义)

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

输出

(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


 类似资料:
  • C++ 指针 在上一章中,我们已经了解了 C++ 中如何从函数返回数组,类似地,C++ 允许您从函数返回指针。为了做到这点,您必须声明一个返回指针的函数,如下所示: int * myFunction() { . . . } 另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。 现在,让我们来看下面的函数,它会生成 10 个随机数,并使用表示指针的数组名(即第

  • 如果指针类型作为函数参数,修改形参会影响实参 不能将函数内的指向局部变量的指针作为返回值,函数结束指向空间会被释放 可以将函数内的局部变量作为返回值,本质是拷贝一份

  • 我在玩C语言中的函数指针只是为了学习。我尝试调用一个空函数,并将其结果设置为int。 我从中得到的结果是: 在对它进行了一点研究之后,我意识到main正在函数()中打印printf语句中的字符数。为什么会这样?这是预期产出吗?

  • 正如我们在上一章中看到的,C ++如何允许从函数返回数组,类似的方式C ++允许您从函数返回指针。 为此,您必须声明一个返回指针的函数,如下例所示 - int * myFunction() { . . . } 要记住的第二点是,将局部变量的地址返回到函数外部并不是一个好主意,因此您必须将局部变量定义为static变量。 现在,考虑以下函数,它将生成10个随机数并使用表示指针的数

  • 一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个 指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是 函数指针。 函数指针的定义形式为: returnType (*pointerName)(param list); returnType

  • 本文向大家介绍指针数组、数组指针、函数指针的区别?相关面试题,主要包含被问及指针数组、数组指针、函数指针的区别?时的应答技巧和注意事项,需要的朋友参考一下 指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。 数组指针:首先它是一个指针,它指向一个数组。在32