python调用dll三种方式

很早就开始用cython方法,ctype方法,最近知道还有个pythonnet方法。分别说来。

1 cython包海康sdk

根目录先放inc和lib ,分别是厂商的.h文件和.so文件。然后根据头文件写cython的声明文件wrap.pyd:

# -*- coding: UTF-8 -*-
# -*- coding: UTF-8 -*-
# cdef extern from "string.h":
#     void* memcpy(void* dest, void* src,size_t count) except +
cdef extern from "HCNetSDK.h":
    ctypedef int BOOL
    ctypedef  unsigned int       DWORD
    ctypedef  unsigned short     WORD
    ctypedef  unsigned short     USHORT
    ctypedef  short              SHORT
    ctypedef  int                LONG
    ctypedef  unsigned char      BYTE
    ctypedef  unsigned int       UINT
    ctypedef  void*LPVOID
    ctypedef  void*HANDLE
    ctypedef  unsigned int*LPDWORD
    ctypedef  unsigned long long UINT64
    ctypedef  signed long long   INT64

    cdef enum:
        SERIALNO_LEN=48
        NAME_LEN=32
        MACADDR_LEN=6
        COMM_ALARM=0x1100  #8000报警信息主动上传
        MAX_DISKNUM=16#8000设备最大硬盘数
        MAX_CHANNUM=16#8000设备最大通道数
        MAX_ALARMOUT=4#8000设备最大报警输出数
    ctypedef struct NET_DVR_DEVICEINFO_V30:
        BYTE sSerialNumber[SERIALNO_LEN]
        BYTE byAlarmInPortNum
        BYTE byAlarmOutPortNum
        BYTE byDiskNum
        BYTE byDVRType
        BYTE byChanNum
        BYTE byStartChan
        BYTE byAudioChanNum
        BYTE byIPChanNum
        BYTE byZeroChanNum
        BYTE byMainProto
        BYTE bySubProto
        BYTE bySupport
        BYTE bySupport1
        BYTE bySupport2
        WORD wDevType
        BYTE bySupport3
        BYTE byMultiStreamProto
        BYTE byStartDChan
        BYTE byStartDTalkChan
        BYTE byHighDChanNum
        BYTE bySupport4
        BYTE byLanguageType
        BYTE byVoiceInChanNum
        BYTE byStartVoiceInChanNo
        BYTE  bySupport5
        BYTE  bySupport6
        BYTE  byMirrorChanNum
        WORD wStartMirrorChanNo
        BYTE bySupport7
        BYTE  byRes2
    ctypedef struct NET_DVR_SDKSTATE:
        DWORD dwTotalLoginNum
        DWORD dwTotalRealPlayNum
        DWORD dwTotalPlayBackNum
        DWORD dwTotalAlarmChanNum
        DWORD dwTotalFormatNum
        DWORD dwTotalFileSearchNum
        DWORD dwTotalLogSearchNum
        DWORD dwTotalSerialNum
        DWORD dwTotalUpgradeNum
        DWORD dwTotalVoiceComNum
        DWORD dwTotalBroadCastNum
        DWORD dwTotalListenNum
        DWORD dwEmailTestNum
        DWORD dwBackupNum
        DWORD dwTotalInquestUploadNum
        DWORD dwRes[6]
    ctypedef NET_DVR_DEVICEINFO_V30 *LPNET_DVR_DEVICEINFO_V30
    ctypedef NET_DVR_SDKSTATE *LPNET_DVR_SDKSTATE

    BOOL __stdcall NET_DVR_Init() except +
    BOOL __stdcall NET_DVR_Cleanup() except +
    BOOL __stdcall NET_DVR_SetConnectTime(DWORD dwWaitTime, DWORD dwTryTime) except +
    BOOL __stdcall NET_DVR_SetReconnect(DWORD dwInterval, BOOL bEnableRecon) except +
    DWORD __stdcall NET_DVR_GetLastError() except +

    LONG __stdcall NET_DVR_Login_V30(char *sDVRIP, WORD wDVRPort, char *sUserName, char *sPassword, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo) except +
    BOOL __stdcall NET_DVR_Logout_V30(LONG lUserID) except +
    BOOL __stdcall NET_DVR_Logout(LONG lUserID) except +
    BOOL NET_DVR_GetSDKState( LPNET_DVR_SDKSTATE pSDKState) except +



    ctypedef struct NET_DVR_ALARMER:
        BYTE byUserIDValid# userid是否有效 0-无效,1-有效 */
        BYTE bySerialValid# 序列号是否有效 0-无效,1-有效 */
        BYTE byVersionValid# 版本号是否有效 0-无效,1-有效 */
        BYTE byDeviceNameValid#  设备名字是否有效 0-无效,1-有效 */
        BYTE byMacAddrValid# MAC地址是否有效 0-无效,1-有效 */
        BYTE byLinkPortValid# login端口是否有效 0-无效,1-有效 */
        BYTE byDeviceIPValid# 设备IP是否有效 0-无效,1-有效 */
        BYTE bySocketIPValid# socket ip是否有效 0-无效,1-有效 */
        LONG lUserID#  NET_DVR_Login()返回值, 布防时有效 */
        BYTE sSerialNumber[SERIALNO_LEN]#  /* 序列号 */
        DWORD dwDeviceVersion# /* 版本信息 高16位表示主版本,低16位表示次版本*/
        char sDeviceName[NAME_LEN]#  /* 设备名字 */
        BYTE byMacAddr[MACADDR_LEN]#   /* MAC地址 */
        WORD wLinkPort#       link port */
        char sDeviceIP[128]#    /* IP地址 */
        char sSocketIP[128]# /* 报警主动上传时的socket IP地址 */
        BYTE byIpProtocol#    /* Ip协议 0-IPV4, 1-IPV6 */
        BYTE byRes1[2]
        BYTE bJSONBroken#   //JSON断网续传标志。0:不续传;1:续传
        WORD wSocketPort
        BYTE byRes2[6]
    ctypedef NET_DVR_ALARMER *LPNET_DVR_ALARMER


    ctypedef struct NET_DVR_ALARMINFO:
        DWORD dwAlarmType#/*0-信号量报警,1-硬盘满,2-信号丢失,3-移动侦测,4-硬盘未格式化,5-读写硬盘出错,6-遮挡报警,7-制式不匹配, 8-非法访问, 9-视频信号异常,10-录像异常 11- 智能场景变化*/
        DWORD dwAlarmInputNumber#/*报警输入端口*/
        DWORD dwAlarmOutputNumber[MAX_ALARMOUT]#/*触发的输出端口,哪一位为1表示对应哪一个输出*/
        DWORD dwAlarmRelateChannel[MAX_CHANNUM]#/*触发的录像通道,哪一位为1表示对应哪一路录像, dwAlarmRelateChannel[0]对应第1个通道*/
        DWORD dwChannel[MAX_CHANNUM]#/*dwAlarmType为2或3,6,9,10时,表示哪个通道,dwChannel[0]位对应第1个通道*/
        DWORD dwDiskNumber[MAX_DISKNUM]#/*dwAlarmType为1,4,5时,表示哪个硬盘, dwDiskNumber[0]位对应第1个硬盘*/
    ctypedef NET_DVR_ALARMINFO *LPNET_DVR_ALARMINFO

    ctypedef void (__stdcall *MSGCallBack)(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser)
    BOOL __stdcall NET_DVR_SetDVRMessageCallBack_V30(MSGCallBack fMessageCallBack, void* pUser)


    LONG __stdcall NET_DVR_StartListen_V30(char *sLocalIP, WORD wLocalPort, MSGCallBack DataCallback, void* pUserData)
    BOOL __stdcall NET_DVR_StopListen_V30(LONG lListenHandle)

自己设计到底用啥,写桥梁过度文件wrap.pyx:

cimport wrap
from libc.stdlib cimport malloc, free
from libc.string cimport memcpy
from cpython.pycapsule cimport *

# cdef object callbackfunc
# cdef void __stdcall MessageCallback(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser):
#     cdef wrap.NET_DVR_ALARMINFO struAlarmInfo
#     memcpy(&struAlarmInfo, pAlarmInfo, sizeof(wrap.NET_DVR_ALARMINFO))
#     altype=struAlarmInfo.dwAlarmType
#     chan=-1
#     for i in range(0,17):
#         if struAlarmInfo.dwChannel[i] == 1:
#             chan=i+1
#             break
#     # 使用request 发送到sever
#     global callbackfunc
#     (callbackfunc)(lCommand,altype,chan)
#     return

cdef class Hc:
    cdef wrap.NET_DVR_DEVICEINFO_V30* struDeviceInfo
    cdef object callbackfunc # 这里不写,下面__init__要报错
    def __cinit__(self):
        self.struDeviceInfo = <wrap.NET_DVR_DEVICEINFO_V30*>malloc(sizeof(wrap.NET_DVR_DEVICEINFO_V30))
        if not self.struDeviceInfo:
            raise MemoryError
    def __dealloc__(self):
        free(self.struDeviceInfo)
    def notify(self,success,what):
        if success==1:
            print (what+"success")
        else:print (what+"fail")
        return
    def __init__(self):
        self.callbackfunc=""


    def dvr_init(self):
        success=wrap.NET_DVR_Init()
        self.notify(success,"init")
        return
    def set_con_time(self):
        success=wrap.NET_DVR_SetConnectTime(2000, 1)
        self.notify(success,"set con time")
        return
    def set_recon_time(self):
        success=wrap.NET_DVR_SetReconnect(10000, 1)
        self.notify(success,"reset time")
        return
    def get_error(self):
        error_code=wrap.NET_DVR_GetLastError()
        return error_code
    def login_v30(self,ip,port,user,pwd):
        userid=wrap.NET_DVR_Login_V30(ip,port,user,pwd,self.struDeviceInfo)
        if userid<0:
            error_code=self.get_error()
            print ("login fail,error code:",error_code)
            self.cleanup()
        print (self.struDeviceInfo.sSerialNumber)
        return userid
    def logout_v30(self,userid):
        success=wrap.NET_DVR_Logout_V30(userid)
        self.notify(success,"logout")
        return
    def logout(self,userid):
        success=wrap.NET_DVR_Logout(userid)
        self.notify(success,"logout")
        return
    # 函数内部分配内存,free
    cpdef get_version(self):
        cdef wrap.NET_DVR_SDKSTATE* psdkstate
        psdkstate=<wrap.NET_DVR_SDKSTATE*>malloc(sizeof(wrap.NET_DVR_SDKSTATE))
        if not psdkstate:
            raise MemoryError
        success=wrap.NET_DVR_GetSDKState(psdkstate)
        self.notify(success,"get version")
        print (psdkstate.dwTotalLoginNum)
        free(psdkstate)
        return
    def cleanup(self):
        success=wrap.NET_DVR_Cleanup()
        self.notify(success,"clean up")
        return
    def stop_listen(self,handler):
        status=wrap.NET_DVR_StopListen_V30(handler)
        return status

# 在set callback中把pyfunc创建来,让他等于self。callback,真正的c callback就是下面的中,运行pyfunc,达到运行时传入函数控制的目的


    cdef void __stdcall MessageCallback(self,LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser):
            cdef wrap.NET_DVR_ALARMINFO struAlarmInfo
            memcpy(&struAlarmInfo, pAlarmInfo, sizeof(wrap.NET_DVR_ALARMINFO))
            altype=struAlarmInfo.dwAlarmType
            chan=-1
            for i in range(0,17):
                if struAlarmInfo.dwChannel[i] == 1:
                    chan=i+1
                    break
            # 使用request 发送到sever
            (<object>self.callbackfunc)(lCommand,altype,chan)
            return


    cpdef set_callback_listen(self,pyfunc,port,lUserID):
        # global callbackfunc
        self.callbackfunc=pyfunc
        wrap.NET_DVR_SetDVRMessageCallBack_V30(<wrap.MSGCallBack>self.MessageCallback, NULL)
        lHandle = wrap.NET_DVR_StartListen_V30(NULL,port, <wrap.MSGCallBack>self.MessageCallback, NULL)
        if lHandle< 0:
            print ("NET_DVR_StartListen_V30 error code", self.get_error())
            self.logout(lUserID)
            self.cleanup()
        return

 
  

setup.py文件 python setup.py build_ext -i

# -*- coding: utf-8 -*-
from distutils.core import setup, Extension
from Cython.Build import cythonize
import os

hccom = list(map(lambda x: x[3:-3], os.listdir("./lib/HCNetSDKCom")))
libs=["hcnetsdk", "HCCore", "ssl", "crypto", ":libcrypto.so.1.0.0"] + hccom
print ("*********************************************************************")
print(libs)

setup(
    ext_modules=cythonize(
        Extension(
            'wrap',#mod name should be equal pyx file name,so the file will be entrance
            sources=['wrap.pyx'],
            language="c++",
            include_dirs=[os.path.abspath("./inc")],
            library_dirs=[os.path.abspath('.'), os.path.abspath("./lib"),os.path.abspath("./lib/HCNetSDKCom")],
            libraries=libs,
            extra_compile_args=[],
            extra_link_args=[]
        ),
        compiler_directives={'language_level': 3},
    )
)

# build_ext --inplace

# sources 里面可以包含 .pyx 文件,以及后面如果我们要调用 C/C++ 程序的话,还可以往里面加 .c / .cpp 文件
# language 其实默认就是 c,如果要用 C++,就改成 c++ 就好了
# include_dirs 这个就是传给 gcc 的 -I 参数
# library_dirs 这个就是传给 gcc 的 -L 参数
# libraries 这个就是传给 gcc 的 -l 参数
# extra_compile_args 就是传给 gcc 的额外的编译参数,比方说你可以传一个 -std=c++11
# extra_link_args 就是传给 gcc 的额外的链接参数(也就是生成动态链接库的时候用的)

# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/HCNetSDKCom
# etc 下有一个ld的conf可以增加 ldconfig

最后是调用的例子

# -*- coding: utf-8 -*-
import os,sys

import wrap

alarms={0:"信号量报警",
        1:"硬盘满",
        2:'信号丢失',
        3:'移动侦测',
        4:'硬盘未格式化',
        5:'读写硬盘出错',
        6:'遮挡报警',
        7:'制式不匹配',
        8:'非法访问',
        9:'视频信号异常',
        10:'录像异常',
        11: '智能场景变化'}
def pyfunc(lCommand,altype,chan):
    print ("消息类型",lCommand)
    print ("alarm type",altype,alarms.get(altype,"not found"))
    print ("channel number",chan)
    return

hc=wrap.Hc()
hc.dvr_init()
hc.set_con_time()
hc.set_recon_time()
userid=hc.login_v30(b"192.168.2.102", 8000, b"admin", b"wangqi1977")
print ("userid",userid)
hc.get_version()
port=8087

hc.set_callback_listen(pyfunc,port,userid)
hc.logout(userid)
hc.cleanup()

2 ctype调用佳博打印机

这个简单很多,不过cython的好处在于可以自己设计api,把c的回调函数什么的调用设计更加pythonic

from ctypes import*
dll='./TSCLIB.dll'
TSCLIB_DLL= cdll.LoadLibrary(dll)
TSCLIB_DLL.openport("TSC TTP-245C")
# specified printer driver
TSCLIB_DLL.setup("100", "63.5", "4", "8", "0", "0", "0")
TSCLIB_DLL.clearbuffer()
TSCLIB_DLL.barcode("100", "100", "128", "100", "1", "0", "2", "2", "Barcode Test")
TSCLIB_DLL.printerfont("100", "250", "3", "0", "1", "1", "Print Font Test")
TSCLIB_DLL.windowsfont(100, 300, 24, 0, 0, 0, "ARIAL", "Windows Arial Font Test")

TSCLIB_DLL.downloadpcx("UL.PCX", "UL.PCX")
TSCLIB_DLL.sendcommand("PUTPCX 100,400,\"UL.PCX\"")
TSCLIB_DLL.printlabel("1", "1")
TSCLIB_DLL.closeport()

3 pythonnet

这个是有条件的,就是dll符合com接口,比如c#代码编译的出ClassLibrary1.dll:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace dllTest
{
    public class Operate
    {
        public int GetSum(int a, int b)
        {
            return a + b;
        }
    }
}


py调用

import clr  # pythonnet


# clr.FindAssembly('ClassLibrary1.dll')

clr.AddReference('ClassLibrary1')

from dllTest import Operate

s=Operate()
a=s.GetSum(12,3)
print (a)

你可能感兴趣的:(python)