1. 背景与需求

  1. 公司通过提供 C/C++ 语言开发的 DLL/SO 动态链接库,向第三方提供接口服务,需要对此进行测试。
  2. 测试人员普遍不具备 C 语言能力,但可以使用 Python 语言解决问题

2. 基础知识

  1. 动态链接库在 Windows 中为.dll 文件,在 linux 中为.so 文件,使用 C/C++ 进行编写。
  2. 通过 Python 调用 C 语言接口时,可以使用 Python 提供的 ctypes 模块调用 C 接口
  3. ctypes 是一个为 Python 准备的内建函数库。无需安装可直接使用。它提供兼容 C 的数据类型,并允许调用 DLL 或 SO 库中的函数。
  4. 每一个 C 语言的参数,都不能直接使用 Python 直接定义的类型,均需使用 ctypes 提供的类型进行定义

3. 调用方法

1. 使用 Python 加载.so 库文件

from ctypes import *
os.chdir('/home/wuhao/eclipse-workspace/yunhsm_SDK_Test/data/')
sdkPath = os.getcwd()
cur = cdll.LoadLibrary(sdkPath + "/libtestapi.so")
return cur

2. 使用 Python 调用.so 中的方法

self.uiLength = c_int(8)     
self.random = (c_ubyte*self.uiLength.value)()
self.ret_generateRandom = cur.SDF_GenerateRandom(self.uiLength, self.random)
print "ret_generateRandom: " + str(self.ret_generateRandom)

3. 使用 Python 的 ctypes 模块,定义几种 C 中常用数据类型

A = (c_ubyte*8)() 对应 C 中的 unsigned char 类型
B = (c_ubyte*3)(1,2,3) 对应 C 中的 unsigned char 类型
C = c_uint(8) 对应 C 中的 unsigned int 类型
D = c_void_p 对应 C 中的 void 类型

Python 调用 ctypes 数据格式 意义 C 中数据结构
A = (c_ubyte*8)() 用 Python 定义一个长度为 8 的数组,数组中内容默认赋值全 0 对应 C 中的 unsigned char 类型
B = (c_ubyte*3)(1,2,3) 用 Python 定义一个长度为 3 的数组,数组中内容为 1,2,3 对应 C 中的 unsigned char 类型
C = c_uint(8) 定义一个 int 型变量,值为 8 对应 C 中的 unsigned int 类型
D = c_void_p 定义一个 void 型变量 对应 C 中的 void 类型

4. 使用 Python 定义 C 中结构体

  1. C 中结构体
-typedf struct ECCrefPublicKey_st{
unsigned int bits;
unsigned char K[ECCref_MAX_LEN]
}ECCrefPrivateKey
  1. Python 中定义 C 结构体
from ctypes import *
from ctypes import c_ubyte
from ctypes import c_int
class ECCrefPublicKey(Structure):
_fields_ = [("bits", c_uint),
("K", c_ubyte*64)]

5. 使用 Python 传入 C 中结构体类型参数 (传入的实质为结构体的指针)

  1. C 中需传入结构体参数的接口:
int SDF_ExternalVerify_ECC(
    void *hSessionHandle,
    ECCrefPublicKey *pucPublicKey,
)

2.Python 中传入结构体参数的方法:

self.eccPublicKey = ECCrefPublicKey()
self.ret_ExternalVerifyECC = self.cur.SDF_ExternalVerify_ECC(hSessionHandle, byref(self.eccPublicKey))

6. 使用 Python 传入 C 中指针类型参数

  1. C 中需传入指针参数的接口:
int SDF_OpenDevice(void **phDeviceHandle);
  1. Python 中传入指针参数的方法:
self.hDeviceHandle = c_void_p(0)
self.ret_OpenDevice = self.cur.SDF_OpenDevice(byref(self.hDeviceHandle))

3. 总结

目前使用 ctypes 模块和以上数据类型,能够满足工作中对动态链接库的接口测试需求,如果工作中涉及到了新得数据类型,后续也会继续进行补充,希望可以帮到大家。


↙↙↙阅读原文可查看相关链接,并与作者交流