Python 为什么对kernel32.TerminateProcess的第一个参数使用进程句柄对象的整数强制转换?

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

好的,我正在做一些关于使用python绑定windows系统调用来终止windows进程的测试,我使用了两种方法,但是它们做的不同,一些细节让我困惑

第一种方法是使用win32api

第二种方法是使用ctypes

在第二种方法中,除非像下面这样对进程进行整数强制转换,否则它将不起作用:

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'>
我的问题:

  • 这个整数存储在int_handle中的具体内容是什么
  • 为什么它可以通过对proc.\u handle对象进行整数转换来获得句柄值
  • 为什么要将这个数字传递给kernel32.TerminateProcess
  • 有关系统中各种类型的对象以及如何使用句柄引用它们的概述,请参阅上的MSDN主题。内核地址空间中存在诸如
    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,默认参数转换仅将整数参数转换为C
    int
    值,这就是为什么必须使用
    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不会将参数的堆栈内存归零,并且使用默认的C
    int
    类型只写入堆栈
    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())