如何在使用Python C API时在Windows上中断Python子进程?

问题描述:

我能打断我的子进程在Windows中如何在使用Python C API时在Windows上中断Python子进程?

import ctypes 
ctypes.windll.kernel32.GenerateConsoleCtrlEvent(1, _proc.pid) 

,但只有当我通过正常的Python程序运行它。

当运行通过使用Python C API单独启动程序相同的代码(代码是下面),上面的代码不具有任何影响。

我应该以某种方式改变我的启动,以便能够中断子进程?

#include <Python.h> 
#include <windows.h> 

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow) 
{ 
    LPWSTR *argv; 
    int argc; 

    argv = CommandLineToArgvW(GetCommandLine(), &argc); 
    if (argv == NULL) 
    { 
     MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK); 
     return 10; 
    } 

    Py_SetProgramName(argv[0]); 
    Py_Initialize(); 
    PySys_SetArgvEx(argc, argv, 0); 

    PyObject *py_main, *py_dict; 
    py_main = PyImport_AddModule("__main__"); 
    py_dict = PyModule_GetDict(py_main); 

    PyObject* result = PyRun_String(
     "from runpy import run_module\n" 
     "run_module('thonny')\n", 
     Py_file_input, 
     py_dict, 
     py_dict 
     ); 

    int code; 
    if (!result) { 
     PyObject *ptype, *pvalue, *ptraceback; 
     PyErr_Fetch(&ptype, &pvalue, &ptraceback); 

     PyObject* valueAsString = PyObject_Str(pvalue); 

     wchar_t* error_msg = PyUnicode_AsWideCharString(valueAsString, NULL); 
     MessageBox(0, error_msg, L"Thonny startup error", MB_OK | MB_ICONERROR); 
     code = -1; 
    } 
    else { 
     code = 1; 
    } 

    Py_Finalize(); 

    return code; 
} 

编辑:原来与pythonw.exe相同的问题。

+0

除非目标'pid'实际上是一个过程* *组ID,作为进行了说明,然后'GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,PID)的行为'是未定义的。实际上,它的作用就像'pid'是0,即它将事件广播到连接到控制台的每个进程。你不想那样。它可能会杀死连接到控制台的父进程。 – eryksun

+0

至于你的启动程序,它根本没有连接到控制台,所以我不明白你为什么会认为'GenerateConsoleCtrlEvent'会起作用。 – eryksun

+0

最好的办法是使用真实的IPC,例如一个命名的事件对象。 (当然,我不知道如何在Python中做到这一点)。 –

根据@ eryksun的评论,我会提出一种可能的解决方案。

只是做

import ctypes 
ctypes.windll.kernel32.AllocConsole() 

的父进程。

不幸的是(如eryksun还指出),这也造成不必要的和混乱的控制台窗口。

+1

如果你这样做,你可以使用'user32.ShowWindow(kernel32.GetConsoleWindow(),SW_HIDE)'来隐藏控制台。顺便说一句,请定义原型,从您自己的库实例开始,即'kernel32 = ctypes.WinDLL('kernel32',use_last_error = True)'并使用'ctypes.get_last_error()'检查捕获的错误值。 – eryksun

那怎么我终于得到了无闪烁控制台窗口(感谢@eryksun为指针)分配控制台:

import sys 
import ctypes 
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) 

cmd = [sys.executable, "-c", "print('Hi!'); input()"] 
child = subprocess.Popen(cmd, 
         stdin=subprocess.PIPE, 
         stdout=subprocess.PIPE, 
         stderr=subprocess.PIPE, 
         shell=True) 

child.stdout.readline() # now I know subprocess is ready 
result = kernel32.AttachConsole(child.pid) 
if not result: 
    err = ctypes.get_last_error() 
    print("Could not allocate console. Error code:", err, file=sys.stderr) 
child.stdin.write(b"\n") # allow subprocess to complete 
child.stdin.flush() 

基本上我从一个虚拟子偷控制台。

+1

如果您打算使用cmd shell,那么比超时更可靠。将shell回显到标准输出并从标准输入读取。父母从孩子的stdout读取,附加到控制台,并关闭标准输入。例如: – eryksun

+1

'child = subprocess.Popen('echo&set/p“x =”',stdin = subprocess.PIPE,stdout = subprocess.PIPE,shell = True);''child.stdout.read(1) ;''result = kernel32.AttachConsole(child。如果child.poll()是None:child.kill()' – eryksun

+1

如果有人想知道在这种情况下控制台是如何隐藏的,'shell = True '参数通过'%ComSpec%/ c'(通常是cmd.exe)运行该命令,并且还*设置'STARTUPINFO'来隐藏窗口。 – eryksun