Python 为什么对kernel32.TerminateProcess的第一个参数使用进程句柄对象的整数强制转换?
好的,我正在做一些关于使用python绑定windows系统调用来终止windows进程的测试,我使用了两种方法,但是它们做的不同,一些细节让我困惑 第一种方法是使用win32api 第二种方法是使用ctypes 在第二种方法中,除非像下面这样对进程进行整数强制转换,否则它将不起作用:Python 为什么对kernel32.TerminateProcess的第一个参数使用进程句柄对象的整数强制转换?,python,windows,python-2.7,ctypes,windows-kernel,Python,Windows,Python 2.7,Ctypes,Windows Kernel,好的,我正在做一些关于使用python绑定windows系统调用来终止windows进程的测试,我使用了两种方法,但是它们做的不同,一些细节让我困惑 第一种方法是使用win32api 第二种方法是使用ctypes 在第二种方法中,除非像下面这样对进程进行整数强制转换,否则它将不起作用: proc = subprocess.Popen([ 'notepad' ]) int_handle = int(proc._handle) ctypes.windll.kernel32.TerminateProc
proc = subprocess.Popen([ 'notepad' ])
int_handle = int(proc._handle)
ctypes.windll.kernel32.TerminateProcess(int_handle, -1 )
这个int_句柄,大概是某种进程句柄号,在我的机器上通常值在1-1000之间。
通过使用这些内置项检查proc.\u handle对象,我没有得到任何非常有用的信息:
print "dir() of handle >>",dir(proc._handle) # ['Close', 'Detach']
print "type() of handle >>",type(proc._handle) # <type '_subprocess_handle'>
我的问题:
Process
对象之类的内核对象。用户模式程序不能直接引用内核内存。相反,每个进程都有一个内核对象句柄表,该表将句柄值(如4、8、12等)映射到内核对象。例如,调用时,调用返回进程
及其主线程
的句柄。您还可以通过ID调用或打开现有进程或线程的句柄。调用关闭不再需要的句柄
(内核File
对象,如调用CreateFile
,也通过句柄引用。但是,CPython使用C运行时的低I/O文件描述符,如0、1、2等,即Unix/POSIX文件描述符。在Unix上,所有内容都是文件,而在Windows上,所有内容都是对象。C运行时维护一个将文件描述符映射到Windows文件
句柄。其他对象类型不映射到Unix文件描述符。使用低I/O文件描述符和C标准I/O使同时支持Unix和Windows变得更简单。也就是说,至少有一个建议的补丁,其雄心勃勃的目标是在Windows上重写Python 3的I/O支持Windows直接使用Windows API和文件句柄。)
在Python 2中,子进程在Windows上使用\u subprocess\u handle
类型在不再引用句柄时自动关闭句柄。该类型定义了一个\u int\u
方法来支持将句柄值转换为整数,但它没有子类int
,因此它本身不是整数。针对您的特定问题对于ctypes,默认参数转换仅将整数参数转换为Cint
值,这就是为什么必须使用int(proc.\u handle)
但是,使用默认的整数转换是错误的。您应该定义TerminateProcess.argtypes=(ctypes.c\u void\u p,ctypes.c\u uint)
。虽然实际内核句柄始终在32位范围内,即使在64位系统上,也可以使用伪句柄,例如(HANDLE)-1
(用于引用当前进程而不必打开实句柄)确实使用完整的指针范围。因此,在64位系统上,实内核句柄的上限DWORD
应为零,而对于伪句柄,如(句柄)-1
,必须保留非零的上层DWORD
。在2.x中,ctypes不会将参数的堆栈内存归零,并且使用默认的Cint
类型只写入堆栈QWORD
的下层DWORD
。为确保可靠性,必须手动将参数包装为指针或定义所有句柄类型参数(包括HMODULE
,HWND
等)作为指针类型,例如ctypes.c\u void\u p
或wintypes.HANDLE
使用ctypes.windell
也有问题,因为它会缓存DLL,从而缓存函数指针。这会使脚本或模块受全局状态支配,并可能导致冲突的argtypes
、restype
和errcheck
定义。它还不允许启用每线程保护的存储对于最后一个错误值。下面是一个更可靠的ctypes定义示例:
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
kernel32.TerminateProcess.argtypes = (wintypes.HANDLE, wintypes.UINT)
if __name__ == '__main__':
import subprocess
p = subprocess.Popen('notepad')
if not kernel32.TerminateProcess(int(p._handle), 1):
raise ctypes.WinError(ctypes.get_last_error())
有关系统中各种类型的对象以及如何使用句柄引用它们的概述,请参阅上的MSDN主题。内核地址空间中存在诸如进程
对象之类的内核对象。用户模式程序不能直接引用内核内存。相反,每个进程都有一个映射句柄值的内核对象句柄表内核对象的UE,如4、8、12等。例如,当您调用时,调用将返回进程及其主线程的句柄。您还可以通过ID调用或打开现有进程或线程的句柄。调用以关闭不再需要的句柄
(内核File
对象,如调用CreateFile
,也通过句柄引用。但是,CPython使用C运行时的低I/O文件描述符,如0、1、2等,即Unix/POSIX文件描述符。在Unix上,所有内容都是文件,而在Windows上,所有内容都是对象。C运行时维护一个将文件描述符映射到Windows文件
句柄。其他对象类型不映射到Unix文件描述符。使用低I/O文件描述符和C标准I/O使同时支持Unix和Windows变得更简单。也就是说,至少有一个建议的补丁,其雄心勃勃的目标是在Windows上重写Python 3的I/O支持Windows直接使用Windows API和文件句柄。)
在Python2中,子进程在Windows上使用\u subprocess\u handle
类型,在不再引用句柄时自动关闭句柄。Thi
print "dir() of handle >>",dir(proc._handle) # ['Close', 'Detach']
print "type() of handle >>",type(proc._handle) # <type '_subprocess_handle'>
notepad.exe pid: 8688 HOST\user
4: Event
8: WaitCompletionPacket
C: IoCompletion
10: TpWorkerFactory
14: IRTimer
...
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
kernel32.TerminateProcess.argtypes = (wintypes.HANDLE, wintypes.UINT)
if __name__ == '__main__':
import subprocess
p = subprocess.Popen('notepad')
if not kernel32.TerminateProcess(int(p._handle), 1):
raise ctypes.WinError(ctypes.get_last_error())