一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Python:使用ctypes庫調(diào)用外部DLL

 londonKu 2012-03-26

Python:使用ctypes庫調(diào)用外部DLL

2010-04-04 23:36 by 無常, 4056 visits, 收藏, 編輯

前言

朋友的公司是做GPS的,上周聯(lián)系到我要幫做個程序把他們平臺的車輛定位跟蹤數(shù)據(jù)和省里的平臺對接。看一下官方提供的三個文檔,洋洋灑灑共一百多頁,一大堆協(xié)議的定義甚是齊全,好在官方的文件中也帶有個封裝好通信功能的DLL和一個調(diào)用此接口的c++ DEMO程序,既然有現(xiàn)成的可用,那就不必去看他的協(xié)議了。

說實話,參加工作之后就基本沒用過c++,生疏了。特別是要用c++操作數(shù)據(jù)庫,對我來說比割幾刀還要痛苦。官方的API中已經(jīng)很詳盡,要做的就是從現(xiàn)有平臺的數(shù)據(jù)庫中獲取車輛定位信息,通過官方的API發(fā)送到省中心平臺。

本想用C#給官方API做個包裝,省得再去動用C++,可是看到此API中定義有幾個Struct,而且下行數(shù)據(jù)都是通過回調(diào)函數(shù)方式提供,google了一下,似乎C#對調(diào)用有回調(diào)函數(shù)的C DLL不是很順暢,于是放棄了,想到了Python。

一、Python之ctypes

ctypesPython的一個外部庫,提供和C語言兼容的數(shù)據(jù)類型,可以很方便地調(diào)用C DLL中的函數(shù)。在Python2.5官方安裝包都帶有ctypes 1.1版。ctypes的官方文檔在這里。

ctypes的使用非常簡明,如調(diào)用cdecl方式的DLL只需這樣:

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。

二、加載庫和普通函數(shù)的調(diào)用

官方API提供的庫中有幾個主要的函數(shù):

 

//初始化
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 );

//發(fā)車輛實時定位數(shù)據(jù)
int DCSPCLIENTDLL SendUPRealLocation( const char * const pDeviceId,  const char cDeviceColor,
					const unsigned short nMsgCode, const _stBPDynamicData * const pStGpsData );

 

 

在Python中加載使用:

from ctypes import *

#加載API庫
api = CDLL('DCSPClientDLL.dll');
#初始化函數(shù)的參數(shù)類型
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"中心服務(wù)器地址" , u'上行服務(wù)端端口' , u'下行客戶端端口')
api.Login(platformID,userID,password);
#.....其它操作
api.Logout(platformID,userID,password); #注銷

參數(shù)類型可以像上面的代碼一樣預(yù)先設(shè)定好,或者在調(diào)用函數(shù)時再把參數(shù)轉(zhuǎn)成相應(yīng)的c_***類型。ctypes的類型對應(yīng)如下:

image

如此,完成了簡單的第一步。

三、C語言中的Struct數(shù)據(jù)結(jié)構(gòu)

在發(fā)送實時定位數(shù)據(jù)的函數(shù)SendUPRealLocation中有一個參數(shù)是結(jié)構(gòu)體類型 _stBPDynamicData。python中沒有struct這種數(shù)據(jù)結(jié)構(gòu),ctypes很周全,對C的struct和union這二種數(shù)據(jù)類型都提供很好的支持。stBPDynamicData結(jié)構(gòu)的定義如下:

// 車輛動態(tài)數(shù)據(jù)結(jié)構(gòu)體
struct _stBPDynamicData
{
    // 加密狀態(tài)
    unsigned char encrypt;
    // GPS 時間
    _StructTime gpsTime;
    // 經(jīng)度
    unsigned int  longitude;
    // 緯度
    unsigned int  latitude;
    // GPS速度
    unsigned short unGpsSpeed;
    // 行駛記錄儀速度
    unsigned short unTachographSpeed;
    // 車輛當前總里程數(shù)
    unsigned int uiMileageTotal;
    // 角度
    unsigned short angle;
    // 車輛狀態(tài)
    unsigned short state;
    // 報警狀態(tài)
    unsigned short alarm;
};

在python中,需要定義一個與這兼容的類,繼承于ctypes.Structure,其中還用到一個_StructTime結(jié)構(gòu),這里一并貼出代碼:

 

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是我自己加的一個類,用于記錄每輛車的信息。

現(xiàn)在就可以使用SendUPRealLocation函數(shù)發(fā)送車輛實時數(shù)據(jù)了:

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;#...其它參數(shù)
data=gpsData();
data.strDeviceID=u'桂Coo007';data.stBPD=bpd;
#調(diào)用 API發(fā)送數(shù)據(jù)
api.SendUPRealLocation( data.strDeviceID, data.cDeviceColor ,
                        data.nMsgCode, addressof( data.stBPD ) );

注意SendUPRealLocation第三個參數(shù)是_stBPDynamicData * 指針類型,所以要用ctypes.addressof()取參數(shù)的地址。

四、回調(diào)函數(shù)

寫到這里就忍不住嘮叨幾句,這個系統(tǒng)的協(xié)議設(shè)計的太有 “個性”了。這個系統(tǒng)的功能說起來也不復(fù)雜,就是要GPS運營商把指定的車輛位置信息發(fā)送到中心平臺,同時中心平臺可以向各GPS終端發(fā)送一些數(shù)據(jù)和指令,比如傳送文字信息到終端,或者要求終端拍張照片反饋到中心。

這個協(xié)議流程是這樣,運營商端主動連接到中心服務(wù)器,然后此連接只用于傳輸向中心平臺主動發(fā)送的數(shù)據(jù)。登錄成功了之后呢,中心平臺再向運營商的IP建立一個連接,用于中心下發(fā)的數(shù)據(jù)和指令。官方稱為“雙鏈路”。

于是,就要求運營商必須要有固定的公網(wǎng)IP(這個不是問題,據(jù)了解GPS運營商服務(wù)器都有固定IP),而且這個程序必須運行在有公網(wǎng)IP的電腦上或采用端口映射之類的方法。可是俺開發(fā)設(shè)計時是在大教育局域網(wǎng)中的,搞個端口映射都不可能更別談公網(wǎng)IP了。于是,在調(diào)試下行數(shù)據(jù)部分功能時就只能遠程到運營商服務(wù)器上去調(diào)試了。

回歸正題。

要使用回調(diào)函數(shù),需要先用 CFUNCTYPE 定義回調(diào)函數(shù)的類型,官方API中有十多個回調(diào)函數(shù)注冊,定義摘抄:

#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中,定義相應(yīng)的類型和回調(diào)處理函數(shù):

"""下發(fā)文字信息"""
def downTextInfo(pDeviceID,cDeviceColor,pInfo):
    print(u'<-[下發(fā)文字]:{0},{1}'.format(str(pDeviceID),str(pInfo)) );
    r=api.SendUpTextInfoAck(pDeviceID, cDeviceColor, True ); 
    if r==0:
        print(u'->回復(fù)下發(fā)文字成功。');
    else:
        print(u'->回復(fù)下發(fā)文字失敗。');

pDownTextInfoFv = CFUNCTYPE(c_void_p,c_char_p, c_char, c_char_p)  #回調(diào)函數(shù)類型定義
pDownTextInfoHandle = pDownTextInfoFv(downTextInfo);

api.RegDownTextInfoFunc(pDownTextInfoHandle);   #注冊回調(diào)函數(shù)

其中SendUpCommunicateAck是回應(yīng)中心,告知已經(jīng)收到信息。二個參數(shù)類型和downTextInfo中的參數(shù)類型一到,所以可以不用初始化聲明此函數(shù)的參數(shù)定義。

其余的回調(diào)函數(shù)用相同的方法處理。

結(jié)尾

調(diào)試完API對接部分功能后,在想用哪個py庫操作數(shù)據(jù)庫比較方便呢,找了一下之后才想到為何不用ironPython而可以直接使用ado.net訪問數(shù)據(jù)庫,豈不是更爽。

于是把代碼搬到ironPython2.6中試試,讓我十分驚喜的是不用做任何個性代碼直接運行成功!ironPython 2.6中的ctypes和Python2.6的一樣都是1.1.0版。

 

PS:借用 1號園友的主題和CSS。

 

出處:無常筆記 http://wuchang.cnblogs.com

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    一本久道久久综合中文字幕| 大胆裸体写真一区二区| 美女黄片大全在线观看| 国产精品一区二区丝袜| 九九久久精品久久久精品| 日本午夜福利视频免费观看| 中日韩美一级特黄大片| 亚洲免费黄色高清在线观看| 99在线视频精品免费播放| 国产成人精品一区在线观看| 大香伊蕉欧美一区二区三区| 男女一进一出午夜视频| 国产精品欧美日韩中文字幕| 国产一区二区熟女精品免费| 久久91精品国产亚洲| 亚洲欧洲日韩综合二区| 久久国产精品亚州精品毛片| 中文字幕亚洲精品在线播放| 国产精品午夜小视频观看| 午夜福利在线观看免费| 在线精品首页中文字幕亚洲| 大屁股肥臀熟女一区二区视频| 欧美一级黄片免费视频| 爱草草在线观看免费视频| 久久99爱爱视频视频| 国产色第一区不卡高清| 欧美日韩中黄片免费看| 欧美日韩欧美国产另类| 欧美一区二区不卡专区| 台湾综合熟女一区二区| 国产精品视频第一第二区| 欧美胖熟妇一区二区三区| 成人国产激情在线视频| 97人摸人人澡人人人超碰| 国产成人精品国产成人亚洲| 欧美日韩综合在线精品| 亚洲第一香蕉视频在线| 国产av一区二区三区久久不卡| 在线亚洲成人中文字幕高清| 亚洲精品熟女国产多毛| 日韩综合国产欧美一区|