Python使用ctypes調(diào)用Windows API清空回收站
引言
很多朋友剛接觸 Windows 編程時(shí),總覺(jué)得調(diào)用系統(tǒng)底層 API 是一件很高深、非常復(fù)雜的事情,感到很害怕。其實(shí) Python 自帶的 ctypes 庫(kù)就能讓咱們輕松調(diào)用 C 語(yǔ)言導(dǎo)出的動(dòng)態(tài)鏈接庫(kù)函數(shù),本文就以實(shí)現(xiàn) Windows 上的 “清空回收站”功能為例,從零開(kāi)始詳解 Python 如何通過(guò) ctypes 庫(kù)跟 Windows API 進(jìn)行交互調(diào)用。
一、整體思路
清空回收站,不能直接調(diào)用 SHEmptyRecycleBin 就完事了,我們要編寫一個(gè)更智能更穩(wěn)健的程序,能準(zhǔn)確判斷是否真正成功清空:
- 清空前先查詢回收站里有多少文件,就知道有沒(méi)有必要清空了。
- 調(diào)用 Windows 提供的清空回收站 API,執(zhí)行清理。
- 清空后再次查詢文件數(shù)量,對(duì)比前后變化,判斷用戶是真的清空了,還是中途取消了(比如在系統(tǒng)彈出的確認(rèn)對(duì)話框里點(diǎn)了“否”)。
這種執(zhí)行后查詢驗(yàn)證結(jié)果的思路,在實(shí)際開(kāi)發(fā)中非常實(shí)用,我們不能盲目依賴 API 的返回值,比如 SHEmptyRecycleBin 的返回值,僅表示函數(shù)是不是成功調(diào)用了,不表示用戶點(diǎn)擊了確定清空,或執(zhí)行過(guò)程中是否取消了操作,所以說(shuō),它的結(jié)果并不表示業(yè)務(wù)是否成功,也就是是否真正清空了回收站。
二、ctypes 是什么?怎么用?
ctypes 是 Python 內(nèi)置的庫(kù),這個(gè)庫(kù)能把 Python 的數(shù)據(jù)類型轉(zhuǎn)換成 C 語(yǔ)言的數(shù)據(jù)類型,然后調(diào)用 DLL 或共享庫(kù)中的函數(shù)。簡(jiǎn)單說(shuō)就是讓 Python 可以直接調(diào)用 Windows 系統(tǒng)底層函數(shù)。
2.1 加載 DLL
Windows API 大多存放在 kernel32.dll、user32.dll、shell32.dll 等系統(tǒng)核心動(dòng)態(tài)鏈接庫(kù)文件中。這里咱們要用的兩個(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ù)聲明(來(lái)自 Win32 SDK):
HRESULT SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags);
對(duì)應(yīng)到 Python 就是:
shell32.SHEmptyRecycleBinW.argtypes = [wintypes.HWND, wintypes.LPCWSTR, wintypes.DWORD] shell32.SHEmptyRecycleBinW.restype = ctypes.HRESULT
在這里
HWND是窗口句柄(可理解為窗口的身份證),傳None表示沒(méi)有父級(jí)窗口。LPCWSTR是寬字符串指針,對(duì)應(yīng) Python 的str(ctypes 會(huì)自動(dòng)轉(zhuǎn)成wchar_t*)。DWORD是 32 位無(wú)符號(hào)整數(shù)。HRESULT是一個(gè) 32 位整數(shù),一般來(lái)講 0(即S_OK)表示成功。
2.3 定義結(jié)構(gòu)體
很多 Windows API 都要傳結(jié)構(gòu)體,比如我們這里用到的查詢回收站信息函數(shù)要用到 SHQUERYRBINFO 結(jié)構(gòu)體。在 ctypes 中定義C語(yǔ)言結(jié)構(gòu)體還是相對(duì)比較容易的:只要繼承 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 語(yǔ)言的 __int64 對(duì)應(yīng) Python 的 ctypes.c_longlong(8 字節(jié)有符號(hào)整數(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ū)動(dòng)器回收站里的文件總數(shù)。
- 調(diào)用
SHQueryRecycleBinW(None, pointer_to_struct)
第一個(gè)參數(shù)傳None表示“查詢所有驅(qū)動(dòng)器的總回收站”。也可以傳"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表示沒(méi)有父窗口。 - 第二個(gè)參數(shù)
pszRootPath:None表示清空所有驅(qū)動(dòng)器的回收站。 - 第三個(gè)參數(shù)
dwFlags:0表示使用系統(tǒng)默認(rèn)行為,也就是彈出確認(rèn)對(duì)話框并顯示進(jìn)度。
聰明的你一定想到了:如果用戶在確認(rèn)對(duì)話框點(diǎn)了“否”,API 會(huì)返回什么?
經(jīng)過(guò)實(shí)測(cè),SHEmptyRecycleBinW 在這種情況下依然返回 S_OK(成功)!所以僅靠返回值無(wú)法區(qū)分“用戶取消了”和“真的清空了”。
因此我們的代碼要做更嚴(yán)謹(jǐn)?shù)呐袛啵?/p>
- 清空前記錄文件數(shù)
count_before。 - 調(diào)用清空 API。
- 清空后再次查詢文件數(shù)
count_after。 - 綜合判斷:
- 如果
hr == 0且count_after < count_before(文件數(shù)減少了),說(shuō)明真的刪除了,于是提示“清空成功”。 - 如果
hr == 0但count_after == count_before,說(shuō)明用戶取消了確認(rèn)對(duì)話框所以提示“操作已取消”。 - 如果
hr != 0,說(shuō)明 API 調(diào)用失?。ɡ鐧?quán)限不足),應(yīng)顯示錯(cuò)誤代碼。
- 如果
另,如果清空前文件數(shù)就是 0,直接彈窗告知“無(wú)需操作”。
五、完整代碼
可以直接把下面的代碼復(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ū)動(dòng)器回收站的文件總數(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ū)動(dòng)器
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)是空的,無(wú)需操作。", "提示", MB_OK | MB_ICONINFORMATION)
return
# 調(diào)用 API 執(zhí)行清空 (dwFlags=0, 顯示系統(tǒng)確認(rèn)對(duì)話框)
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ù)量沒(méi)變,說(shuō)明用戶在系統(tǒng)對(duì)話框點(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位的。
通過(guò)上面這個(gè)例子,就應(yīng)該意識(shí)到了,Python + ctypes 和 comtypes 幾乎可以調(diào)用所有 Windows API,借助Python 調(diào)用 C dll 把系統(tǒng)底層能力融入到自己的程序中,其實(shí)也沒(méi)有想象中的那么難。
以上就是Python使用ctypes調(diào)用Windows API清空回收站的詳細(xì)內(nèi)容,更多關(guān)于Python ctypes調(diào)用Windows API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何使用 Python 中的功能和庫(kù)創(chuàng)建 n-gram的過(guò)程
在計(jì)算語(yǔ)言學(xué)中,n-gram 對(duì)于語(yǔ)言處理、上下文和語(yǔ)義分析非常重要,本文將討論如何使用 Python 中的功能和庫(kù)創(chuàng)建 n-gram,感興趣的朋友一起看看吧2023-09-09
如何利用python在剪貼板上讀取/寫入數(shù)據(jù)
說(shuō)起處理數(shù)據(jù)就離不開(kāi)導(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é),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(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)試,并描述了如何通過(guò)修改`launch.json`文件來(lái)簡(jiǎn)化調(diào)試過(guò)程2025-01-01
Python使用psycopg2連接PostgreSQL數(shù)據(jù)庫(kù)的步驟
PostgreSQL 是一個(gè)廣泛使用的開(kāi)源對(duì)象關(guān)系數(shù)據(jù)庫(kù)系統(tǒng),以其強(qiáng)大的功能和靈活性而聞名,Python,作為一種流行的編程語(yǔ)言,提供了多種方式與數(shù)據(jù)庫(kù)交互,其中 psycopg2 是連接 PostgreSQL 數(shù)據(jù)庫(kù)的流行選擇之一,本文介紹了Python使用psycopg2連接PostgreSQL數(shù)據(jù)庫(kù)的步驟2024-12-12
使用Python對(duì)Excel數(shù)據(jù)讀取與保存的全面指南
在數(shù)據(jù)分析與處理工作中,Excel文件是最常見(jiàn)的數(shù)據(jù)源之一,本文將詳細(xì)介紹如何使用Python的Pandas庫(kù)進(jìn)行Excel文件的讀寫操作,涵蓋常用函數(shù)、典型應(yīng)用場(chǎng)景、實(shí)例演示及常見(jiàn)問(wèn)題解決方案,需要的朋友可以參考下2025-12-12

