如果我们不想让子进程处理用户输入,而是只是在这种情况下将其杀死,解决方案可以如下:
- 使用重定向的stdin启动子进程到管道。
- 我们在异步模式下创建管道服务器端,并将主管道缓冲区设置为0大小。
- 在启动子进程之前,向此管道写入1个字节。
- 由于管道缓冲区大小为0,操作不会完成,直到另一侧未读取此字节。
- 在我们写入此1个字节并操作正在进行(挂起)时 - 启动子进程。
- 最后开始等待先完成的操作:写入操作还是子进程?
- 如果先完成的是写入操作,则意味着子进程开始从stdin读取 - 因此在此时杀死它。
在C++上的一种可能的实现:
struct ReadWriteContext : public OVERLAPPED
{
enum OpType : char { e_write, e_read } _op;
BOOLEAN _bCompleted;
ReadWriteContext(OpType op) : _op(op), _bCompleted(false)
{
}
};
VOID WINAPI OnReadWrite(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* lpOverlapped)
{
static_cast<ReadWriteContext*>(lpOverlapped)->_bCompleted = TRUE;
DbgPrint("%u:%x %p\n", static_cast<ReadWriteContext*>(lpOverlapped)->_op, dwErrorCode, dwNumberOfBytesTransfered);
}
void nul(PCWSTR lpApplicationName)
{
ReadWriteContext wc(ReadWriteContext::e_write), rc(ReadWriteContext::e_read);
static const WCHAR pipename[] = L"\\\\?\\pipe\\{221B9EC9-85E6-4b64-9B70-249026EFAEAF}";
if (HANDLE hPipe = CreateNamedPipeW(pipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1, 0, 0, 0, 0))
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si)};
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = CreateFileW(pipename, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE != si.hStdInput)
{
char buf[256];
if (WriteFileEx(hPipe, "\n", 1, &wc, OnReadWrite))
{
si.hStdError = si.hStdOutput = si.hStdInput;
if (CreateProcessW(lpApplicationName, 0, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
BOOLEAN bQuit = true;
goto __read;
do
{
bQuit = true;
switch (WaitForSingleObjectEx(pi.hProcess, INFINITE, TRUE))
{
case WAIT_OBJECT_0:
DbgPrint("child terminated\n");
break;
case WAIT_IO_COMPLETION:
if (wc._bCompleted)
{
DbgPrint("child read from hStdInput!\n");
TerminateProcess(pi.hProcess, 0);
}
else if (rc._bCompleted)
{
__read:
rc._bCompleted = false;
if (ReadFileEx(hPipe, buf, sizeof(buf), &rc, OnReadWrite))
{
bQuit = false;
}
}
break;
default:
__debugbreak();
}
} while (!bQuit);
CloseHandle(pi.hProcess);
}
}
CloseHandle(si.hStdInput);
SleepEx(0, TRUE);
}
CloseHandle(hPipe);
}
}
另一种代码变体 - 使用事件完成(event completion),而不是apc。然而这不会影响最终结果。这个代码变体与第一个代码完全相同:
void nul(PCWSTR lpApplicationName)
{
OVERLAPPED ovw = {}, ovr = {};
if (ovr.hEvent = CreateEvent(0, 0, 0, 0))
{
if (ovw.hEvent = CreateEvent(0, 0, 0, 0))
{
static const WCHAR pipename[] = L"\\\\?\\pipe\\{221B9EC9-85E6-4b64-9B70-249026EFAEAF}";
if (HANDLE hPipe = CreateNamedPipeW(pipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1, 0, 0, 0, 0))
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si)};
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = CreateFileW(pipename, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE != si.hStdInput)
{
char buf[256];
if (!WriteFile(hPipe, "\n", 1, 0, &ovw) && GetLastError() == ERROR_IO_PENDING)
{
si.hStdError = si.hStdOutput = si.hStdInput;
if (CreateProcessW(lpApplicationName, 0, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
BOOLEAN bQuit = true;
HANDLE h[] = { ovr.hEvent, ovw.hEvent, pi.hProcess };
goto __read;
do
{
bQuit = true;
switch (WaitForMultipleObjects(3, h, false, INFINITE))
{
case WAIT_OBJECT_0 + 0:
__read:
if (ReadFile(hPipe, buf, sizeof(buf), 0, &ovr) || GetLastError() == ERROR_IO_PENDING)
{
bQuit = false;
}
break;
case WAIT_OBJECT_0 + 1:
DbgPrint("child read from hStdInput!\n");
TerminateProcess(pi.hProcess, 0);
break;
case WAIT_OBJECT_0 + 2:
DbgPrint("child terminated\n");
break;
default:
__debugbreak();
}
} while (!bQuit);
CloseHandle(pi.hProcess);
}
}
CloseHandle(si.hStdInput);
}
CloseHandle(hPipe);
}
CloseHandle(ovw.hEvent);
}
CloseHandle(ovr.hEvent);
}
}
['python', '-u', 'ask.py']
),但这样您也可能编辑它。 - JohanL