讀者朋友們可能經常會碰到這樣一個問題,想對某些進行操作時,發現這些文件正在被其它程序使用,處於打開狀態,而且是被獨占打開,這時是沒法對文件進行操作的。因此,要想操作這些文件,必須將打開這些文件的進程清除掉。那麼如何干淨地清除進程呢?其實,在Windows2000操作系統版本中有一個工具程序叫tskill.exe,用它就可以清除掉某個程序的進程,在輸入"tskill 程序名"後就可以清除其運行實例。但是如何要在代碼裡實現tskill的功能該如何做呢?針對這一問題,本實例介紹了在Windows2000下實現的方法。
一、實現方法
在Visual C++編程中,最安全的殺死進程的方法是向運行程序的主窗口發送WM_CLOSE消息,其實現代碼如下:
HWND hwnd =this.m_hWnd; // 獲得主窗口
PostMessage(hwnd, WM_CLOSE, 0, 0);
發送此消息後,通常應該等待直到進程確實終止,當進程終止時,它發出狀態信號,並且 WaitForSingleObject 返回WAIT_OBJECT_0。如果返回別的值,進程要麼掛起了,要麼仍然在進行處理。在這種情況下,殺死這個進程的唯一方法是用功能更強大的API函數:TerminateProcess()。如果想干得漂亮一點,可以在關閉之前向主窗口發送一個WM_QUERYENDSESSION消息,當用戶結束會話(log out)或者調用ExitWindows()函數時,應用程序會收到這個消息,然後准備退出進程,此時一般都會彈出一個確認對話框,告訴用戶:"程序要推出了,如果要保存修改的東西,現在是最佳時機,想保存嗎?"有三種選擇(Yes/No/Cancel)。此外,發送WM_QUERYENDSESSION消息可以拒絕推出進程(按下"Cancel鍵"),如果是這樣,進程將會延續。
如果想要關閉的進程被掛起,使用SendMessageTimeout()函數就非常重要,而不是用SendMessage()函數,其參數SMTO_NOTIMEOUTIFNOTHUNG是一個只有Windows 2000 和Windows XP才有的標志。其意義是"如果線程沒有掛起,不要超時",換句話說就是如果線程正在進行正常處理,那麼永遠等待,以便用戶能看到對話框並決定做什麼,當用戶最終做出決定後,SendMessageTimeout()將帶著相應的bOKToKill值返回。
本例為了增強代碼的可重用性,將實現細節都封裝在一個叫CFindKillProcess的類中,包括查找和殺死進程,詳情請參見EnumProc.h和EnumProc.cpp文件。文件中還有另外兩個可重用類,一個是CProcessIterator,另一個是CWindowIterator。這在實例《獲取進程的主窗口以及創建進程的程序名》中有過詳細的敘述。
CfindKillProcess類的成員函數FindProcess()查找某個進程序,如果找到這個進程,它返回此進程的ID,然後將此ID傳給CFindKillProcess::KillProcess()函數,KillProcess()函數封裝了關閉窗口以及終止邏輯,它利用CmainWindowIterator類對象來枚舉進程的主窗口(可能不止一個,見"如何獲取某個進程的主窗口以及創建進程的程序名?"),並發送WM_CLOSE到每一個窗口,然後等待進程死亡。它有一個布爾型參數用來指示當應用程序進程不願意退出時是否執行TerminateProcess()函數。詳細細節請參見下載的代碼。
二、編程步驟
1、啟動Visual C++6.0,生成一個控制台應用程序,將該程序命名為"kp";
2、在程序代碼中添加CfindKillProcess、CProcessIterator類的定義;
3、添加代碼,編譯運行程序。
三、程序代碼
////////////////////////////////////////////////////// #pragma once ////////////////// // Process iterator -- iterator over all system processes // Always skips the first (IDLE) process with PID=0. class CProcessIterator { protected: DWORD* m_pids; // array of procssor IDs DWORD m_count; // size of array DWORD m_current; // next array item public: CProcessIterator(); ~CProcessIterator(); DWORD First(); DWORD Next() { return m_pids && m_current < m_count ? m_pids[m_current++] : 0; } DWORD GetCount() { return m_count; } }; ////////////////// // Handy class to facilitate finding and killing a process by name. class CFindKillProcess { public: CFindKillProcess(); ~CFindKillProcess(); DWORD FindProcess(LPCTSTR lpModname, BOOL bAddExe=TRUE); BOOL KillProcess(DWORD pid, BOOL bZap); }; //////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "EnumProc.h" // CProcessIterator - Iterates all processes CProcessIterator::CProcessIterator() { m_pids = NULL; } CProcessIterator::~CProcessIterator() { delete [] m_pids; } ////////////////// // Get first process: Call EnumProcesses to init array. Return first one. DWORD CProcessIterator::First() { m_current = (DWORD)-1; m_count = 0; DWORD nalloc = 1024; do { delete [] m_pids; m_pids = new DWORD [nalloc]; if (EnumProcesses(m_pids, nalloc*sizeof(DWORD), &m_count)) { m_count /= sizeof(DWORD); m_current = 1; // skip IDLE process } } while (nalloc <= m_count); return Next(); } //////////////////////////////////////////////////////////////// // CFindKillProcess - to find/kill a process by module name. // CFindKillProcess::CFindKillProcess() {} CFindKillProcess::~CFindKillProcess() {} ////////////////// // Search for process whose module name matches parameter. // Finds "foo" or "foo.exe" DWORD CFindKillProcess::FindProcess(LPCTSTR modname, BOOL bAddExe) { CProcessIterator itp; for (DWORD pid=itp.First(); pid; pid=itp.Next()) { TCHAR name[_MAX_PATH]; CProcessModuleIterator itm(pid); HMODULE hModule = itm.First(); // .EXE if (hModule) { GetModuleBaseName(itm.GetProcessHandle(),hModule, name, _MAX_PATH); string sModName = modname; if (strcmpi(sModName.c_str(),name)==0) return pid; sModName += ".exe"; if (bAddExe && strcmpi(sModName.c_str(),name)==0) return pid; } } return 0; } ////////////////// // Kill a process cleanly: Close main windows and wait. // bZap=TRUE to force kill. BOOL CFindKillProcess::KillProcess(DWORD pid, BOOL bZap) { CMainWindowIterator itw(pid); for (HWND hwnd=itw.First(); hwnd; hwnd=itw.Next()) { DWORD bOKToKill = FALSE; SendMessageTimeout(hwnd, WM_QUERYENDSESSION, 0, 0, SMTO_ABORTIFHUNG|SMTO_NOTIMEOUTIFNOTHUNG,100, &bOKToKill); if (!bOKToKill) return FALSE; // window doesn''t want to die: abort PostMessage(hwnd, WM_CLOSE, 0, 0); } // I''ve closed the main windows; now wait for process to die. BOOL bKilled = TRUE; HANDLE hp=OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE,FALSE,pid); if (hp) { if (WaitForSingleObject(hp, 5000) != WAIT_OBJECT_0) { if (bZap) { // didn''t die: force kill it if zap requested TerminateProcess(hp,0); } else { bKilled = FALSE; } } CloseHandle(hp); } return bKilled; } ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "EnumProc.h" #define tpf _tprintf // to save typing typedef list<string> CStringList; // like MFC, but with STL // pre-declare functions int help(); // check for switch: / or - inline BOOL isswitch(TCHAR c) { return c==L''/'' || c==L''-''; } int main(int argc, TCHAR* argv[], TCHAR* envp[]) { CStringList cmdargs; // command-line args (processes to kill) BOOL bDisplayOnly=FALSE; // don''t kill, just show results BOOL bQuiet=FALSE; // suppress error messages BOOL bZap=FALSE; // force-kill process // Parse command line. Switches can come in any order. for (int i=1; i<argc; i++) { if (isswitch(argv[i][0])) { for (UINT j=1; j<strlen(argv[i]); j++) { switch(tolower(argv[i][j])) { case ''?'': help(); return 0; case ''n'': bDisplayOnly=TRUE; break; case ''q'': bQuiet=TRUE; break; case ''z'': bZap=TRUE; break; default: return help(); } } } else { cmdargs.push_back(argv[i]); // got a non-switch arg: add to list } } if (cmdargs.size()<=0) help(); // Now iterate args (module names), killing each one CStringList::iterator it; for (it=cmdargs.begin(); it!=cmdargs.end(); it++) { CFindKillProcess fkp; DWORD pid = fkp.FindProcess(it->c_str()); if (pid) { if (bDisplayOnly) { tpf(_T("Kill process %d(0x%08x)\n"),pid,pid); } else { fkp.KillProcess(pid, bZap); } } else if (!bQuiet) { tpf(_T("Error: Can''t find process ''%s''.\n"),it->c_str()); } } return 0; } int help() { tpf(_T("kp: Kill process from command line.\n")); tpf(_T(" Copyright 2002 Paul DiLascia.\n\n")); tpf(_T(" kp [/nqz?] modname1 [modname2....]\n")); tpf(_T(" where modnameN is a module name; eg foo or foo.exe\n")); tpf(_T("\n")); tpf(_T(" /n(othing) don''t kill, just show results\n")); tpf(_T(" /q(uiet) don''t show errors\n")); tpf(_T(" /z(ap) force kill (ignore WM_QUERYENDSESSION)\n")); tpf(_T("\n")); return 0; }
四、小結
本實例通過介紹CfindKillProcess類探討了在Windows2000下徹底消除進程的方法,雖然該程序只能在Windows2000環境下編譯運行,但是該方法對Windows98下進程的控制也是有借鑒意義的。