科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网安全频道干干净净杀死进程

干干净净杀死进程

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

要想备份这些文件,必须将打开它们的那些进程kill掉。那么如何干净地杀死这些打开文件的进程呢?相信看完本文后,自然会有办法解决!

作者:巧巧读书 来源:巧巧读书 2008年9月2日

关键字: 进程 系统进程 进程管理

  • 评论
  • 分享微博
  • 分享邮件

  最近在写程序时碰到这样一个问题:我想将文件备份到网络驱动器上,但是有一些文件正在被其它程序使用,处于打开状态,而且是被独占打开,这时是没法对文件进行备份操作的。因此,要想备份这些文件,必须将打开它们的那些进程kill掉。那么如何干净地杀死这些打开文件的进程呢?相信看完本文后,自然会有办法解决!

  其实,在较新的Windows操作系统版本中有一个工具程序叫tskill.exe,用它就可以解决问题。如图一所示:

图一 tskill程序

  要杀掉某个程序的进程,可以输入下面的命令便可以杀死其运行实例: tskill 程序名   但是我想在自己写的代码里实现tskill的功能该如何做呢?最安全的杀死进程的方法是向运行程序的主窗口发送WM_CLOSE消息。 HWND hwnd = // 获得主窗口

  PostMessage(hwnd, WM_CLOSE, 0, 0);   发送此消息后,通常应该等待直到进程确实终止: HANDLE hp = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE,FALSE,pid);

  WaitForSingleObject(hp, 5000); // 等待5秒   当进程终止时,它发出状态信号,并且 WaitForSingleObject 返回WAIT_OBJECT_0。如果返回别的值,进程要么挂起了,要么仍然在进行处理。在这种情况下,杀死这个进程的唯一方法是用功能更强大的TerminateProcess: if (WaitForSingleObject(hp, 5000) != WAIT_OBJECT_0)

  TerminateProcess(hp,0);    如果想干得漂亮一点,可以在关闭之前向主窗口发送一个WM_QUERYENDSESSION消息。当用户结束会话(log out)或者有人调用ExitWindows时,应用程序会收到这个消息。然后准备即将来临的死亡。此时一般都会弹出一个确认对话框,告诉世人:“我要完蛋了,如果要保存修改的东西,现在是最佳时机,想保存吗?”有三种选择(Yes/No/Cancel)。此外,WM_QUERYENDSESSION甚至可以拒绝死亡(按下"Cancel键"),如果是这样,生命将会延续。代码应该这样写: DWORD bOKToKill = FALSE;

  SendMessageTimeout(hwnd, WM_QUERYENDSESSION, 0, 0,

  SMTO_ABORTIFHUNG|SMTO_NOTIMEOUTIFNOTHUNG, 100, &bOKToKill);

  if (bOKToKill) {

  // 发送WM_CLOSE 并等待

  }   如果想要关闭的进程被挂起。使用SendMessageTimeout就非常重要,而不是用SendMessage。SMTO_NOTIMEOUTIFNOTHUNG是一个只有Windows 2000 和Windows XP才有的标志。其意义是“如果线程没有挂起,不要超时。”换句话说就是:如果线程正在进行正常处理,那么永远等待,以便用户能看到对话框并决定做什么。当用户最终做出决定后,SendMessageTimeout将带着相应的bOKToKill值返回。所有这些的前提是其它的应用程序运行正常并且WM_QUERYENDSESSION也得到正常处理。 本文提供了一个小例子程序kp.exe,下面是关键代码: /////////////////////////////////////////////

  kp.exe程序代码

  ////////////////////////////////////////////

  #include "stdafx.h"

  #include "EnumProc.h"

  typedef list<string> CStringList;     // 使用STL,与MFC的类似

  inline BOOL isswitch(TCHAR c) { return c==L''''''''/'''''''' || c==L''''''''-''''''''; }

  int main(int argc, TCHAR* argv[], TCHAR* envp[])

  {

  CStringList cmdargs;       // 命令行参数 (要杀死的进程,可以多个)

  BOOL bDisplayOnly=FALSE;   // 不杀,只是显示

  BOOL bQuiet=FALSE;      // 禁止出错信息

  BOOL bZap=FALSE;      // 强行杀死

  // 解析命令行开关,开关顺序可以任意

  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]);   // 如果是非开关参数,添加到列表

  }

  }

  if (cmdargs.size()<=0)

  help();

  // 遍历参数(模块名),一个一个干掉

  CStringList::iterator it;

  for (it=cmdargs.begin(); it!=cmdargs.end(); it++) {

  CFindKillProcess fkp;

  DWORD pid = fkp.FindProcess(it->c_str());

  if (pid) {

  if (bDisplayOnly) {

  _tprintf(_T("Kill process %d(0x%08x)\n"),pid,pid);

  } else {

  fkp.KillProcess(pid, bZap);

  }

  } else if (!bQuiet) {

  _tprintf(_T("错误: 找不到进程 ''''''''%s''''''''.\n"),it->c_str());

  }

  }

  return 0;

  }   这个程序的功能与tskill类似,用它也可以杀死进程。为了增强代码的可重用性,将实现细节都封装在一个叫CFindKillProcess的类中,包括查找和杀死进程,详情请参见EnumProc.h和EnumProc.cpp文件。文件中还有另外两个可重用类,一个是CProcessIterator,另一个是CWindowIterator。这在以前的文章中有过详细描述。见“如何获取某个进程的主窗口以及创建进程的程序名?” ///////////////////////////////////////////////////////////////

  EnumProc.h和EnumProc.cpp

  //////////////////////////////////////////////////////////////

  EnumProc.h

  //////////////////////////////////////////////////////////////////////////////////////////////

  // 下面是CFindKillProcess的定义和实现,这是一个小巧工具类,专门用来通过某个进程的名字查找并杀死它。

  //

  class CFindKillProcess {

  public:

  CFindKillProcess();

  ~CFindKillProcess();

  DWORD FindProcess(LPCTSTR lpModname, BOOL bAddExe=TRUE);

  BOOL KillProcess(DWORD pid, BOOL bZap);

  };

  EnumProc.cpp

  #include "stdafx.h"

  #include "EnumProc.h"

  ……

  //实现CProcessIterator

  …….

  //实现CWindowIterator

  ////////////////////////////////////////////////////////////////

  // 实现CFindKillProcess - 用模块名查找并杀死进程。

  //

  CFindKillProcess::CFindKillProcess()

  {

  }

  CFindKillProcess::~CFindKillProcess()

  {

  }

  //////////////////

  // 搜索与参数匹配的模块名,模块名可以是foo或者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;

  }

  //////////////////

  // 干净地杀死进程:关闭窗口后等待。

  // bZap=TRUE :强行杀死

  //

  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; // 不愿意死亡,取消行动

  PostMessage(hwnd, WM_CLOSE, 0, 0);

  }

  // 我已经关闭了全部主窗口,现在等待进程死亡。

  BOOL bKilled = TRUE;

  HANDLE hp=OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE,FALSE,pid);

  if (hp) {

  if (WaitForSingleObject(hp, 5000) != WAIT_OBJECT_0) {

  if (bZap) { // 不愿死,那不行,你必须死

  TerminateProcess(hp,0);

  } else {

  bKilled = FALSE;

  }

  }

  CloseHandle(hp);

  }

  return bKilled;

  }

  CFindKillProcess的使用方法如下:

  CFindKillProcess fkp;

  DWORD pid = fkp.FindProcess("outlook.exe");

  fkp.KillProcess(pid);FindKillProcess用 CProcessIterator 和 CProcessModuleIterator类来枚举所有进程,查找第一个与名字匹配的模块(即启动进程的exe文件): CProcessIterator itp;

  for (DWORD pid=itp.First(); pid; pid=itp.Next()) {

  CProcessModuleIterator itm(pid);

  HMODULE hModule = itm.First(); // .EXE

  if (/* 模块名 = 正在查找的模块名*/) {

  return pid;

  }

  }   FindProcess查找foo或者foo.exe。如果找到这个进程,它返回此进程的ID,然后你将它传给CFindKillProcess::KillProcess。KillProcess封装了关闭窗口以及终止逻辑。它利用CMainWindowIterator来枚举进程的主窗口(可能不止一个,见“如何获取某个进程的主窗口以及创建进程的程序名?”),并发送WM_CLOSE到每一个窗口,然后等待进程死亡。它有一个布尔型参数用来指示当应用程序不愿意死亡时是否执行TerminateProcess。详细细节请参见下载的代码。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章