Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[148] [팁] 콘솔 어플리케이션의 표준출력/표준에러 받아오기
박지훈.임프 [cbuilder] 20847 읽음    2001-08-16 16:22
임프랍니다.

이번 팁은, 콘솔 어플리케이션의 표준출력(stdout)과 표준에러(stderr)를 받아오기입니다.
여기서 콘솔 어플리케이션이란, 도스 프로그램처럼 윈도우즈의 GUI 방식이 아닌 표준 입출력으로 동작하는
프로그램을 말합니다.

표준 출력의 경우, 도스 컴맨드라인에서 쓰는 리다이렉션(>)을 써서 해결할 수도 있습니다. 하지만,
이 방법을 써서는 아무리해도 표준에러(stderr)를 받아올 수는 없습니다. 표준에러란 콘솔 어플리케이션에서
에러가 발생했을 경우 출력하는 것으로, 콘솔 어플을 작성한 프로그래머에 따라서 stdout으로 바로 출력할
수도 있지만, 원칙은 stderr로 출력하도록 되어있지요. 기본적으로 stderr은 stdout과 같은 핸들로 설정되어
있으므로 콘솔 어플을 컴맨드라인상에서 실행하면 에러메시지가 정상적인 출력과 함께 출력됩니다.
하지만 컴맨드라인에서 리다이렉션을 써서 stdout을 파일 등 다른 출력으로 리다이렉트시킨 경우,
에러메시지는 그대로 콘솔로 나오게 됩니다.

다음의 테크닉은 이러한 stdout과 stderr을 함께 받아오는 것입니다. 게다가, 임시 파일로 출력해서 받아오는
방법 대신 파이프를 써서 깔끔하게 받아올 수 있게 하였습니다.
이 방법은 원래 일본 사이트 중 Kaityo라는 분의 사이트에 실린 팁을 보고 따라해본 것인데, 제대로 동작하지
않아 MSDN에서 찾아본 후 양쪽 모두를 참고하여 잘못된 부분을 수정하고 다시 작성해본 것입니다.
http://homepage1.nifty.com/kaityo/
http://support.microsoft.com/support/kb/articles/Q190/3/51.ASP

빈 프로젝트를 열고, 화면에 버튼 하나와 메모 하나를 놓습니다.
그리고 버튼의 클릭 이벤트 핸들러에 다음과 같이 코딩합니다.

참고: 델파이 코드는 본문의 가장 밑에 있습니다.

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    bool flag;
    HANDLE hwrite, hread;
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = true;

    // 어노니머스 파이프 생성
    flag = CreatePipe(&hread, &hwrite, &sa, 0);
    if (!flag)
    {
         ShowMessage("Fail to open pipe.");
         return;
    }

    // 콘솔어플리케이션 프로세스 실행을 위한 준비
    STARTUPINFO si;
    memset(&si, 0, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdOutput = hwrite;  // 표준출력(stdout) 리다이렉션
    si.hStdError = hwrite;   // 표준에러(stderr) 리다이렉션
    PROCESS_INFORMATION pi;

    // 콘솔어플리케이션 프로세스 실행
    flag = CreateProcess(NULL, "bcc32.exe", NULL, NULL, true, DETACHED_PROCESS,
      NULL, NULL, &si, &pi);
    if(!flag)
    {
       ShowMessage("Fail to create process.");
       return;
    }

    CloseHandle(hwrite);//이것을 하지 않으면 프로세스가 block된다

    char buffer[512];
    DWORD BytesRead;
    AnsiString ResultString;
    while(ReadFile(hread, buffer, sizeof(buffer)-1, &BytesRead, NULL) && BytesRead)
    {
        buffer[BytesRead] = '\0';
        ResultString = ResultString + buffer;
        /*if (GetAsyncKeyState(VK_ESCAPE)<0) // 실행에 많은 시간이 걸릴 경우
        {                                    // 주석해제 필요
            ShowMessage("Canceled.");
            break;
        }
        Application->ProcessMessages();*/
    }
    CloseHandle(hread);

    Memo1->Lines->Text = ResultString;
}


이 예제를 조금 응용해보면, 표준출력과 표준에러를 별도로 출력한다든지 혹은 파일로 출력과 같은 응용도
가능합니다. 또한 표준 입력도 재지향이 가능하겠지요.

그럼 도움되시길...

델파이 코드

procedure TForm1.Button2Click(Sender: TObject); 
var 
  start: TStartupInfo; 
  sec: TSecurityAttributes; 
  pinfo: TProcessInformation; 
  hwrite, hread: THandle; 
  BytesRead: DWORD; 
  Buffer: array[0..512] of AnsiChar; 
  ResultString: string; 
begin 
  sec.nLength := sizeof(sec); 
  sec.lpSecurityDescriptor := nil; 
  sec.bInheritHandle := true; 

  // 어노니머스 파이프 생성 
  if CreatePipe(hread, hwrite, @sec, 0)<>true then 
  begin 
    ShowMessage('Fail to open pipe.'); 
    exit; 
  end; 

  // 콘솔어플리케이션 프로세스 실행을 위한 준비 
  FillChar(start, sizeof(STARTUPINFO), 0); 
  start.cb := sizeof(start); 
  start.dwFlags := STARTF_USESTDHANDLES; 
  start.hStdOutput := hwrite;  // 표준출력(stdout) 리다이렉션 
  start.hStdError := hwrite;   // 표준에러(stderr) 리다이렉션 

  // 콘솔어플리케이션 프로세스 실행 
  if CreateProcess(nil, 'dcc32.exe', nil, nil, true, DETACHED_PROCESS, nil, nil, start, pinfo) <> true then 
  begin 
    ShowMessage(AnsiString('CreateProcess() failed: ') + IntToStr(GetLastError)); 
    exit; 
  end; 

  CloseHandle(hwrite);//이것을 하지 않으면 프로세스가 block된다 

  while ReadFile(hread, Buffer, Length(buffer)-1, BytesRead, nil) and (BytesRead>0) do 
  begin 
    Buffer[BytesRead] := #0; 
    ResultString := ResultString + Buffer; 
    {if GetAsyncKeyState(VK_ESCAPE)<0 then // 실행에 많은 시간이 걸릴 경우 
    begin                                  // 주석해제 필요 
      ShowMessage('Canceled.'); 
      break; 
    end; 
    Application.ProcessMessages;} 
  end; 
  CloseHandle(hread); 

  Memo1.Lines.Text := ResultString; 
end;

+ -

관련 글 리스트
148 [팁] 콘솔 어플리케이션의 표준출력/표준에러 받아오기 박지훈.임프 20847 2001/08/16
(링크)     C++Builder Tip'N Tricks > [팁] 콘솔 어플리케이션의 표준출력/표준에러 받아오기
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.