[ VC++ ] 콘솔 프로그램 실행 후 출력 메시지 갈무리
포스트
취소

[ VC++ ] 콘솔 프로그램 실행 후 출력 메시지 갈무리

HitCount


Contents


콘솔 프로그램 출력 메시지 갈무리

어플리케이션에서 콘솔 프로그램의 출력된 메시지를 갈무리(Console redirection) 하는 방법에 대한 정리입니다.
콘솔창의 메시지 갈무리를 위해서는 CreateProcess와 CreatePipe 함수를 이용하며 아래의 내용에 유의 하여야 합니다.

  • dwFlags에 STARTF_USESTDHANDLES을 지정.
    • 사용자에 의해 지정된 핸들(hStdInput, hStdOutput, hStdError)을 사용.
    • 지정하지 않을 경우 표준 출력의 기본 값인 콘솔 창에 출력.
  • hStdOutput에 모니터링에 사용할 핸들 지정.
  • hStdError는 오류 모니터링에 사용할 핸들 지정.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#define PEEK_NAMED_PIPE

TCHAR APP[] = _T("C:\\Windows\\System32\\NETSTAT.EXE");

int _tmain(int argc, _TCHAR *argv[]) 
{
	TCHAR szMsgW[256];
	char szMsg[256];
	char szBuff[256];
	DWORD dwRead = 0, dwOut = 0, dwErr = 0;
	HANDLE hStdOutWrite = NULL, hStdOutRead = NULL;
#ifdef PEEK_NAMED_PIPE
	HANDLE hStdErrWrite = NULL, hStdErrRead = NULL;
#endif

	STARTUPINFO si;
	SECURITY_ATTRIBUTES sa;
	PROCESS_INFORMATION pi;

	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	HANDLE hReadPipe;
	CreatePipe(&hStdOutRead, &hStdOutWrite, &sa, 0);  // 콘솔에 출력되는 정보와 연결할 파이프 생성
#ifdef PEEK_NAMED_PIPE
	CreatePipe(&hStdErrRead, &hStdErrWrite, &sa, 0);  // 콘솔에 출력되는 오류와 연결할 파이프 생성
#endif

	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
	si.hStdInput = NULL;
	si.hStdOutput = hStdOutWrite;
#ifdef PEEK_NAMED_PIPE
	si.hStdError = hStdErrWrite;
#else
	si.hStdError = hStdOutWrite;
#endif
	si.wShowWindow = SW_HIDE;
	if (!CreateProcess(NULL, APP, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
	{
		memset(szMsg, 0x00, sizeof(szMsg));
		sprintf(szMsg, "LastError : %ld", GetLastError());
		OutputDebugStringA(szMsg);
	}
	CloseHandle(pi.hThread);

#ifdef PEEK_NAMED_PIPE
	while (PeekNamedPipe(hStdOutRead, NULL, 0, NULL, &dwOut, NULL) ||
		PeekNamedPipe(hStdErrRead, NULL, 0, NULL, &dwErr, NULL))  // 읽을 데이터가 있는지 체크
	{
		if (dwOut <= 0 && dwErr <= 0 && WaitForSingleObject(pi.hProcess, 0) != WAIT_TIMEOUT)
			break;  // 콘솔 프로그램이 종료된 경우 loop를 빠져나간다.

		while (PeekNamedPipe(hStdOutRead, NULL, 0, NULL, &dwOut, NULL) && dwOut > 0)
		{
			memset(szBuff, 0x00, sizeof(szBuff));
			ReadFile(hStdOutRead, szBuff, sizeof(szBuff), &dwRead, NULL);
			szBuff[dwRead] = 0;

			memset(szMsg, 0x00, sizeof(szMsg));
			sprintf(szMsg, ">>[R] %s", szBuff);
			OutputDebugStringA(szMsg);
		}

		while (PeekNamedPipe(hStdErrRead, NULL, 0, NULL, &dwErr, NULL) && dwErr > 0)
		{
			memset(szBuff, 0x00, sizeof(szBuff));
			ReadFile(hStdErrRead, szBuff, sizeof(szBuff), &dwRead, NULL);
			szBuff[dwRead] = 0;

			memset(szMsg, 0x00, sizeof(szMsg));
			sprintf(szMsg, ">>[E] %s", szBuff);
			OutputDebugStringA(szMsg);
		}
	}
#else
	char buf[128];
	CString strOutput, strTemp;
	BOOL result;
	do
	{
		memset(buf, 0x00, sizeof(buf));
		result = ::ReadFile(hStdOutRead, buf, sizeof(buf), &dwRead, 0);

		memset(szMsgW, 0x00, sizeof(szMsgW));
		TCHAR *pBuff = CA2CT(buf);
		_stprintf(szMsgW, _T(">>[O] %s"), (TCHAR *)CA2CT(buf));
		OutputDebugString(szMsgW);

		strTemp = CA2CT(buf);
		strOutput += strTemp.Left(dwRead);
	} while (result);

	memset(szMsgW, 0x00, sizeof(szMsgW));
	_stprintf(szMsgW, _T(">>[T] %s"), strOutput);
	OutputDebugString(szMsgW);
#endif

	CloseHandle(pi.hProcess);
	CloseHandle(hStdOutRead);
	CloseHandle(hStdOutWrite);
#ifdef PEEK_NAMED_PIPE
	CloseHandle(hStdErrRead);
	CloseHandle(hStdErrWrite);
#endif

	return 0;
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.