在python中某些時候需要C做效率上的補充,在實際應用中,需要做部分數據的交互。使用python中的ctypes模組可以很方便的調用windows的dll(也包括linux下的so等文件),下面將詳細的講解這個模組(以windows平台為例子),當然我假設你們已經對windows下怎麼寫一個DLL是沒有問題的。
引入ctypes函式庫
from ctypes import *
假設你已經有了一個的DLL(名字是add.dll),且該DLL有一個符合cdecl(這裡強調呼叫約定是因為,stdcall呼叫約定和cdecl呼叫約定聲明的導出函數,在使用python載入時使用的載入函數是不同的,後面會有說明)呼叫約定的導出函數Add。
相關推薦:《python影片教學》
建立一個Python檔DllCall.py測試:
from ctypes import * dll = CDLL("add.dll") print dll.Add(1, 102)
結果:103
#上面是一個簡單的例子。下面簡單聊聊呼叫流程:
1、載入DLL
上面已經說過,載入的時候要根據你將要呼叫的函數是符合什麼呼叫約定的。
stdcall呼叫約定:兩個載入方式
Objdll = ctypes.windll.LoadLibrary("dllpath") Objdll = ctypes.WinDLL("dllpath")
cdecl呼叫約定:也有兩種載入方式
Objdll = ctypes.cdll.LoadLibrary("dllpath") Objdll = ctypes.CDLL("dllpath") /*其实windll和cdll分别是WinDLL类和CDll类的对象。*/
2、呼叫dll中的方法
在1中載入dll的時候會回傳一個DLL物件(假設名字叫Objdll),利用該物件就可以呼叫dll中的方法。
e.g.如果dll中有個方法名字叫Add(注意如果經過stdcall宣告的方法,如果不是用def檔案宣告的匯出函式或extern “C” 宣告的話,編譯器會對函式名稱進行修改,這個要注意,我想你們懂的。)
呼叫:nRet = Objdll.Add(12, 15) 即完成一次呼叫。
看起來呼叫似乎很簡單,不要只看表象,呵呵,這是因為Add這個函數太簡單了,現在假設函數需要你傳入一個int型別的指標(int*),可以透過函式庫中的byref關鍵字來實現,假設現在所呼叫的函數的第三個參數是個int型別的指標。
intPara = c_int(9) dll.sub(23, 102, byref(intPara)) print intPara.value
如果要傳入一個char緩衝區指針,和緩衝區長度,方法至少有四種:
# 方法1 szPara = create_string_buffer('/0'*100) dll.PrintInfo(byref(szPara), 100); print szPara.value # 方法2 sBuf = 'aaaaaaaaaabbbbbbbbbbbbbb' pStr = c_char_p( ) pStr.value = sBuf #pVoid = ctypes.cast( pStr, ctypes.c_void_p ).value dll.PrintInfo(pStr, len(pStr.value)) print pStr.value # 方法3 strMa = "/0"*20 FunPrint = dll.PrintInfo FunPrint.argtypes = [c_char_p, c_int] #FunPrint.restypes = c_void_p nRst = FunPrint(strMa, len(strMa)) print strMa,len(strMa) # 方法4 pStr2 = c_char_p("/0") print pStr2.value #pVoid = ctypes.cast( pStr, ctypes.c_void_p ).value dll.PrintInfo(pStr2, len(pStr.value)) print pStr2.value
3、C基本型別和ctypes中實作的型別對應表
ctypes資料類型 C資料類型
c_char short
c_int long
c_ulong float
c_double void
對應的指標型別是在後面加上"_p",如int*是c_int_p等等。
在python中要實作c語言中的結構,需要用到類別。
4、DLL中的函數傳回一個指標。
雖然這不是個好的程式設計方法,不過這種情況的處理方法也很簡單,其實回傳的都是地址,把他們轉換對應的python類型,再透過value屬性存取。
pchar = dll.getbuffer() szbuffer = c_char_p(pchar) print szbuffer.value
範例:
Python程式碼如下:
def CreateGUID(): """ 创建一个全局唯一标识符 类似:E06093E2-699A-4BF2-A325-4F1EADB50E18 NewVersion """ try: # dll path strDllPath = sys.path[0] + str(os.sep) + "createguid.dll" dll = CDLL(strDllPath) b = dll.newGUID() a = c_char_p(b) except Exception, error: print error return "" return a.value
範例2:
這個範例是呼叫kernel32.dll中的createprocessA函數來啟動一個記事本進程。
from ctypes import * # 定义_PROCESS_INFORMATION结构体 class _PROCESS_INFORMATION(Structure): _fields_ = [('hProcess', c_void_p), ('hThread', c_void_p), ('dwProcessId', c_ulong), ('dwThreadId', c_ulong)] # 定义_STARTUPINFO结构体 class _STARTUPINFO(Structure): _fields_ = [('cb',c_ulong), ('lpReserved', c_char_p), ('lpDesktop', c_char_p), ('lpTitle', c_char_p), ('dwX', c_ulong), ('dwY', c_ulong), ('dwXSize', c_ulong), ('dwYSize', c_ulong), ('dwXCountChars', c_ulong), ('dwYCountChars', c_ulong), ('dwFillAttribute', c_ulong), ('dwFlags', c_ulong), ('wShowWindow', c_ushort), ('cbReserved2', c_ushort), ('lpReserved2', c_char_p), ('hStdInput', c_ulong), ('hStdOutput', c_ulong), ('hStdError', c_ulong)] NORMAL_PRIORITY_CLASS = 0x00000020 #定义NORMAL_PRIORITY_CLASS kernel32 = windll.LoadLibrary("kernel32.dll") #加载kernel32.dll CreateProcess = kernel32.CreateProcessA #获得CreateProcess函数地址 ReadProcessMemory = kernel32.ReadProcessMemory #获得ReadProcessMemory函数地址 WriteProcessMemory = kernel32.WriteProcessMemory #获得WriteProcessMemory函数地址 TerminateProcess = kernel32.TerminateProcess # 声明结构体 ProcessInfo = _PROCESS_INFORMATION() StartupInfo = _STARTUPINFO() fileName = 'c:/windows/notepad.exe' # 要进行修改的文件 address = 0x0040103c # 要修改的内存地址 strbuf = c_char_p("_") # 缓冲区地址 bytesRead = c_ulong(0) # 读入的字节数 bufferSize = len(strbuf.value) # 缓冲区大小 # 创建进程 CreateProcess(fileName, 0, 0, 0, 0, NORMAL_PRIORITY_CLASS,0, 0, byref(StartupInfo), byref(ProcessInfo))
以上是python如何呼叫dll函式庫的詳細內容。更多資訊請關注PHP中文網其他相關文章!