我正在使用一个包含单个调用的.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 部分:
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
(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