Python ctypes返回一个函数指针数组

问题描述 投票:5回答:1

我正在使用包含单个调用的.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)

此时,我只是想让函数列表的第一个函数返回工作。任何帮助指向我更好的方向的人都表示赞赏。

python function-pointers ctypes
1个回答
4
投票

事情并不像第一眼看到的那样容易。我将发布一个虚拟示例,恰好包含使用.dlls(.sos)函数的两种方法(如[Python 3]: ctypes - A foreign function library for Python中所述)。

dll.c:

#include <stdio.h>

#if defined(_WIN32)
#    define DLL_EXPORT __declspec(dllexport)
#    pragma warning(disable: 4477)  // !!! Just to avoid having additional code (macro for size_t), do NOT do this !!!
#else
#    define DLL_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

DLL_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;
    }
}

code.朋友:

#!/usr/bin/env python3

import sys
import ctypes


DLL_NAME = "test.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部分: 我不得不添加一些虚拟代码来测试和说明行为 虽然你提到它不可修改,但我改变了东西(主要是命名/编码风格,......): 字母案例+下划线看起来都不好看(至少对我而言) (我的)(或其任何变体)在(函数,类或任何其他)名称中只是划伤了我的大脑
  • Python部分: 正如我在评论中所说,C语言必须在Python中“重复”
  • 虽然我认为这是一个主要的设计缺陷,但为了使事情尽可能接近问题,我只是遵循它(GetInterfaceV2(V2部分)考虑到它的arg(版本)没有任何意义) 我的个人意见(虽然没有所有上下文)是(为了确保可伸缩性),该函数应该返回一个通用结构,以及可以由客户端应用程序检查的附加字段(例如版本)。

输出:

(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
code.py
dll.c

(py35x64_test) e:\Work\Dev\StackOverflow\q051507196>cl /nologo dll.c  /link /DLL /OUT:test.dll
dll.c
   Creating library test.lib and object test.exp

(py35x64_test) e:\Work\Dev\StackOverflow\q051507196>dir /b
code.py
dll.c
dll.obj
test.dll
test.exp
test.lib

(py35x64_test) e:\Work\Dev\StackOverflow\q051507196>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.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>

        [dll.c] (16) - [IsValidInt]:  ARG0: -2718281
    `is_valid_int` member returned: 2718281

        [dll.c] (22) - [InvalidInt]
    `invalid_int` member returned: 0

        [dll.c] (28) - [IsValidSize]:  ARG0: 3141592
    `is_valid_size` member returned: -3141592
© www.soinside.com 2019 - 2024. All rights reserved.