Python:使用ctypes库调用外部DLL

前言

朋友的公司是做GPS的,上周联系到我要帮做个程序把他们平台的车辆定位跟踪数据和省里的平台对接。看一下官方提供的三个文档,洋洋洒洒共一百多页,一大堆协议的定义甚是齐全,好在官方的文件中也带有个封装好通信功能的DLL和一个调用此接口的c++ DEMO程序,既然有现成的可用,那就不必去看他的协议了。

说实话,参加工作之后就基本没用过c++,生疏了。特别是要用c++操作数据库,对我来说比割几刀还要痛苦。官方的API中已经很详尽,要做的就是从现有平台的数据库中获取车辆定位信息,通过官方的API发送到省中心平台。

本想用C#给官方API做个包装,省得再去动用C++,可是看到此API中定义有几个Struct,而且下行数据都是通过回调函数方式提供,google了一下,似乎C#对调用有回调函数的C DLL不是很顺畅,于是放弃了,想到了Python。

一、Python之ctypes

ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C DLL中的函数。在Python2.5官方安装包都带有ctypes1.1版。ctypes的官方文档在这里。

ctypes的使用非常简明,如调用cdecl方式的DLL只需这样:

1
2
3
from ctypes import *;
h=CDLL('msvcrt.dll')
h.printf('a=%d,b=%d,a+b=%d',1,2,1+2);

以上代码运行后输出 a=1,b=2,a+b=3。

二、加载库和普通函数的调用

官方API提供的库中有几个主要的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//初始化
int DCSPCLIENTDLL InitInterface( const char *pCenterIP, const unsigned short nUpLinkSvrPort,const unsigned short nDownLinkSvrPort );
//释放资源
int DCSPCLIENTDLL FiniInterface( void );
//登录
int DCSPCLIENTDLL Login( const unsigned int uiBranchPlatformID, const unsigned int nUserID,  const char *pPassword );
//注销
int DCSPCLIENTDLL Logout( const unsigned int uiBranchPlatformID, const unsigned int nUserID,   const char *pPassword );
//发车辆实时定位数据
int DCSPCLIENTDLL SendUPRealLocation( const char const pDeviceId,  const char cDeviceColor,
                    const unsigned short nMsgCode, const _stBPDynamicData * const pStGpsData );

在Python中加载使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from ctypes import *
#加载API库
api = CDLL('DCSPClientDLL.dll');
#初始化函数的参数类型
api.InitInterface.argtypes=[c_char_p,c_ushort,c_ushort]
api.Login.argtypes=[c_uint,c_uint,c_char_p]
api.Logout.argtypes=[c_uint,c_uint,c_char_p]
#初始化并登录
api.InitInterface(u"中心服务器地址" , u'上行服务端端口' , u'下行客户端端口')
api.Login(platformID,userID,password);
#.....其它操作
api.Logout(platformID,userID,password); #注销

参数类型可以像上面的代码一样预先设定好,或者在调用函数时再把参数转成相应的c_***类型。ctypes的类型对应如下:

如此,完成了简单的第一步。

三、C语言中的Struct数据结构

在发送实时定位数据的函数SendUPRealLocation中有一个参数是结构体类型 _stBPDynamicData。python中没有struct这种数据结构,ctypes很周全,对C的struct和union这二种数据类型都提供很好的支持。stBPDynamicData结构的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 车辆动态数据结构体
struct _stBPDynamicData
{
    // 加密状态
    unsigned char encrypt;
    // GPS 时间
    _StructTime gpsTime;
    // 经度
    unsigned int  longitude;
    // 纬度
    unsigned int  latitude;
    // GPS速度
    unsigned short unGpsSpeed;
    // 行驶记录仪速度
    unsigned short unTachographSpeed;
    // 车辆当前总里程数
    unsigned int uiMileageTotal;
    // 角度
    unsigned short angle;
    // 车辆状态
    unsigned short state;
    // 报警状态
    unsigned short alarm;
};

在python中,需要定义一个与这兼容的类,继承于ctypes.Structure,其中还用到一个_StructTime结构,这里一并贴出代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class _StructTime(Structure):
    _fields_ =[('day',c_ubyte),
               ('month',c_ubyte),
               ('year',c_ushort),
               ('hour',c_ubyte),
               ('minute',c_ubyte),
               ('second',c_ubyte)];   
    def __str__(self):
        return '{0}-{1}-{2} {3}:{4}:{5}'.format(self.year,self.month,self.day,self.hour,self.minute,self.second);
         
class _stBPDynamicData(Structure):
    _fields_ =[('encrypt',c_ubyte),
               ('gpsTime',_StructTime),            
               ('longitude',c_uint),
               ('latitude',c_uint),
               ('unGpsSpeed',c_ushort),
               ('unTachographSpeed',c_ushort),
               ('uiMileageTotal',c_uint),
               ('angle',c_ushort),
               ('state',c_ushort),
               ('alarm',c_ushort)];
    def __str__(self):
        return u'({0},{1}),{6},sp:{2},angle:{3},st:{4},al:{5}'.format(self.longitude,
                self.latitude,self.unGpsSpeed,
                self.angle ,self.state,self.alarm,self.gpsTime );
     
class gpsData(Structure):
    _fields_ =[('strDeviceID',c_char_p),
               ('cDeviceColor',c_char),            
               ('nMsgCode',c_ushort),
               ('stBPD',_stBPDynamicData)];
    def __str__(self):
        return u'{0},{1}'.format(self.strDeviceID,self.stBPD );

gpsData是我自己加的一个类,用于记录每辆车的信息。

现在就可以使用SendUPRealLocation函数发送车辆实时数据了:

1
2
3
4
5
6
7
8
9
tm=_StructTime();
tm.year=2010;tm.month=4;tm.day=3;tm.hour=11;tm.minute=2;tm.second=11;
bpd=_stBPDynamicData();
bpd.gpsTime=tm;bpd.longitude=1234567;bpd.latitude=246898;#...其它参数
data=gpsData();
data.strDeviceID=u'桂Coo007';data.stBPD=bpd;
#调用 API发送数据
api.SendUPRealLocation( data.strDeviceID, data.cDeviceColor ,
                        data.nMsgCode, addressof( data.stBPD ) );

注意SendUPRealLocation第三个参数是_stBPDynamicData * 指针类型,所以要用ctypes.addressof()取参数的地址。

四、回调函数

写到这里就忍不住唠叨几句,这个系统的协议设计的太有 “个性”了。这个系统的功能说起来也不复杂,就是要GPS运营商把指定的车辆位置信息发送到中心平台,同时中心平台可以向各GPS终端发送一些数据和指令,比如传送文字信息到终端,或者要求终端拍张照片反馈到中心。

这个协议流程是这样,运营商端主动连接到中心服务器,然后此连接只用于传输向中心平台主动发送的数据。登录成功了之后呢,中心平台再向运营商的IP建立一个连接,用于中心下发的数据和指令。官方称为“双链路”。

于是,就要求运营商必须要有固定的公网IP(这个不是问题,据了解GPS运营商服务器都有固定IP),而且这个程序必须运行在有公网IP的电脑上或采用端口映射之类的方法。可是俺开发设计时是在大教育局域网中的,搞个端口映射都不可能更别谈公网IP了。于是,在调试下行数据部分功能时就只能远程到运营商服务器上去调试了。

回归正题。

要使用回调函数,需要先用 CFUNCTYPE 定义回调函数的类型,官方API中有十多个回调函数注册,定义摘抄:

1
2
3
4
5
6
7
8
9
10
11
12
#define DCSPCLIENTDLL __declspec(dllexport)
typedef void (*pDownTextInfoFv) ( const char *const pDeviceID,
                                  const char cDeviceColor, const char *const pInfo );
typedef void (*pDownCommunicateReqFv) ( const char *const pDeviceID,
                                        const char cDeviceColor,  const char *const pCalledTel );
extern "C"
{
    void DCSPCLIENTDLL RegDownTextInfoFunc( pDownTextInfoFv pFv );
    void DCSPCLIENTDLL RegDownCommunicateReqFunc( pDownCommunicateReqFv pFv );
};

在python中,定义相应的类型和回调处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
"""下发文字信息"""
def downTextInfo(pDeviceID,cDeviceColor,pInfo):
    print(u'<-[下发文字]:{0},{1}'.format(str(pDeviceID),str(pInfo)) );
    r=api.SendUpTextInfoAck(pDeviceID, cDeviceColor, True );
    if r==0:
        print(u'->回复下发文字成功。');
    else:
        print(u'->回复下发文字失败。');
pDownTextInfoFv = CFUNCTYPE(c_void_p,c_char_p, c_char, c_char_p)  #回调函数类型定义
pDownTextInfoHandle = pDownTextInfoFv(downTextInfo);
api.RegDownTextInfoFunc(pDownTextInfoHandle);   #注册回调函数

其中SendUpCommunicateAck是回应中心,告知已经收到信息。二个参数类型和downTextInfo中的参数类型一到,所以可以不用初始化声明此函数的参数定义。

其余的回调函数用相同的方法处理。

结尾

调试完API对接部分功能后,在想用哪个py库操作数据库比较方便呢,找了一下之后才想到为何不用ironPython而可以直接使用ado.net访问数据库,岂不是更爽。

于是把代码搬到ironPython2.6中试试,让我十分惊喜的是不用做任何个性代码直接运行成功!ironPython 2.6中的ctypes和Python2.6的一样都是1.1.0版。

Python:使用ctypes库调用外部DLL相关推荐

  1. Python:使用ctypes库调用外部DLL 数据类型对应

    Python:使用ctypes库调用外部DLL(转) 前言

  2. python库_python使用ctypes库调用DLL动态链接库_python

    最近要使用python调用C++编译生成的DLL动态链接库,因此学习了一下ctypes库的基本使用. ctypes是一个用于Python的外部函数库,它提供C兼容的数据类型,并允许在DLL或共享库中调 ...

  3. python调用dll函数指针_python使用ctypes库调用DLL动态链接库

    最近要使用python调用C++编译生成的DLL动态链接库,因此学习了一下ctypes库的基本使用. ctypes是一个用于Python的外部函数库,它提供C兼容的数据类型,并允许在DLL或共享库中调 ...

  4. GameMakerStudio2调用外部dll库

    项目上遇到的问题,记录下来 windows平台下GMS2调用外部动态链接库(DLL) 1.制作DLL文件(自行根据功能制作:C#制作DLL) 制作Dll需注意几点: 创建项目工程时需选择类库 由于要将 ...

  5. c#调用外部dll详解

     一.      DLL与应用程序 动态链接库(也称为DLL,即为"Dynamic Link Library"的缩写)是Microsoft Windows最重要的组成要素之一, ...

  6. 在Delphi中调用外部DLL 之External DLL 导入DLL

    调用一个DLL比写一个DLL要容易一些.首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较.同样的,我们先举一个静态调用的例子. unit Unit1;interfaceu ...

  7. python通过Ctypes 模块 调用C++动态链接库,遇到的access violation reading 0x0000000000000000解决方案

    问题是:通过C++ 程序生成 DLL 内部使用了STL 等模板以及指针和引用.通过ctypes 模块给python 程序调用.有时会报OSError: exception: access violat ...

  8. qt调用c语言编写的dll文件,Qt之调用外部DLL - moki_oschina的个人空间 - OSCHINA - 中文开源技术交流社区...

    预备知识: 1.如果在没有导入库文件(.lib),而只有头文件(.h)与动态链接库(.dll)时,我们才需要显示调用,如果这三个文件都全的话,我们就可以使用简单方便的隐式调用. 2.通常Windows ...

  9. Python——OpenCV(opencv-python库)调用摄像头

    基本概念 OpenCV: OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效--由一系列 ...

最新文章

  1. linux 安装 MySQL
  2. 人工智能学习框架TensorFlow必须掌握和了解的数学基础
  3. java有string这个类型吗_关于java的String类型
  4. 加了密的PDF如何破解复制?
  5. 语言在线组卷系统_如何使用在线考试系统创建题库?
  6. python读写ini文件的库支持unicode_Python读写unicode文件的方法
  7. 松鼠会啊松鼠会,22号来杭州啦
  8. matlab图像的邻域操作,matlab图像的邻域操作与块操作 | 学步园
  9. odoo15 po文件自动翻译器
  10. Python游戏——Pong
  11. 连续时间 Markov 链从某一状态 i 转移到其他状态之前在 i 逗留的时间服从指数分布
  12. 2022-2028全球及中国棕榈甲酯衍生物行业研究及十四五规划分析报告
  13. Vue打包优化篇-CDN加速
  14. 中国集成灶10大品牌排行榜揭晓,公认的集成灶10大品牌是哪几个?
  15. layui的lay-verify参数验证的问题
  16. SQL 如何返回最大值所在的多条记录
  17. Google的秘密通道
  18. 银行软件测试工作总结
  19. Java中获取内存地址
  20. 二、DMSP/OLS、NPP/VIIRS等夜间灯光数据能源碳排放空间化——灯光指数计算(一)

热门文章

  1. 【翻译】在backtrack5上用Evilgrade工具15步**windows
  2. 什么是抽象类?抽象类的作用_Java面试题amp;和amp;amp;的作用和区别
  3. vue add element报错_Vue 源码解析 -- new Vue -gt; mountComponent 001
  4. 类型约束的本质:泛型是不完备类型,只有合乎要求的构造才能正确使用和访问。...
  5. Django实例-静态访问
  6. concurrent.futures 使用及解析
  7. CrazyWing:Python自动化运维开发实战 五、Python运算符与表达式
  8. [React Native Android 安利系列]样式与布局的书写
  9. lnmp 虚拟主机的配置
  10. My SQL外键约束