사용자 도구

사이트 도구


kb:windowsconsoleapp

Windows Consone

콘솔 프로그램 관련 팁들

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);

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

콘솔창 입력

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

커서 위치 처리가 좀 난감한데, 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();
    }
}

콘솔창 출력

콘솔창 열기

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

콘솔창 닫기

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

콘솔창에다 출력하기

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

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();
}

듀얼 모니터일때 옆모니터에 콘솔창 띄우기 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 );
}

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

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 프로그램인 경우 자동으로 창이 닫히지 않는 문제가 발생하기 때문.

링크

kb/windowsconsoleapp.txt · 마지막으로 수정됨: 2014/11/09 21:05 (바깥 편집)