|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <windows.h> |
|
|
#include <stdio.h> |
|
|
#include <string.h> |
|
|
|
|
|
#include "TestUtil.cc" |
|
|
|
|
|
const char *g_prefix = ""; |
|
|
|
|
|
static void dumpHandles() { |
|
|
trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x", |
|
|
g_prefix, |
|
|
(long long)GetStdHandle(STD_INPUT_HANDLE), |
|
|
(long long)GetStdHandle(STD_OUTPUT_HANDLE), |
|
|
(long long)GetStdHandle(STD_ERROR_HANDLE)); |
|
|
} |
|
|
|
|
|
static const char *successOrFail(BOOL ret) { |
|
|
return ret ? "ok" : "FAILED"; |
|
|
} |
|
|
|
|
|
static void startChildInSameConsole(const wchar_t *args, BOOL |
|
|
bInheritHandles=FALSE) { |
|
|
wchar_t program[1024]; |
|
|
wchar_t cmdline[1024]; |
|
|
GetModuleFileNameW(NULL, program, 1024); |
|
|
swprintf(cmdline, L"\"%ls\" %ls", program, args); |
|
|
|
|
|
STARTUPINFOW sui; |
|
|
PROCESS_INFORMATION pi; |
|
|
memset(&sui, 0, sizeof(sui)); |
|
|
memset(&pi, 0, sizeof(pi)); |
|
|
sui.cb = sizeof(sui); |
|
|
|
|
|
CreateProcessW(program, cmdline, |
|
|
NULL, NULL, |
|
|
bInheritHandles, |
|
|
0, |
|
|
NULL, NULL, |
|
|
&sui, &pi); |
|
|
} |
|
|
|
|
|
static void closeHandle(HANDLE h) { |
|
|
trace("%sClosing handle 0x%I64x...", g_prefix, (long long)h); |
|
|
trace("%sClosing handle 0x%I64x... %s", g_prefix, (long long)h, successOrFail(CloseHandle(h))); |
|
|
} |
|
|
|
|
|
static HANDLE createBuffer() { |
|
|
|
|
|
|
|
|
SECURITY_ATTRIBUTES sa = {0}; |
|
|
sa.nLength = sizeof(sa); |
|
|
sa.bInheritHandle = TRUE; |
|
|
|
|
|
trace("%sCreating a new buffer...", g_prefix); |
|
|
HANDLE conout = CreateConsoleScreenBuffer( |
|
|
GENERIC_READ | GENERIC_WRITE, |
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
|
&sa, |
|
|
CONSOLE_TEXTMODE_BUFFER, NULL); |
|
|
|
|
|
trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout); |
|
|
return conout; |
|
|
} |
|
|
|
|
|
static HANDLE openConout() { |
|
|
|
|
|
|
|
|
SECURITY_ATTRIBUTES sa = {0}; |
|
|
sa.nLength = sizeof(sa); |
|
|
sa.bInheritHandle = TRUE; |
|
|
|
|
|
trace("%sOpening CONOUT...", g_prefix); |
|
|
HANDLE conout = CreateFileW(L"CONOUT$", |
|
|
GENERIC_READ | GENERIC_WRITE, |
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
|
&sa, |
|
|
OPEN_EXISTING, 0, NULL); |
|
|
trace("%sOpening CONOUT... 0x%I64x", g_prefix, (long long)conout); |
|
|
return conout; |
|
|
} |
|
|
|
|
|
static void setConsoleActiveScreenBuffer(HANDLE conout) { |
|
|
trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...", |
|
|
g_prefix, (long long)conout); |
|
|
trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s", |
|
|
g_prefix, (long long)conout, |
|
|
successOrFail(SetConsoleActiveScreenBuffer(conout))); |
|
|
} |
|
|
|
|
|
static void writeTest(HANDLE conout, const char *msg) { |
|
|
char writeData[256]; |
|
|
sprintf(writeData, "%s%s\n", g_prefix, msg); |
|
|
|
|
|
trace("%sWriting to 0x%I64x: '%s'...", |
|
|
g_prefix, (long long)conout, msg); |
|
|
DWORD actual = 0; |
|
|
BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL); |
|
|
trace("%sWriting to 0x%I64x: '%s'... %s", |
|
|
g_prefix, (long long)conout, msg, |
|
|
successOrFail(ret && actual == strlen(writeData))); |
|
|
} |
|
|
|
|
|
static void writeTest(const char *msg) { |
|
|
writeTest(GetStdHandle(STD_OUTPUT_HANDLE), msg); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void test1(int argc, char *argv[]) { |
|
|
if (!strcmp(argv[1], "1")) { |
|
|
startChildProcess(L"1:child"); |
|
|
return; |
|
|
} |
|
|
|
|
|
HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); |
|
|
writeTest(origBuffer, "<-- origBuffer -->"); |
|
|
|
|
|
HANDLE newBuffer = createBuffer(); |
|
|
writeTest(newBuffer, "<-- newBuffer -->"); |
|
|
setConsoleActiveScreenBuffer(newBuffer); |
|
|
Sleep(2000); |
|
|
|
|
|
writeTest(origBuffer, "TEST PASSED!"); |
|
|
|
|
|
|
|
|
|
|
|
closeHandle(newBuffer); |
|
|
|
|
|
while (true) { |
|
|
Sleep(1000); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void test2(int argc, char *argv[]) { |
|
|
if (!strcmp(argv[1], "2")) { |
|
|
startChildProcess(L"2:parent"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "2:parent")) { |
|
|
g_prefix = "parent: "; |
|
|
dumpHandles(); |
|
|
HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); |
|
|
writeTest(origBuffer, "<-- origBuffer -->"); |
|
|
|
|
|
HANDLE newBuffer = createBuffer(); |
|
|
writeTest(newBuffer, "<-- newBuffer -->"); |
|
|
setConsoleActiveScreenBuffer(newBuffer); |
|
|
|
|
|
Sleep(1000); |
|
|
writeTest(newBuffer, "bInheritHandles=FALSE:"); |
|
|
startChildInSameConsole(L"2:child", FALSE); |
|
|
Sleep(1000); |
|
|
writeTest(newBuffer, "bInheritHandles=TRUE:"); |
|
|
startChildInSameConsole(L"2:child", TRUE); |
|
|
|
|
|
Sleep(1000); |
|
|
trace("parent:----"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
closeHandle(newBuffer); |
|
|
|
|
|
Sleep(600 * 1000); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "2:child")) { |
|
|
g_prefix = "child: "; |
|
|
dumpHandles(); |
|
|
|
|
|
|
|
|
trace("child:----"); |
|
|
writeTest("writing to STDOUT"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned int i = 0x1; i <= 0xB0; ++i) { |
|
|
char msg[256]; |
|
|
sprintf(msg, "Write to handle 0x%x", i); |
|
|
HANDLE h = reinterpret_cast<HANDLE>(i); |
|
|
writeTest(h, msg); |
|
|
} |
|
|
|
|
|
Sleep(600 * 1000); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void testA_parentWork() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HANDLE dummy = openConout(); |
|
|
|
|
|
Sleep(3000); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HANDLE newBuffer = openConout(); |
|
|
writeTest(newBuffer, "step2: writing to newBuffer"); |
|
|
|
|
|
Sleep(3000); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
closeHandle(newBuffer); |
|
|
} |
|
|
|
|
|
static void testA_childWork() { |
|
|
HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HANDLE newBuffer = createBuffer(); |
|
|
|
|
|
setConsoleActiveScreenBuffer(newBuffer); |
|
|
writeTest(newBuffer, "<-- newBuffer -->"); |
|
|
|
|
|
Sleep(9000); |
|
|
trace("child:----"); |
|
|
|
|
|
|
|
|
writeTest(newBuffer, "TEST PASSED!"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(3000); |
|
|
|
|
|
setConsoleActiveScreenBuffer(origBuffer); |
|
|
writeTest(origBuffer, "writing to origBuffer"); |
|
|
|
|
|
closeHandle(newBuffer); |
|
|
|
|
|
|
|
|
trace("child: console HWND=0x%I64x", (long long)GetConsoleWindow()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
static void testA(int argc, char *argv[]) { |
|
|
|
|
|
if (!strcmp(argv[1], "A")) { |
|
|
startChildProcess(L"A:parent"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "A:parent")) { |
|
|
g_prefix = "parent: "; |
|
|
trace("parent:----"); |
|
|
dumpHandles(); |
|
|
writeTest("<-- origBuffer -->"); |
|
|
startChildInSameConsole(L"A:child"); |
|
|
testA_parentWork(); |
|
|
Sleep(120000); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "A:child")) { |
|
|
g_prefix = "child: "; |
|
|
dumpHandles(); |
|
|
testA_childWork(); |
|
|
Sleep(120000); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void testB(int argc, char *argv[]) { |
|
|
if (!strcmp(argv[1], "B")) { |
|
|
startChildProcess(L"B:parent"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "B:parent")) { |
|
|
g_prefix = "parent: "; |
|
|
startChildInSameConsole(L"B:child"); |
|
|
writeTest("<-- origBuffer -->"); |
|
|
HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
trace("%s----", g_prefix); |
|
|
HANDLE newBuffer = createBuffer(); |
|
|
setConsoleActiveScreenBuffer(newBuffer); |
|
|
writeTest(newBuffer, "<-- newBuffer -->"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(9000); |
|
|
trace("%s----", g_prefix); |
|
|
writeTest(newBuffer, "TEST PASSED!"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(3000); |
|
|
trace("%s----", g_prefix); |
|
|
setConsoleActiveScreenBuffer(origBuffer); |
|
|
closeHandle(newBuffer); |
|
|
writeTest(origBuffer, "writing to the initial buffer"); |
|
|
|
|
|
Sleep(60000); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "B:child")) { |
|
|
g_prefix = "child: "; |
|
|
Sleep(3000); |
|
|
trace("%s----", g_prefix); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HANDLE newBuffer = openConout(); |
|
|
writeTest(newBuffer, "writing to newBuffer"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(3000); |
|
|
closeHandle(newBuffer); |
|
|
|
|
|
Sleep(60000); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void testC(int argc, char *argv[]) { |
|
|
if (!strcmp(argv[1], "C")) { |
|
|
startChildProcess(L"C:parent"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "C:parent")) { |
|
|
startChildInSameConsole(L"C:child"); |
|
|
writeTest("<-- origBuffer -->"); |
|
|
g_prefix = "parent: "; |
|
|
|
|
|
|
|
|
Sleep(4000); |
|
|
trace("%s---- t=4", g_prefix); |
|
|
const HANDLE newBuffer1 = openConout(); |
|
|
|
|
|
|
|
|
Sleep(4000); |
|
|
trace("%s---- t=8", g_prefix); |
|
|
const HANDLE newBuffer2 = openConout(); |
|
|
closeHandle(newBuffer1); |
|
|
|
|
|
|
|
|
Sleep(17000); |
|
|
trace("%s---- t=25", g_prefix); |
|
|
closeHandle(newBuffer2); |
|
|
|
|
|
Sleep(240000); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "C:child")) { |
|
|
g_prefix = "child: "; |
|
|
|
|
|
|
|
|
Sleep(2000); |
|
|
trace("%s---- t=2", g_prefix); |
|
|
const HANDLE newBuffer1 = createBuffer(); |
|
|
setConsoleActiveScreenBuffer(newBuffer1); |
|
|
writeTest(newBuffer1, "<-- newBuffer1 -->"); |
|
|
|
|
|
|
|
|
Sleep(4000); |
|
|
trace("%s---- t=6", g_prefix); |
|
|
const HANDLE newBuffer2 = createBuffer(); |
|
|
setConsoleActiveScreenBuffer(newBuffer2); |
|
|
writeTest(newBuffer2, "<-- newBuffer2 -->"); |
|
|
|
|
|
|
|
|
|
|
|
Sleep(4000); |
|
|
trace("%s---- t=10", g_prefix); |
|
|
setConsoleActiveScreenBuffer(newBuffer1); |
|
|
writeTest(newBuffer1, "write to newBuffer1: TEST PASSED!"); |
|
|
|
|
|
|
|
|
Sleep(15000); |
|
|
trace("%s---- t=25", g_prefix); |
|
|
closeHandle(newBuffer2); |
|
|
|
|
|
|
|
|
|
|
|
Sleep(10000); |
|
|
trace("%s---- t=35", g_prefix); |
|
|
closeHandle(newBuffer1); |
|
|
|
|
|
Sleep(240000); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void testD(int argc, char *argv[]) { |
|
|
if (!strcmp(argv[1], "D")) { |
|
|
startChildProcess(L"D:parent"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "D:parent")) { |
|
|
g_prefix = "parent: "; |
|
|
HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); |
|
|
writeTest(origBuffer, "<-- origBuffer -->"); |
|
|
|
|
|
HANDLE newBuffer = createBuffer(); |
|
|
writeTest(newBuffer, "<-- newBuffer -->"); |
|
|
setConsoleActiveScreenBuffer(newBuffer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(2000); |
|
|
trace("parent:----"); |
|
|
trace("parent: starting child process"); |
|
|
SetStdHandle(STD_OUTPUT_HANDLE, newBuffer); |
|
|
SetStdHandle(STD_ERROR_HANDLE, newBuffer); |
|
|
startChildInSameConsole(L"D:child"); |
|
|
SetStdHandle(STD_OUTPUT_HANDLE, origBuffer); |
|
|
SetStdHandle(STD_ERROR_HANDLE, origBuffer); |
|
|
|
|
|
|
|
|
Sleep(4000); |
|
|
trace("parent:----"); |
|
|
writeTest(newBuffer, "TEST PASSED!"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(2000); |
|
|
closeHandle(newBuffer); |
|
|
|
|
|
Sleep(120000); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!strcmp(argv[1], "D:child")) { |
|
|
g_prefix = "child: "; |
|
|
|
|
|
trace("child:----"); |
|
|
dumpHandles(); |
|
|
writeTest("writing to newBuffer"); |
|
|
|
|
|
|
|
|
Sleep(2000); |
|
|
trace("child:----"); |
|
|
if (GetStdHandle(STD_ERROR_HANDLE) != GetStdHandle(STD_OUTPUT_HANDLE)) { |
|
|
closeHandle(GetStdHandle(STD_ERROR_HANDLE)); |
|
|
} |
|
|
closeHandle(GetStdHandle(STD_OUTPUT_HANDLE)); |
|
|
closeHandle(GetStdHandle(STD_INPUT_HANDLE)); |
|
|
|
|
|
Sleep(120000); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) { |
|
|
if (argc == 1) { |
|
|
printf("USAGE: %s testnum\n", argv[0]); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (argv[1][0] == '1') { |
|
|
test1(argc, argv); |
|
|
} else if (argv[1][0] == '2') { |
|
|
test2(argc, argv); |
|
|
} else if (argv[1][0] == 'A') { |
|
|
testA(argc, argv); |
|
|
} else if (argv[1][0] == 'B') { |
|
|
testB(argc, argv); |
|
|
} else if (argv[1][0] == 'C') { |
|
|
testC(argc, argv); |
|
|
} else if (argv[1][0] == 'D') { |
|
|
testD(argc, argv); |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|