Python ctypes 到 return 函数指针数组
Python ctypes to return an array of function pointers
我正在使用一个包含单个调用的 .dll,其中 returns 一个函数指针数组。 GetMyApi() returns 一个指向结构的指针,它是一个函数指针数组。这些功能本身具有不同的输入和输出。到目前为止我尝试了什么:
我不能轻易修改的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)
此时,我只是想让函数列表中的第一个函数恢复工作。感谢任何帮助我指明更好方向的帮助。
事情并不像第一眼 想象的那么简单。我要 post 一个虚拟示例,它恰好包含使用 .dlls (.sos)(如 [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"(或其任何变体)在(函数、类 或任何其他)名称中简直让我抓狂
Python部分:
- 正如我在评论中所述,C 内容必须是 Python 中的 "duplicated"
虽然我认为这是一个主要的设计缺陷,但为了尽可能接近问题,我只是照着做(GetInterfaceV2(V2 部分)考虑到它的 arg (version))
- 我个人的意见(尽管没有所有上下文)是(为了确保可伸缩性)函数应该 return 一个通用结构,带有一个附加字段(例如 version ) 可以由客户端应用程序检查。
输出:
(py35x64_test) e:\Work\Dev\Whosebug\q051507196>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64
(py35x64_test) e:\Work\Dev\Whosebug\q051507196>dir /b
code00.py
dll00.c
(py35x64_test) e:\Work\Dev\Whosebug\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\Whosebug\q051507196>dir /b
code00.py
dll00.c
dll00.obj
test00.dll
test00.exp
test00.lib
(py35x64_test) e:\Work\Dev\Whosebug\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
我正在使用一个包含单个调用的 .dll,其中 returns 一个函数指针数组。 GetMyApi() returns 一个指向结构的指针,它是一个函数指针数组。这些功能本身具有不同的输入和输出。到目前为止我尝试了什么:
我不能轻易修改的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)
此时,我只是想让函数列表中的第一个函数恢复工作。感谢任何帮助我指明更好方向的帮助。
事情并不像第一眼 想象的那么简单。我要 post 一个虚拟示例,它恰好包含使用 .dlls (.sos)(如 [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"(或其任何变体)在(函数、类 或任何其他)名称中简直让我抓狂
Python部分:
- 正如我在评论中所述,C 内容必须是 Python 中的 "duplicated"
虽然我认为这是一个主要的设计缺陷,但为了尽可能接近问题,我只是照着做(GetInterfaceV2(V2 部分)考虑到它的 arg (version))
- 我个人的意见(尽管没有所有上下文)是(为了确保可伸缩性)函数应该 return 一个通用结构,带有一个附加字段(例如 version ) 可以由客户端应用程序检查。
输出:
(py35x64_test) e:\Work\Dev\Whosebug\q051507196>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64 (py35x64_test) e:\Work\Dev\Whosebug\q051507196>dir /b code00.py dll00.c (py35x64_test) e:\Work\Dev\Whosebug\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\Whosebug\q051507196>dir /b code00.py dll00.c dll00.obj test00.dll test00.exp test00.lib (py35x64_test) e:\Work\Dev\Whosebug\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