プロセスを一時停止/再開するプログラム

プロセスを一時停止/再開するプログラムをPythonで書いた。標準ライブラリのみで行いたかったのでpywin32などサードパーティー製のライブラリは使用していない。
プロセスを一時停止/再開させる(ドキュメント化された)Windows APIは残念ながら存在しないので、SuspendThreadとResumeThreadにより一時停止/再開させたいプロセスが所有する全てのスレッドを一時停止/再開させるという手法を採っている。また、SuspendThreadとResumeThreadのサスペンドカウントを増減させ、0になったら再開するという仕様により、例えばこのプログラムを使用して3回プロセスを一時停止させたならば、再開するためには3回プロセスを再開させねばならない。

使い方

psusres.py

で、何もオプションを指定しなければプロセスIDとプロセスの名前の一覧を表示し、

psusres -s PID

で、PIDで指定されたプロセスの一時停止、

psusres -r PID

で指定されたプロセスの再開をする。

ソースコード

ソースコードを以下に示す。ここで使用しているWindows APIのラッパーは少し長いのでgithubに置いた。

#!/usr/bin/env python
# coding: utf-8
# psusres: a tool to suspend/resume a process
from __future__ import print_function
from optparse import OptionParser
from winapi import *

def PauseResumeThreadList(dwOwnerPID, bSuspendThread):
    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwOwnerPID)
    if hThreadSnap == INVALID_HANDLE_VALUE:
        return
    
    te32 = Thread32First(hThreadSnap)
    if te32:
        while True:
            if te32.th32OwnerProcessID == dwOwnerPID:
                hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID)
                if bSuspendThread:
                    print("Suspending Thread 0x{0:04X}".format(te32.th32ThreadID))
                    SuspendThread(hThread)
                else:
                    print("Resuming Thread 0x{0:04X}".format(te32.th32ThreadID))
                    ResumeThread(hThread)
                CloseHandle(hThread)
            te32 = Thread32Next(hThreadSnap)
            if not te32:
                break
    
    CloseHandle(hThreadSnap)

def ProcessList():
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
    pe32 = Process32First(hProcessSnap)
    if (pe32):
        while True:
            print('PID\t', pe32.th32ProcessID, '\t', pe32.szExeFile, sep='')
            pe32 = Process32Next(hProcessSnap)
            if not pe32:
                break
    CloseHandle(hProcessSnap)

def main():
    usage = 'usage: %prog [options] PID'
    parser = OptionParser(usage)
    parser.add_option('-s', '--suspend', action='store_true', dest='suspend',
        help='suspend a specified process')
    parser.add_option('-r', '--resume', action='store_false', dest='suspend',
        help='resume a specified process')
    options, args = parser.parse_args()
    
    if options.suspend is None:
        parser.print_help()
        print()
        ProcessList()
    elif len(args) == 0:
        parser.print_help()
    else:
        try:
            pid = int(args[0])
        except:
            print("Invalid PID number:", args[0])
        else:
            PauseResumeThreadList(pid, options.suspend)
    
    return

if __name__ == '__main__':
    main()