Python使用ctypes調(diào)用Windows API清空回收站
引言
很多朋友剛接觸 Windows 編程時(shí),總覺得調(diào)用系統(tǒng)底層 API 是一件很高深、非常復(fù)雜的事情,感到很害怕。其實(shí) Python 自帶的 ctypes 庫就能讓咱們輕松調(diào)用 C 語言導(dǎo)出的動態(tài)鏈接庫函數(shù),本文就以實(shí)現(xiàn) Windows 上的 “清空回收站”功能為例,從零開始詳解 Python 如何通過 ctypes 庫跟 Windows API 進(jìn)行交互調(diào)用。
一、整體思路
清空回收站,不能直接調(diào)用 SHEmptyRecycleBin 就完事了,我們要編寫一個(gè)更智能更穩(wěn)健的程序,能準(zhǔn)確判斷是否真正成功清空:
- 清空前先查詢回收站里有多少文件,就知道有沒有必要清空了。
- 調(diào)用 Windows 提供的清空回收站 API,執(zhí)行清理。
- 清空后再次查詢文件數(shù)量,對比前后變化,判斷用戶是真的清空了,還是中途取消了(比如在系統(tǒng)彈出的確認(rèn)對話框里點(diǎn)了“否”)。
這種執(zhí)行后查詢驗(yàn)證結(jié)果的思路,在實(shí)際開發(fā)中非常實(shí)用,我們不能盲目依賴 API 的返回值,比如 SHEmptyRecycleBin 的返回值,僅表示函數(shù)是不是成功調(diào)用了,不表示用戶點(diǎn)擊了確定清空,或執(zhí)行過程中是否取消了操作,所以說,它的結(jié)果并不表示業(yè)務(wù)是否成功,也就是是否真正清空了回收站。
二、ctypes 是什么?怎么用?
ctypes 是 Python 內(nèi)置的庫,這個(gè)庫能把 Python 的數(shù)據(jù)類型轉(zhuǎn)換成 C 語言的數(shù)據(jù)類型,然后調(diào)用 DLL 或共享庫中的函數(shù)。簡單說就是讓 Python 可以直接調(diào)用 Windows 系統(tǒng)底層函數(shù)。
2.1 加載 DLL
Windows API 大多存放在 kernel32.dll、user32.dll、shell32.dll 等系統(tǒng)核心動態(tài)鏈接庫文件中。這里咱們要用的兩個(gè)函數(shù)都在 shell32.dll 里。
import ctypes shell32 = ctypes.WinDLL('shell32', use_last_error=True) WinDLL表示加載一個(gè) Windows DLL,默認(rèn)使用 stdcall 調(diào)用約定(Windows API 的標(biāo)準(zhǔn))。use_last_error=True讓 ctypes 在出錯(cuò)時(shí)保存 Windows 錯(cuò)誤碼,方便調(diào)試。
2.2 定義函數(shù)原型(參數(shù)類型和返回值類型)
這是新手最容易踩坑的地方。Windows API 函數(shù)是用 C 寫的,Python 并不知道它接收什么參數(shù),所以我們必須顯式聲明參數(shù)類型(argtypes)和返回值類型(restype)。
例如咱們用到的清空函數(shù)聲明(來自 Win32 SDK):
HRESULT SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags);
對應(yīng)到 Python 就是:
shell32.SHEmptyRecycleBinW.argtypes = [wintypes.HWND, wintypes.LPCWSTR, wintypes.DWORD] shell32.SHEmptyRecycleBinW.restype = ctypes.HRESULT
在這里
HWND是窗口句柄(可理解為窗口的身份證),傳None表示沒有父級窗口。LPCWSTR是寬字符串指針,對應(yīng) Python 的str(ctypes 會自動轉(zhuǎn)成wchar_t*)。DWORD是 32 位無符號整數(shù)。HRESULT是一個(gè) 32 位整數(shù),一般來講 0(即S_OK)表示成功。
2.3 定義結(jié)構(gòu)體
很多 Windows API 都要傳結(jié)構(gòu)體,比如我們這里用到的查詢回收站信息函數(shù)要用到 SHQUERYRBINFO 結(jié)構(gòu)體。在 ctypes 中定義C語言結(jié)構(gòu)體還是相對比較容易的:只要繼承 ctypes.Structure,然后寫一個(gè) _fields_ 列表,每個(gè)元素是 (字段名, 字段類型)。
class SHQUERYRBINFO(ctypes.Structure):
_fields_ = [
("cbSize", wintypes.DWORD), # 結(jié)構(gòu)體自身大小
("i64Size", ctypes.c_longlong), # 總大?。ㄗ止?jié))
("i64NumItems", ctypes.c_longlong) # 項(xiàng)目總數(shù)
]C 語言的 __int64 對應(yīng) Python 的 ctypes.c_longlong(8 字節(jié)有符號整數(shù))。
另外,考慮到向后兼容,這個(gè)結(jié)構(gòu)體的第一個(gè)成員 cbSize 必須在調(diào)用前賦值為結(jié)構(gòu)體占用的字節(jié)數(shù),很多 Windows API 都這樣設(shè)計(jì)。
rb_info = SHQUERYRBINFO() rb_info.cbSize = ctypes.sizeof(SHQUERYRBINFO)
三、查詢回收站信息
封裝一個(gè) get_recycle_bin_count() 函數(shù),返回所有驅(qū)動器回收站里的文件總數(shù)。
- 調(diào)用
SHQueryRecycleBinW(None, pointer_to_struct)
第一個(gè)參數(shù)傳None表示“查詢所有驅(qū)動器的總回收站”。也可以傳"C:\\"這樣的路徑,查詢指定盤。 - 第二個(gè)參數(shù)需要傳結(jié)構(gòu)體的指針,用
ctypes.byref()獲得。 - 函數(shù)執(zhí)行后,結(jié)構(gòu)體的
i64NumItems字段就被填上了項(xiàng)目總數(shù)。
hr = shell32.SHQueryRecycleBinW(None, ctypes.byref(rb_info))
if hr == 0: # 成功
return rb_info.i64NumItems
else:
return -1為什么函數(shù)名最后總要帶上 W 字母?
那是因?yàn)?Windows API 有兩套字符編碼:A(ANSI)和 W(Unicode)?,F(xiàn)代 Windows 內(nèi)部完全使用 Unicode,所以咱們直接調(diào)用 W 版本,用 Python 的 str 傳參即可。
四、調(diào)用函數(shù)清空回收站
hr = shell32.SHEmptyRecycleBinW(None, None, 0)
- 第一個(gè)參數(shù)
hwnd:None表示沒有父窗口。 - 第二個(gè)參數(shù)
pszRootPath:None表示清空所有驅(qū)動器的回收站。 - 第三個(gè)參數(shù)
dwFlags:0表示使用系統(tǒng)默認(rèn)行為,也就是彈出確認(rèn)對話框并顯示進(jìn)度。
聰明的你一定想到了:如果用戶在確認(rèn)對話框點(diǎn)了“否”,API 會返回什么?
經(jīng)過實(shí)測,SHEmptyRecycleBinW 在這種情況下依然返回 S_OK(成功)!所以僅靠返回值無法區(qū)分“用戶取消了”和“真的清空了”。
因此我們的代碼要做更嚴(yán)謹(jǐn)?shù)呐袛啵?/p>
- 清空前記錄文件數(shù)
count_before。 - 調(diào)用清空 API。
- 清空后再次查詢文件數(shù)
count_after。 - 綜合判斷:
- 如果
hr == 0且count_after < count_before(文件數(shù)減少了),說明真的刪除了,于是提示“清空成功”。 - 如果
hr == 0但count_after == count_before,說明用戶取消了確認(rèn)對話框所以提示“操作已取消”。 - 如果
hr != 0,說明 API 調(diào)用失?。ɡ鐧?quán)限不足),應(yīng)顯示錯(cuò)誤代碼。
- 如果
另,如果清空前文件數(shù)就是 0,直接彈窗告知“無需操作”。
五、完整代碼
可以直接把下面的代碼復(fù)制保存為 empty_recycle_bin.pyw 雙擊運(yùn)行,需要安裝 Python3。
empty_recycle_bin.pyw:
import ctypes
from ctypes import wintypes
# <-定義結(jié)構(gòu)體 ->
# SHQUERYRBINFO 結(jié)構(gòu)體用于接收回收站信息
class SHQUERYRBINFO(ctypes.Structure):
_fields_ = [
("cbSize", wintypes.DWORD), # 結(jié)構(gòu)體大小
("i64Size", ctypes.c_longlong), # 回收站內(nèi)文件總大小 (字節(jié))
("i64NumItems", ctypes.c_longlong) # 回收站內(nèi)項(xiàng)目總數(shù)
]
# <- 定義常量 ->
MB_OK = 0x00000000
MB_ICONINFORMATION = 0x00000040
MB_ICONWARNING = 0x00000030
MB_ICONERROR = 0x00000010
def get_recycle_bin_count():
"""獲取所有驅(qū)動器回收站的文件總數(shù)"""
shell32 = ctypes.WinDLL('shell32', use_last_error=True)
# 定義 SHQueryRecycleBinW
# HRESULT SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBINFO);
shell32.SHQueryRecycleBinW.argtypes = [wintypes.LPCWSTR, ctypes.POINTER(SHQUERYRBINFO)]
shell32.SHQueryRecycleBinW.restype = ctypes.HRESULT
rb_info = SHQUERYRBINFO()
rb_info.cbSize = ctypes.sizeof(SHQUERYRBINFO)
# pszRootPath 為 None 表示查詢所有驅(qū)動器
hr = shell32.SHQueryRecycleBinW(None, ctypes.byref(rb_info))
if hr == 0:
return rb_info.i64NumItems
return -1
def empty_recycle_bin():
shell32 = ctypes.WinDLL('shell32', use_last_error=True)
user32 = ctypes.WinDLL('user32', use_last_error=True)
# 定義 SHEmptyRecycleBinW
shell32.SHEmptyRecycleBinW.argtypes = [wintypes.HWND, wintypes.LPCWSTR, wintypes.DWORD]
shell32.SHEmptyRecycleBinW.restype = ctypes.HRESULT
# 記錄執(zhí)行前的項(xiàng)目數(shù)
count_before = get_recycle_bin_count()
if count_before == 0:
user32.MessageBoxW(None, "回收站已經(jīng)是空的,無需操作。", "提示", MB_OK | MB_ICONINFORMATION)
return
# 調(diào)用 API 執(zhí)行清空 (dwFlags=0, 顯示系統(tǒng)確認(rèn)對話框)
hr = shell32.SHEmptyRecycleBinW(None, None, 0)
# 記錄執(zhí)行后的項(xiàng)目數(shù)
count_after = get_recycle_bin_count()
# 綜合判斷
# 邏輯:API必須成功 AND (文件數(shù)減少了 OR 文件數(shù)變?yōu)榱?)
if hr == 0:
if count_after < count_before and count_after >= 0:
user32.MessageBoxW(None, f"清空成功!\n文件數(shù)從 {count_before} 降至 {count_after}。", "成功", MB_OK | MB_ICONINFORMATION)
elif count_before != -1 and count_after == count_before:
# API 返回了成功,但數(shù)量沒變,說明用戶在系統(tǒng)對話框點(diǎn)了“否”
user32.MessageBoxW(None, "操作已取消或未執(zhí)行刪除。", "提示", MB_OK | MB_ICONWARNING)
else:
user32.MessageBoxW(None, "回收站狀態(tài)未發(fā)生顯著變化。", "提示", MB_OK | MB_ICONWARNING)
else:
# 如果返回了非 0 的 HRESULT
err_hex = hex(hr & 0xFFFFFFFF)
user32.MessageBoxW(None, f"操作失敗。錯(cuò)誤代碼: {err_hex}", "錯(cuò)誤", MB_OK | MB_ICONERROR)
if __name__ == "__main__":
empty_recycle_bin()注: 執(zhí)行腳本的 “python.exe” 要和調(diào)用的 DLL 位數(shù)相同,比如都是64位的。
通過上面這個(gè)例子,就應(yīng)該意識到了,Python + ctypes 和 comtypes 幾乎可以調(diào)用所有 Windows API,借助Python 調(diào)用 C dll 把系統(tǒng)底層能力融入到自己的程序中,其實(shí)也沒有想象中的那么難。
以上就是Python使用ctypes調(diào)用Windows API清空回收站的詳細(xì)內(nèi)容,更多關(guān)于Python ctypes調(diào)用Windows API的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何使用 Python 中的功能和庫創(chuàng)建 n-gram的過程
在計(jì)算語言學(xué)中,n-gram 對于語言處理、上下文和語義分析非常重要,本文將討論如何使用 Python 中的功能和庫創(chuàng)建 n-gram,感興趣的朋友一起看看吧2023-09-09
如何利用python在剪貼板上讀取/寫入數(shù)據(jù)
說起處理數(shù)據(jù)就離不開導(dǎo)入導(dǎo)出,而我們使用Pandas時(shí)候最常用的就是read_excel、read_csv了,下面這篇文章主要給大家介紹了關(guān)于如何利用python在剪貼板上讀取/寫入數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2022-07-07
Python數(shù)據(jù)分析之使用scikit-learn構(gòu)建模型
這篇文章主要介紹了Python數(shù)據(jù)分析之使用scikit-learn構(gòu)建模型,sklearn提供了model_selection模型選擇模塊、preprocessing數(shù)據(jù)預(yù)處理模塊、decompisition特征分解模塊,更多相關(guān)內(nèi)容需要朋友可以參考下面文章內(nèi)容2022-08-08
記錄一下scrapy中settings的一些配置小結(jié)
這篇文章主要介紹了記錄一下scrapy中settings的一些配置小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
python中28種極坐標(biāo)繪圖函數(shù)總結(jié)
這篇文章主要為大家詳細(xì)介紹了python中28種極坐標(biāo)繪圖函數(shù)的用法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起了解一下2023-09-09
vscode帶命令行參數(shù)進(jìn)行調(diào)試的方法
文章介紹了如何在VSCode中使用命令行參數(shù)進(jìn)行調(diào)試,并描述了如何通過修改`launch.json`文件來簡化調(diào)試過程2025-01-01
Python使用psycopg2連接PostgreSQL數(shù)據(jù)庫的步驟
PostgreSQL 是一個(gè)廣泛使用的開源對象關(guān)系數(shù)據(jù)庫系統(tǒng),以其強(qiáng)大的功能和靈活性而聞名,Python,作為一種流行的編程語言,提供了多種方式與數(shù)據(jù)庫交互,其中 psycopg2 是連接 PostgreSQL 數(shù)據(jù)庫的流行選擇之一,本文介紹了Python使用psycopg2連接PostgreSQL數(shù)據(jù)庫的步驟2024-12-12
使用Python對Excel數(shù)據(jù)讀取與保存的全面指南
在數(shù)據(jù)分析與處理工作中,Excel文件是最常見的數(shù)據(jù)源之一,本文將詳細(xì)介紹如何使用Python的Pandas庫進(jìn)行Excel文件的讀寫操作,涵蓋常用函數(shù)、典型應(yīng)用場景、實(shí)例演示及常見問題解決方案,需要的朋友可以參考下2025-12-12

