如何用 Win32 APIs 枚舉應用程式視窗和進程

來源:互聯網
上載者:User

如何用 Win32 APIs 枚舉應用程式視窗和進程
http://www.vckbase.com/document/viewdoc/?id=1482
編譯:NorthTibet

下載原始碼

枚舉頂層(top-level)視窗
枚舉進程
用 ToolHelp32 庫枚舉進程
用 PSAPI 枚舉進程
16位進程的處理方法
關於代碼
參考資料

摘要

  我們在編寫程式時,常常遇到的一件事情就是要準確列出系統中所有正在啟動並執行程式或者進程。Windows 工作管理員就是這樣的一個程式。它既能列出啟動並執行傳統型應用程式,又能列出系統中所有啟動並執行進程。那麼,我們在程式中如何?這樣的任務呢?本文下面將詳細討論這個問題。

枚舉頂層(top-level)視窗

  枚舉案頭頂層視窗相對於枚舉進程來說可能要容易一些。枚舉案頭頂層視窗的方法是用 EnumWindows() 函數。不要用 GetWindow()來建立視窗列表,因為視窗之間複雜的父子及同胞關係(Z-Order)容易造成混亂而使得枚舉結果不準確。
  EnumWindows()有兩個參數,一個是指向回呼函數的指標,一個是使用者定義的 LPARAM 值, 針對每個桌面視窗(或者頂層視窗)它調用回呼函數一次。然後回呼函數用該視窗控制代碼做一些處理,比如將它添加到列表中。這個方法保證枚舉結果不會被視窗複雜的層次關係搞亂,因此,一旦有了視窗控制代碼,我們就可以通過 GetWindowText() 得到視窗標題。

枚舉進程

  建立系統進程列表比枚舉視窗稍微複雜一些。這主要是因為所用的 API 函數對於不同的 Win32 作業系統有依賴性。在 Windows 9x、Windows Me、Windows 2000 Professional 以及 Windows XP 中,我們可以用 ToolHelp32 庫中的 APIs 函數。但是在 Windows NT 裡,我們必須用 PSAPI 庫中的 APIs 函數, PSAPI 庫是 SDK 的一部分。本文我們將討論上述所有平台中的實現。附帶的例子程式將對上述庫中的 APIs 進行封裝,以便封裝後的函數能支援所有 Win32 作業系統。

使用 ToolHelp32 庫枚舉進程

  ToolHelp32 庫函數在 KERNEL32.dll 中,它們都是標準的 API 函數。但是 Windows NT 4.0 不提供這些函。
  ToolHelp32 庫中有各種各樣的函數可以用來枚舉系統中的進程、線程以及擷取記憶體和模組資訊。其中枚舉進程 只需用如下三個的函數:CreateToolhelp32Snapshot()、Process32First()和 Process32Next()。
  使用 ToolHelp32 函數的第一步是用 CreateToolhelp32Snapshot() 函數建立系統資訊“快照”。這個函數可以讓你選擇儲存在快照中的資訊類型。如果你只是對進程資訊感興趣,那麼只要包含 TH32CS_SNAPPROCESS 標誌即可。 CreateToolhelp32Snapshot() 函數返回一個 HANDLE,完成調用之後,必須將此 HANDLE 傳給 CloseHandle()。
  接下來是調用一次 Process32First 函數,從快照中擷取進程列表,然後重複調用 Process32Next,直到函數返回 FALSE 為止。這樣將遍曆快照中進程列表。這兩個函數都帶兩個參數,它們分別是快照控制代碼和一個   PROCESSENTRY32 結構。
  調用完 Process32First 或 Process32Next 之後,PROCESSENTRY32 中將包含系統中某個進程的關鍵資訊。其中進程 ID 就儲存在此結構的 th32ProcessID。此 ID 可以被傳給 OpenProcess() API 以獲得該進程的控制代碼。對應的可執行檔名及其存放路徑存放在 szExeFile  結構成員中。在該結構中還可以找到其它一些有用的資訊。
  注意:在調用 Process32First() 之前,一定要記住將 PROCESSENTRY32  結構的 dwSize 成員設定成 sizeof(PROCESSENTRY32)。

使用 PSAPI 庫枚舉進程

  在 Windows NT 中,建立進程列表使用 PSAPI 函數,這些函數在 PSAPI.DLL 中。這個檔案是隨 Platform SDK 一起分發的,最新版本的 Platform SDK 可以從這裡下載:

使用這個庫所需的 PSAPI.h 和 PSAPI.lib 檔案也在該 Platform SDK 中。
  為了使用 PSAPI 庫中的函數,需將 PSAPI.lib 添加到代碼項目中,同時在所有調用 PSAPI API 的模組中包含 PSAPI.h 檔案。記住一定要隨可執行檔一起分發 PSAPI.DLL,因為它不隨 Windows NT 一起分發。你可以點擊這裡單獨下載 PSAPI.DLL 的可分發版本(不用完全下載 Platform SDK)。
  與 ToolHelp32 一樣,PSAPI 庫也包含各種各樣有用的函數。由於篇幅所限,本文只討論與枚舉進程有關函數:EnumProcesses()、EnumProcessModules()、GetModuleFileNameEx()和 GetModuleBaseName()。
  建立進程列表的第一步是調用 EnumProcesses()。該函數的聲明如下:

BOOL EnumProcesses( DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded );

  EnumProcesses()帶三個參數,DWORD 類型的數組指標 lpidProcess;該數組的大小尺寸 cb;以及一個指向 DWORD 的指標 cbNeeded,它接收返回資料的長度。DWORD 數組用於儲存當前啟動並執行進程 IDs。cbNeeded 返回數組所用的記憶體大小。下面算式可以得出返回了多少進程:nReturned = cbNeeded / sizeof(DWORD)。
  注意:雖然文檔將返回的 DWORD 命名為“cbNeeded”,實際上是沒有辦法知道到底要傳多大的數組的。EnumProcesses()根本不會在 cbNeeded 中返回一個大於 cb 參數傳遞的數組值。結果,唯一確保 EnumProcesses()函數成功的方法是分配一個 DWORD 數組,並且,如果返回的 cbNeeded 等於 cb,分配一個較大的數組,並不停地嘗試直到 cbNeeded 小於 cb
  現在,你獲得了一個數組,其元素儲存著系統中每個進程的ID。如果你要想擷取進程名,那麼你必須首先擷取一個控制代碼。要想從進程 ID 得到控制代碼,就得調用 OpenProcess()。
  一旦有了控制代碼,則需要得到該進程的第一個模組。為此調用 EnumProcessModules() API:

EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbReturned );

  調用之後,hModule 變數中儲存的將是進程中的第一個模組。記住進程其實沒有名字,但進程的第一個模組既是該進程的可執行模組。現在你可以用 hModule 中返回的模組控制代碼調用 GetModuleFileNameEx() 或 GetModuleBaseName() API 函數擷取全路徑名,或者僅僅是進程可執行模組名。兩個函數均帶四個參數:進程控制代碼,模組控制代碼,返回名字的緩衝指標以及緩衝大小尺寸。
  用 EnumProcesses() API 返回的每一個進程 ID 重複這個調用過程,你便可以建立 Windows NT 的進程列表。

16位進程的處理方法

  在 Windows 95,Windows 98 和 Windows ME 中,ToolHelp32 對待16位程式一視同仁,它們與 Win32 程式一樣有自己的進程IDs。但是在 Windows NT,Windows 2000 或 Windows XP 中情況並不是這樣。在這些作業系統中,16位程式運行在所謂的 VDM 當中(也就是DOS機)。
  為了在 Windows NT,Windows 2000 和 Windows XP 中枚舉16位程式,你必須使用一個名為 VDMEnumTaskWOWEx()的函數。在原始碼模組中必須包含 VDMDBG.h,並且 VDMDBG.lib 檔案必須與項目連結。這兩個檔案都在 Platform SDK 中。該函數的聲明如下:

INT WINAPI VDMEnumTaskWOWEx( DWORD dwProcessId, TASKENUMPROCEX fp,LPARAM lparam );

  此處 dwProcessId 是 NTVDM 中擬枚舉的16位任務進程標示符。參數 fp 是回調枚舉函數的指標。參數 lparam 是使用者定義的值,它被傳遞到枚舉函數。枚舉函數應該被定義成如下這樣:

BOOL WINAPI Enum16( DWORD dwThreadId,                        WORD hMod16,                        WORD hTask16,                        PSZ pszModName,                        PSZ pszFileName,                        LPARAM lpUserDefined );

  該函數針對每個運行在 NTVDM 進程中的16位任務調用一次,NTVDM 進程ID將被傳入 VDMEnumTaskWOWEx()。如果想繼續枚舉則返回 FALSE,終止枚舉則返回 TRUE。注意這是與 EnumWindows()相對的。

關於代碼

  本文附帶的代碼例子將 PSAPI 和 ToolHelp32 封裝到一個名為 EnumProcs() 的函數中。該函數的工作原理類似 EnumWindows(),有一個指向回呼函數的指標,並要對該函數進行重複調用,針對系統中的每個進程調用一次。另一個參數是使用者定義的 lParam。下面是該函數的聲明:

BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam );

使用該函數時,要象下面這樣聲明回呼函數:

BOOL CALLBACK Proc( DWORD dw, WORD w16, LPCSTR lpstr, LPARAM lParam );

  參數 dw 包含 ID,“w16”是16位任務的任務號,如果為32位進程則為0(在 Windows 95 中總是0),參數lpstr 指向檔案名稱,lParam 是使用者定義的,要被傳入 EnumProcs()。
  EnumProcs() 函數通過顯示連結使用 ToolHelp32 和 PSAPI,而非通常所用的隱式連結。之所以要這樣做,主要是為了讓代碼能夠在二進位一級相容,從可以在所有 Win32 作業系統平台上運行。

參考資料

  • MSJ“Under the Hood”1996/08 By Pietrek, Matt. 
  • MSJ“Under the Hood”1996/11 By Pietrek, Matt.
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.