事情并不像第一眼看到的那样容易.我将发布一个虚拟示例,恰好包含使用.dlls(.sos)函数的两种方法(如
[Python 3]: ctypes – A foreign function library for Python中所述).
dll.c:
#include
#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.py:
#!/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部分:
>我必须添加一些虚拟代码才能测试和说明行为
>虽然你提到它不可修改,但我改变了东西(主要是命名/编码风格,……):
>两个字母的下划线看起来都不好看(至少对我来说)
>(函数,类或任何其他)名称中的“my”(或其任何变体)只会让我的大脑感到震惊
> Python部分:
>正如我在评论中所说,C语言必须在Python中“重复”
>虽然我认为这是一个主要的设计缺陷,但为了使事情尽可能接近问题,我只是遵循它(GetInterfaceV2(V2部分)考虑到它的arg(版本)没有任何意义)
>我的个人意见(虽然没有所有上下文)是(为了确保可伸缩性),该函数应返回一个通用结构,其中包含可由客户端应用程序检查的附加字段(例如版本).
输出:
06002