首頁 >後端開發 >Python教學 >python如何呼叫dll函式庫

python如何呼叫dll函式庫

爱喝马黛茶的安东尼
爱喝马黛茶的安东尼原創
2019-06-13 11:51:1326805瀏覽

在python中某些時候需要C做效率上的補充,在實際應用中,需要做部分數據的交互。使用python中的ctypes模組可以很方便的調用windows的dll(也包括linux下的so等文件),下面將詳細的講解這個模組(以windows平台為例子),當然我假設你們已經對windows下怎麼寫一個DLL是沒有問題的。

python如何呼叫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

5、處理C中的結構體類型 
為什麼把這個單獨提出來說呢,因為這個是最麻煩也是最複雜的,在python裡面申明一個類似c的結構體,要用到類,而這個類別必須繼承自Structure。

範例:
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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn