如何在使用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
相同的问题。
根据@ eryksun的评论,我会提出一种可能的解决方案。
只是做
import ctypes
ctypes.windll.kernel32.AllocConsole()
的父进程。
不幸的是(如eryksun还指出),这也造成不必要的和混乱的控制台窗口。
如果你这样做,你可以使用'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()
基本上我从一个虚拟子偷控制台。
如果您打算使用cmd shell,那么比超时更可靠。将shell回显到标准输出并从标准输入读取。父母从孩子的stdout读取,附加到控制台,并关闭标准输入。例如: – eryksun
'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
如果有人想知道在这种情况下控制台是如何隐藏的,'shell = True '参数通过'%ComSpec%/ c'(通常是cmd.exe)运行该命令,并且还*设置'STARTUPINFO'来隐藏窗口。 – eryksun
除非目标'pid'实际上是一个过程* *组ID,作为进行了说明,然后'GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,PID)的行为'是未定义的。实际上,它的作用就像'pid'是0,即它将事件广播到连接到控制台的每个进程。你不想那样。它可能会杀死连接到控制台的父进程。 – eryksun
至于你的启动程序,它根本没有连接到控制台,所以我不明白你为什么会认为'GenerateConsoleCtrlEvent'会起作用。 – eryksun
最好的办法是使用真实的IPC,例如一个命名的事件对象。 (当然,我不知道如何在Python中做到这一点)。 –