본문 바로가기
언어/MFC Program

MFC에서 console 다루기

by darkdevilness 2013. 1. 8.
728x90

아래 문서는 다른 곳에서 copy 한 것이며,  Test 해 보지 않음

1 Ctrl+C, Ctrl+Break, Close 버튼 막기

    SetConsoleCtrlHandler 함수를 통해 컨트롤 핸들러를 등록한 다음 무시하기 원하는 이벤트일 경우 TRUE를 반환하면 된다.

 

BOOL WINAPI DisableConsoleWindowClose(DWORD event)
{
BOOL handled = FALSE;

switch (event)
{
case CTRL_C_EVENT: handled = TRUE; break;
case CTRL_BREAK_EVENT: handled = TRUE; break;
case CTRL_CLOSE_EVENT: handled = TRUE; break;
default: break;
}

return handled;
}

SetConsoleCtrlHandler(DisableConsoleWindowClose, TRUE); 

 

세 이벤트를 모두 막아버릴 경우, 윈도우를 정상적으로 닫을 방법이 없어지기 때문에 유의. 강제 종료해야 한다.

2 콘솔창 입력

    GetNumberOfConsoleInputEvents 함수와 ReadConsoleInput 함수를 사용하면 간단히 처리할 수 있다. ReadConsoleInput 함수가 입력이 들어오기 전까지 블로킹되는 함수이기 때문에, GetNumberOfConsoleInputEvents 함수를 이용해 이미 들어와있는 입력이 있는지 체크해야 한다.

    커서 위치 처리가 좀 난감한데, [WWW]http://www.adrianxw.dk/SoftwareSite/index.html 페이지의 'Working with Windows Consoles' 부분을 참고하기 바란다. 라이브러리 잘 만들어주면 예전 도스에서 콘솔 출력하듯이 UI를 구성할 수 있을 것 같기도 한데, 왠지 일이 너무 많을 것 같다.

    void ReadConsoleInput()
    {
        HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
        INPUT_RECORD inRecord = {0, };
        TCHAR readChar[2] = {0, };
        DWORD eventCount = 0;
        DWORD numRecordsRead = 0;
    
        GetNumberOfConsoleInputEvents(hIn, &eventCount);
        while (eventCount > 0)
        {
            if (ReadConsoleInput(hIn, &inRecord, 1, &numRecordsRead) && numRecordsRead == 1)
            {
                if (inRecord.EventType == KEY_EVENT && inRecord.Event.KeyEvent.bKeyDown)
                {
                    if (inRecord.Event.KeyEvent.wVirtualKeyCode != VK_RETURN)
                    {
                        readChar[0] = inRecord.Event.KeyEvent.uChar.AsciiChar;
                        std::cout << readChar << std::endl;
                    }
                    else
                    {
                        std::cout << _T("\r\n") << std::endl;
                    }
                }
    
                --eventCount;
            }
            else
            {
                break;
            }
        }
    }
    
    int main(int argc, TCHAR** argv)
    {
        while (true)
        {
            ...do something else...
            ReadConsoleInput();
        }
    }

     



3 콘솔창 출력

    3.1 콘솔창 열기

      bool OpenConsole()
      {
          if (!::AllocConsole()) { return false; }
      
          HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
          if (INVALID_HANDLE_VALUE == hConsole) 
          {
              ::FreeConsole();
              return false;
          }
              
          return true;
      }
       

       


    3.2 콘솔창 닫기

      void CloseConsole()
      {
          ::FreeConsole();
          ::CloseHandle(hConsole);
          hConsole = INVALID_HANDLE_VALUE;
      } 

       


    3.3 콘솔창에다 출력하기

      void WriteConsole(const char* szMsg)
      {
          DWORD dwBytes = 0;
          ::WriteFile(m_hConsole, szMsg, ::strlen(szMsg), &dwBytes, NULL);
          ::WriteFile(m_hConsole, "\n", ::strlen("\n"), &dwBytes, NULL);
      } 

       


    3.4 STDOUT, STDIN, STDERR 리다이렉션

      #include <fcntl.h>
      #include <io.h>
      #include <iostream>
      #include <fstream>
      
      // STDOUT, STDIN, STDERR redirection
      void RedirectStandardIo(HANDLE hConsole)
      {
          long  StdHandle = 0;
          int   ConHandle = 0;
          FILE* fp        = NULL;
      
          // STDOUT redirection
          StdHandle = PtrToLong(hConsole);
          ConHandle = _open_osfhandle(StdHandle, _O_TEXT);
          fp = _fdopen(ConHandle, "w");
          *stdout = *fp;
          setvbuf(stdout, NULL, _IONBF, 0);
      
          // STDIN redirection
          StdHandle = PtrToLong(::GetStdHandle(STD_INPUT_HANDLE));
          ConHandle = _open_osfhandle(StdHandle, _O_TEXT);
          fp = _fdopen(ConHandle, "r");
          *stdin = *fp;
          setvbuf(stdin, NULL, _IONBF, 0);
      
          // STDERR redirection
          StdHandle = PtrToLong(::GetStdHandle(STD_ERROR_HANDLE));
          ConHandle = _open_osfhandle(StdHandle, _O_TEXT);
          fp = _fdopen( ConHandle, "w" );
          *stderr = *fp;
          setvbuf(stderr, NULL, _IONBF, 0);
      
          // cout, wcout, cin, wcin, wcerr, cerr, wclog, clog sync
          std::ios::sync_with_stdio();
      } 

       


    3.5 듀얼 모니터일때 옆모니터에 콘솔창 띄우기 by 쑥갓

      #define _WIN32_WINNT 0x0500
      
      ...
      
      bool bDualMonitor = GetSystemMetrics( SM_CMONITORS ) > 1;
      if ( bDualMonitor == true )
      {
          HWND hConsoleWindow = GetConsoleWindow();
          const int screenWidth = GetSystemMetrics( SM_CXSCREEN );
      
          RECT rect;
          GetWindowRect( hConsoleWindow, &rect );
      
          // 아래의 +- 코드는 주모니터가 어느 쪽에 있느냐에 따라 변경해줘야한다.
          // 주모니터가 어느 쪽에 있는지 알아내는 방법이 있을 듯 한데...
          int windowX = rect.left;
          if( windowX > screenWidth )
          {
              windowX -= screenWidth; // 주모니터가 왼쪽에 있는 경우
              //windowX += screenWidth; // 주모니터가 오른쪽에 있는 경우
          }
          else
          {
              windowX += screenWidth; // 주모니터가 왼쪽에 있는 경우
              //windowX -= screenWidth; // 주모니터가 오른쪽에 있는 경우
          }
      
          SetWindowPos( hConsoleWindow, NULL, windowX, rect.top, 0, 0, SWP_NOSIZE );
      
          // 콘솔 윈도우를 최대화해주기 위해서는 위의 SetWindowPos 대신에 아래를 사용한다.
          //const int screenHeight = GetSystemMetrics( SM_CYSCREEN );
          //SetWindowPos( hConsoleWindow, NULL, windowX, 0, screenWidth, screenHeight, 0 );
      } 

       



4 콘솔창을 띄우지 않은 상태에서 콘솔 프로그램을 실행하고, 그 결과를 파일에다 쓰기

    void CTttDlg::OnOK() 
    {
        // TODO: Add extra validation here
        PROCESS_INFORMATION pInfo;
        STARTUPINFO         sInfo;
        DWORD               exitCode;
        
        HANDLE hOut = CreateFile("stdinout.txt", 
            GENERIC_WRITE, NULL, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        
        sInfo.cb              = sizeof(STARTUPINFO);
        sInfo.lpReserved      = NULL;
        sInfo.lpReserved2     = NULL;
        sInfo.cbReserved2     = 0;
        sInfo.lpDesktop       = NULL;
        sInfo.lpTitle         = NULL;
        sInfo.dwFlags         = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
        sInfo.dwX             = 0;
        sInfo.dwY             = 0;
        sInfo.dwFillAttribute = 0;
        sInfo.wShowWindow     = SW_HIDE;
        sInfo.hStdOutput      = hOut;
        
        if (!CreateProcess(NULL, "command.com /c lha a tt",
            NULL, NULL, TRUE, 0, NULL, "c:\\", &sInfo, &pInfo)) 
        {
            printf("ERROR: Cannot launch child process\n");
            exit(1);
        }
        
        // Give the process time to execute and finish
        WaitForSingleObject(pInfo.hProcess, INFINITE);
        
        if (GetExitCodeProcess(pInfo.hProcess, &exitCode)) 
        {
            switch(exitCode) 
            {
                case STILL_ACTIVE: 
                    printf("Process is still active\n");
                    break;
        
                default:
                    printf("Exit code = %d\n", exitCode);
                    break;
            }
        } 
        else 
        {
            printf("GetExitCodeProcess() failed\n");
        }
        
        CloseHandle(hOut);
    } 

     

    위의 CreateProcess를 호출하는 부분에서 "Command.com /c"를 호출하는 부분이 있는데 이렇게 해주지 않으면, 호출하는 프로그램이 DOS 프로그램인 경우 자동으로 창이 닫히지 않는 문제가 발생하기 때문.
728x90