在Visual C++應(yīng)用程序中徹底清除進(jìn)程

2010-08-28 10:48:10來源:西部e網(wǎng)作者:

    讀者朋友們可能經(jīng)常會碰到這樣一個問題,想對某些進(jìn)行操作時,發(fā)現(xiàn)這些文件正在被其它程序使用,處于打開狀態(tài),而且是被獨占打開,這時是沒法對文件進(jìn)行操作的。因此,要想操作這些文件,必須將打開這些文件的進(jìn)程清除掉。那么如何干凈地清除進(jìn)程呢?其實,在Windows2000操作系統(tǒng)版本中有一個工具程序叫tskill.exe,用它就可以清除掉某個程序的進(jìn)程,在輸入"tskill 程序名"后就可以清除其運行實例。但是如何要在代碼里實現(xiàn)tskill的功能該如何做呢?針對這一問題,本實例介紹了在Windows2000下實現(xiàn)的方法。

  一、實現(xiàn)方法

  在Visual C++編程中,最安全的殺死進(jìn)程的方法是向運行程序的主窗口發(fā)送WM_CLOSE消息,其實現(xiàn)代碼如下:

HWND hwnd =this.m_hWnd; // 獲得主窗口
PostMessage(hwnd, WM_CLOSE, 0, 0);


  發(fā)送此消息后,通常應(yīng)該等待直到進(jìn)程確實終止,當(dāng)進(jìn)程終止時,它發(fā)出狀態(tài)信號,并且 WaitForSingleObject 返回WAIT_OBJECT_0。如果返回別的值,進(jìn)程要么掛起了,要么仍然在進(jìn)行處理。在這種情況下,殺死這個進(jìn)程的唯一方法是用功能更強大的API函數(shù):TerminateProcess()。如果想干得漂亮一點,可以在關(guān)閉之前向主窗口發(fā)送一個WM_QUERYENDSESSION消息,當(dāng)用戶結(jié)束會話(log out)或者調(diào)用ExitWindows()函數(shù)時,應(yīng)用程序會收到這個消息,然后準(zhǔn)備退出進(jìn)程,此時一般都會彈出一個確認(rèn)對話框,告訴用戶:"程序要推出了,如果要保存修改的東西,現(xiàn)在是最佳時機(jī),想保存嗎?"有三種選擇(Yes/No/Cancel)。此外,發(fā)送WM_QUERYENDSESSION消息可以拒絕推出進(jìn)程(按下"Cancel鍵"),如果是這樣,進(jìn)程將會延續(xù)。

  如果想要關(guān)閉的進(jìn)程被掛起,使用SendMessageTimeout()函數(shù)就非常重要,而不是用SendMessage()函數(shù),其參數(shù)SMTO_NOTIMEOUTIFNOTHUNG是一個只有Windows 2000 和Windows XP才有的標(biāo)志。其意義是"如果線程沒有掛起,不要超時",換句話說就是如果線程正在進(jìn)行正常處理,那么永遠(yuǎn)等待,以便用戶能看到對話框并決定做什么,當(dāng)用戶最終做出決定后,SendMessageTimeout()將帶著相應(yīng)的bOKToKill值返回。

  本例為了增強代碼的可重用性,將實現(xiàn)細(xì)節(jié)都封裝在一個叫CFindKillProcess的類中,包括查找和殺死進(jìn)程,詳情請參見EnumProc.h和EnumProc.cpp文件。文件中還有另外兩個可重用類,一個是CProcessIterator,另一個是CWindowIterator。

  CfindKillProcess類的成員函數(shù)FindProcess()查找某個進(jìn)程序,如果找到這個進(jìn)程,它返回此進(jìn)程的ID,然后將此ID傳給CFindKillProcess::KillProcess()函數(shù),KillProcess()函數(shù)封裝了關(guān)閉窗口以及終止邏輯,它利用CmainWindowIterator類對象來枚舉進(jìn)程的主窗口(可能不止一個,見"如何獲取某個進(jìn)程的主窗口以及創(chuàng)建進(jìn)程的程序名?"),并發(fā)送WM_CLOSE到每一個窗口,然后等待進(jìn)程死亡。它有一個布爾型參數(shù)用來指示當(dāng)應(yīng)用程序進(jìn)程不愿意退出時是否執(zhí)行TerminateProcess()函數(shù)。詳細(xì)細(xì)節(jié)請參見下載的代碼。

  二、編程步驟

  1、 啟動Visual C++6.0,生成一個控制臺應(yīng)用程序,將該程序命名為"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;
 }

  四、小結(jié)

  本實例通過介紹CfindKillProcess類探討了在Windows2000下徹底消除進(jìn)程的方法,雖然該程序只能在Windows2000環(huán)境下編譯運行,但是該方法對Windows98下進(jìn)程的控制也是有借鑒意義的.

贊助商鏈接: