【问题标题】:Suspend/resume processes as PsSuspend does像 PsSuspend 一样暂停/恢复进程
【发布时间】:2012-04-26 13:00:03
【问题描述】:

我希望这篇文章不是重复的。让我解释一下:

我考虑过类似的帖子How to pause / resume any external process under Windows?,但偏好 C++/Python,但截至发帖时还没有公认的答案。


我的问题:

我对由 Windows SysinternalsMark Russinovich 提供的 PsSuspend 提供的功能在 Delphi 中的可能实现感兴趣。

行情:

PsSuspend 允许您暂停本地或远程系统上的进程, 这在进程正在消耗资源的情况下是可取的 (例如网络、CPU 或磁盘)您希望允许不同的进程 使用。而不是杀死消耗资源的进程, 暂停允许您让它在以后继续运行 时间点。

谢谢。


编辑:

部分实现即可。可以删除远程功能。

【问题讨论】:

  • 您想模拟整个功能吗?也有远程进程?
  • 现在这是一个非常(x3)的好问题
  • @David Heffernan:不,让我们放弃远程功能。

标签: delphi winapi windows-7 process 32-bit


【解决方案1】:

您可以尝试使用以下代码。它使用未记录的函数NtSuspendProcessNtResumeProcess。我已经从 Delphi 2009 中内置的 32 位应用程序在 Windows 7 64 位上进行了尝试,它适用于我。请注意,这些函数未记录在案,因此可以从未来版本的 Windows 中删除。

更新

以下代码中的 SuspendProcessResumeProcess 包装器现在是函数,如果成功则返回 True,否则返回 False。

type
  NTSTATUS = LongInt;
  TProcFunction = function(ProcHandle: THandle): NTSTATUS; stdcall;

const
  STATUS_SUCCESS = $00000000;
  PROCESS_SUSPEND_RESUME = $0800;

function SuspendProcess(const PID: DWORD): Boolean;
var
  LibHandle: THandle;
  ProcHandle: THandle;
  NtSuspendProcess: TProcFunction;
begin
  Result := False;
  LibHandle := SafeLoadLibrary('ntdll.dll');
  if LibHandle <> 0 then
  try
    @NtSuspendProcess := GetProcAddress(LibHandle, 'NtSuspendProcess');
    if @NtSuspendProcess <> nil then
    begin
      ProcHandle := OpenProcess(PROCESS_SUSPEND_RESUME, False, PID);
      if ProcHandle <> 0 then
      try
        Result := NtSuspendProcess(ProcHandle) = STATUS_SUCCESS;
      finally
        CloseHandle(ProcHandle);
      end;
    end;
  finally
    FreeLibrary(LibHandle);
  end;
end;

function ResumeProcess(const PID: DWORD): Boolean;
var
  LibHandle: THandle;
  ProcHandle: THandle;
  NtResumeProcess: TProcFunction;
begin
  Result := False;
  LibHandle := SafeLoadLibrary('ntdll.dll');
  if LibHandle <> 0 then
  try
    @NtResumeProcess := GetProcAddress(LibHandle, 'NtResumeProcess');
    if @NtResumeProcess <> nil then
    begin
      ProcHandle := OpenProcess(PROCESS_SUSPEND_RESUME, False, PID);
      if ProcHandle <> 0 then
      try
        Result := NtResumeProcess(ProcHandle) = STATUS_SUCCESS;
      finally
        CloseHandle(ProcHandle);
      end;
    end;
  finally
    FreeLibrary(LibHandle);
  end;
end;

【讨论】:

  • 这就是无证函数的乐趣所在。不管怎样,OpenProcess 返回一个进程句柄而不是模块句柄,FWIW。
  • 这里有一些code sample(也展示了如何挂起每个线程)。从PsSuspend 程序里面戳,我相信它也使用了这些 API。还有一件事,以某种方式获得当前状态会很好,就像在 Process Explorer 中一样。
  • 我不信任使用 PROCESS_ALL_ACCESS 的代码。指定操作所需的最小访问权限集始终是一个好主意。可能你需要用SeDebugPrivilege设置调用进程,这个应该用标准用户权限测试一下。
  • @TLama:发现这个网站The Undocumented Functions。看起来很有趣。
【解决方案2】:

Windows 中没有SuspendProcess API 调用。所以你需要做的是:

  1. 枚举进程中的所有线程。示例代码见RRUZ's answer
  2. 为每个线程调用SuspendThread
  3. 为了实现程序的恢复部分,为每个线程调用ResumeThread

【讨论】:

  • 模拟这个功能似乎是合理的。我怀疑基于 PsSuspend 使用的未记录 API:pssuspend [- ] [-r] [\\computer [-u username] [-p password]] &lt;process name | process id&gt;。鉴于它是由 MS 本身的专家编写的,有没有办法确定它不使用这样的 api?
  • Mark 在写完 PsSuspend 后不久加入了 MS。尽管如此,它很可能会使用未记录的 API。在加入 MS 之前,他还编写了 Windows Internals,因此内部或外部几乎没有区别。我认为 profile 模式下的依赖 walker 会告诉你使用了哪些 API。
  • @Chibueze Opata:问题是由于某些原因(故意/疏忽)MS 没有披露它们,所以剩下的唯一选择是寻找其他公共/地下来源。
  • 我的意思是,这是一个共同的话题,所以即使它是秘密的,它也是一个公开的秘密......
  • 您需要注意的是,未记录的 API 可能会发生变化。这意味着如果您依赖它们,那么您的程序在未来版本的操作系统上运行时可能会停止工作
【解决方案3】:

“暂停所有线程”实现存在竞争条件 - 如果您尝试暂停的程序在您创建快照和完成暂停之间创建一个或多个线程,会发生什么情况?

您可以循环,获取另一个快照并挂起任何未挂起的线程,只有在没有找到时才退出。

未记录的函数避免了这个问题。

【讨论】:

    【解决方案4】:

    我刚刚发现了以下sn-ps here(作者:steve10120)。

    我认为它们是贵重物品,我不禁将它们发布为我自己问题的替代答案。


    恢复过程:

    function ResumeProcess(ProcessID: DWORD): Boolean;
     var
       Snapshot,cThr: DWORD;
       ThrHandle: THandle;
       Thread:TThreadEntry32;
     begin
       Result := False;
       cThr := GetCurrentThreadId;
       Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
       if Snapshot <> INVALID_HANDLE_VALUE then
        begin
         Thread.dwSize := SizeOf(TThreadEntry32);
         if Thread32First(Snapshot, Thread) then
          repeat
           if (Thread.th32ThreadID <> cThr) and (Thread.th32OwnerProcessID = ProcessID) then
            begin
             ThrHandle := OpenThread(THREAD_ALL_ACCESS, false, Thread.th32ThreadID);
             if ThrHandle = 0 then Exit;
             ResumeThread(ThrHandle);
             CloseHandle(ThrHandle);
            end;
          until not Thread32Next(Snapshot, Thread);
          Result := CloseHandle(Snapshot);
         end;
     end;
    

    暂停进程:

    function SuspendProcess(PID:DWORD):Boolean;
     var
     hSnap:  THandle;
     THR32:  THREADENTRY32;
     hOpen:  THandle;
     begin
       Result := FALSE;
       hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
       if hSnap <> INVALID_HANDLE_VALUE then
       begin
         THR32.dwSize := SizeOf(THR32);
         Thread32First(hSnap, THR32);
         repeat
           if THR32.th32OwnerProcessID = PID then
           begin
             hOpen := OpenThread($0002, FALSE, THR32.th32ThreadID);
             if hOpen <> INVALID_HANDLE_VALUE then
             begin
               Result := TRUE;
               SuspendThread(hOpen);
               CloseHandle(hOpen);
             end;
           end;
         until Thread32Next(hSnap, THR32) = FALSE;
         CloseHandle(hSnap);
       end;
     end;
    

    免责声明:

    我根本没有测试它们。请尽情享受,不要忘记反馈。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多