2017-04-17 76 views
1

我能打斷我的子進程在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中做到這一點)。 –

回答

0

根據@ 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

1

那怎麼我終於得到了無閃爍控制檯窗口(感謝@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