다음을 통해 공유


키보드 입력 사용

창은 키 입력 메시지 및 문자 메시지 형식으로 키보드 입력을 받습니다. 창에 연결된 메시지 루프에는 키 입력 메시지를 해당 문자 메시지로 변환하는 코드가 포함되어야 합니다. 창에 클라이언트 영역에 키보드 입력이 표시되면 다음 문자가 입력될 위치를 나타내는 캐리트를 만들고 표시해야 합니다. 다음 섹션에서는 키보드 입력 수신, 처리 및 표시와 관련된 코드에 대해 설명합니다.

키 입력 메시지 처리

키보드 포커스가 있는 창의 창 프로시저는 사용자가 키보드에서 입력할 때 키 입력 메시지를 받습니다. 키 입력 메시지는 WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWNWM_SYSKEYUP. 일반적인 창 프로시저는 WM_KEYDOWN제외한 모든 키 입력 메시지를 무시합니다. 사용자가 키를 누르면 시스템에서 WM_KEYDOWN 메시지를 게시합니다.

창 프로시저가 WM_KEYDOWN 메시지를 받으면 메시지와 함께 제공되는 가상 키 코드를 검사하여 키 입력을 처리하는 방법을 결정해야 합니다. 가상 키 코드는 메시지의 wParam 매개 변수에 있습니다. 일반적으로 애플리케이션은 함수 키, 커서 이동 키 및 INS, DEL, HOME 및 END와 같은 특수한 용도 키를 포함하여 비문자 키에서 생성된 키 입력만 처리합니다.

다음 예제에서는 일반적인 애플리케이션에서 키 입력 메시지를 수신하고 처리하는 데 사용하는 창 프로시저 프레임워크를 보여 있습니다.

case WM_KEYDOWN: 
	switch (wParam) 
	{ 
		case VK_LEFT: 
			// Process the LEFT ARROW key. 
			break; 

		case VK_RIGHT: 
			// Process the RIGHT ARROW key. 
			break; 

		case VK_UP: 
			// Process the UP ARROW key. 
			break; 

		case VK_DOWN: 
			// Process the DOWN ARROW key. 
			break; 

		case VK_HOME: 
			// Process the HOME key. 
			break; 

		case VK_END: 
			// Process the END key. 
			break; 

		case VK_INSERT: 
			// Process the INS key. 
			break; 

		case VK_DELETE: 
			// Process the DEL key. 
			break; 

		case VK_F2: 
			// Process the F2 key. 
			break; 

		default: 
			// Process other non-character keystrokes. 
			break; 
	}

문자 메시지 번역

사용자로부터 문자 입력을 받는 스레드는 메시지 루프에 TranslateMessage 함수를 포함해야 합니다. 이 함수는 키 입력 메시지의 가상 키 코드를 검사하고 코드가 문자에 해당하는 경우 메시지 큐에 문자 메시지를 배치합니다. 메시지 루프의 다음 반복에서 문자 메시지가 제거되고 디스패치됩니다. 메시지의 wParam 매개 변수에는 문자 코드가 포함되어 있습니다.

일반적으로 스레드의 메시지 루프는 TranslateMessage 함수를 사용하여 가상 키 메시지뿐만 아니라 모든 메시지를 번역해야 합니다. TranslateMessage 다른 유형의 메시지에는 영향을 주지 않지만 키보드 입력이 올바르게 번역되도록 보장합니다. 다음 예제에서는 일반적인 스레드 메시지 루프에 TranslateMessage 함수를 포함하는 방법을 보여 줍니다.

MSG msg;
BOOL bRet;

while ((bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) 
{
    if (bRet == -1);
    {
        // handle the error and possibly exit
    }
    else
    { 
        if (TranslateAccelerator(hwndMain, haccl, &msg) == 0) 
        { 
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        } 
    } 
}

문자 메시지 처리

TranslateMessage 함수가 문자 키에 해당하는 가상 키 코드를 변환하면 창 프로시저가 문자 메시지를 받습니다. 문자 메시지는 WM_CHAR, WM_DEADCHAR, WM_SYSCHARWM_SYSDEADCHAR. 일반적인 창 프로시저는 WM_CHAR제외한 모든 문자 메시지를 무시합니다. TranslateMessage 함수는 사용자가 다음 키를 누를 때 WM_CHAR 메시지를 생성합니다.

  • 모든 문자 키
  • 백스페이스
  • ENTER(캐리지 리턴)
  • ESC
  • SHIFT+ENTER (줄 바꾸기)

창 프로시저가 WM_CHAR 메시지를 받으면 메시지와 함께 문자 코드를 검사하여 문자를 처리하는 방법을 결정해야 합니다. 문자 코드는 메시지의 wParam 매개 변수에 있습니다.

다음 예제에서는 일반적인 애플리케이션에서 문자 메시지를 수신하고 처리하는 데 사용하는 창 프로시저 프레임워크를 보여 있습니다.

case WM_CHAR:
	switch (wParam)
	{
		case 0x08: // or '\b'
			// Process a backspace.
			break;
			
		case 0x09: // or '\t'
			// Process a tab.
			break;

		case 0x0A: // or '\n'
			// Process a linefeed.
			break;
			
		case 0x0D:
			// Process a carriage return.
			break;

		case 0x1B:
			// Process an escape.
			break;

		default:
			// Process displayable characters.
			break;
	}

Caret 사용

키보드 입력을 받는 창은 일반적으로 창의 클라이언트 영역에서 사용자가 입력하는 문자를 표시합니다. 창은 다음 문자가 나타날 클라이언트 영역의 위치를 나타내기 위해 caret를 사용해야 합니다. 또한 창은 키보드 포커스를 받을 때 캐리트를 만들고 표시하고 포커스를 잃을 때 캐리트를 숨기고 제거해야 합니다. 창은 WM_SETFOCUSWM_KILLFOCUS 메시지 처리에서 이러한 작업을 수행할 수 있습니다. 캐럿에 대한 자세한 내용은 Carets참조하세요.

키보드 입력 표시

이 섹션의 예제에서는 애플리케이션이 키보드에서 문자를 수신하고, 창의 클라이언트 영역에 표시하고, 캐럿의 위치를 입력된 각 문자로 업데이트하는 방법을 보여줍니다. 또한 왼쪽 화살표, 오른쪽 화살표, 홈 및 끝 키 입력에 대한 응답으로 캐리트를 이동하는 방법을 보여 줍니다. Shift+오른쪽 화살표 키 조합에 대한 응답으로 선택한 텍스트를 강조 표시하는 방법을 보여 줍니다.

WM_CREATE 메시지를 처리하는 동안 예제에 표시된 창 프로시저는 키보드 입력을 저장하기 위해 64K 버퍼를 할당합니다. 또한 현재 로드된 글꼴의 메트릭을 검색하여 글꼴에 문자의 높이와 평균 너비를 저장합니다. 높이와 너비는 클라이언트 영역의 크기에 따라 줄 길이와 최대 줄 수를 계산하기 위해 WM_SIZE 메시지를 처리하는 데 사용됩니다.

창 프로시저는 WM_SETFOCUS 메시지를 처리할 때 캐리트를 만들고 표시합니다. WM_KILLFOCUS 메시지를 처리할 때 캐럿을 숨기고 삭제합니다.

WM_CHAR 메시지를 처리할 때 창 프로시저는 문자를 표시하고, 입력 버퍼에 저장하고, 캐리트 위치를 업데이트합니다. 또한 창 프로시저는 탭 문자를 4개의 연속된 공백 문자로 변환합니다. 백스페이스, 줄 바꿈 및 이스케이프 문자는 경고음을 생성하지만 그렇지 않으면 처리되지 않습니다.

창 프로시저는 WM_KEYDOWN 메시지를 처리할 때 커서의 왼쪽, 오른쪽, 끝, 그리고 홈 이동을 수행합니다. 오른쪽 화살표 키의 동작을 처리하는 동안 창 프로시저는 SHIFT 키의 상태를 확인하고, 아래쪽에 있는 경우 캐리트가 이동될 때 캐리트의 오른쪽에 있는 문자를 선택합니다.

다음 코드는 유니코드 또는 ANSI로 컴파일할 수 있도록 작성되었습니다. 소스 코드가 UNICODE를 정의하는 경우 문자열은 유니코드 문자로 처리됩니다. 그렇지 않으면 ANSI 문자로 처리됩니다.

#define BUFSIZE 65535 
#define SHIFTED 0x8000 
 
LONG APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    HDC hdc;                   // handle to device context 
    TEXTMETRIC tm;             // structure for text metrics 
    static DWORD dwCharX;      // average width of characters 
    static DWORD dwCharY;      // height of characters 
    static DWORD dwClientX;    // width of client area 
    static DWORD dwClientY;    // height of client area 
    static DWORD dwLineLen;    // line length 
    static DWORD dwLines;      // text lines in client area 
    static int nCaretPosX = 0; // horizontal position of caret 
    static int nCaretPosY = 0; // vertical position of caret 
    static int nCharWidth = 0; // width of a character 
    static int cch = 0;        // characters in buffer 
    static int nCurChar = 0;   // index of current character 
    static PTCHAR pchInputBuf; // input buffer 
    int i, j;                  // loop counters 
    int cCR = 0;               // count of carriage returns 
    int nCRIndex = 0;          // index of last carriage return 
    int nVirtKey;              // virtual-key code 
    TCHAR szBuf[128];          // temporary buffer 
    TCHAR ch;                  // current character 
    PAINTSTRUCT ps;            // required by BeginPaint 
    RECT rc;                   // output rectangle for DrawText 
    SIZE sz;                   // string dimensions 
    COLORREF crPrevText;       // previous text color 
    COLORREF crPrevBk;         // previous background color
    size_t * pcch;
    HRESULT hResult; 
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
            // Get the metrics of the current font. 
 
            hdc = GetDC(hwndMain); 
            GetTextMetrics(hdc, &tm); 
            ReleaseDC(hwndMain, hdc); 
 
            // Save the average character width and height. 
 
            dwCharX = tm.tmAveCharWidth; 
            dwCharY = tm.tmHeight; 
 
            // Allocate a buffer to store keyboard input. 
 
            pchInputBuf = (LPTSTR) GlobalAlloc(GPTR, 
                BUFSIZE * sizeof(TCHAR)); 
            return 0; 
 
        case WM_SIZE: 
 
            // Save the new width and height of the client area. 
 
            dwClientX = LOWORD(lParam); 
            dwClientY = HIWORD(lParam); 
 
            // Calculate the maximum width of a line and the 
            // maximum number of lines in the client area. 
            
            dwLineLen = dwClientX - dwCharX; 
            dwLines = dwClientY / dwCharY; 
            break; 
 
 
        case WM_SETFOCUS: 
 
            // Create, position, and display the caret when the 
            // window receives the keyboard focus. 
 
            CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY); 
            SetCaretPos(nCaretPosX, nCaretPosY * dwCharY); 
            ShowCaret(hwndMain); 
            break; 
 
        case WM_KILLFOCUS: 
 
            // Hide and destroy the caret when the window loses the 
            // keyboard focus. 
 
            HideCaret(hwndMain); 
            DestroyCaret(); 
            break; 
 
        case WM_CHAR:
        // check if current location is close enough to the
        // end of the buffer that a buffer overflow may
        // occur. If so, add null and display contents. 
    if (cch > BUFSIZE-5)
    {
        pchInputBuf[cch] = 0x00;
        SendMessage(hwndMain, WM_PAINT, 0, 0);
    } 
            switch (wParam) 
            { 
                case 0x08:  // backspace 
                case 0x0A:  // linefeed 
                case 0x1B:  // escape 
                    MessageBeep((UINT) -1); 
                    return 0; 
 
                case 0x09:  // tab 
 
                    // Convert tabs to four consecutive spaces. 
 
                    for (i = 0; i < 4; i++) 
                        SendMessage(hwndMain, WM_CHAR, 0x20, 0); 
                    return 0; 
 
                case 0x0D:  // carriage return 
 
                    // Record the carriage return and position the 
                    // caret at the beginning of the new line.

                    pchInputBuf[cch++] = 0x0D; 
                    nCaretPosX = 0; 
                    nCaretPosY += 1; 
                    break; 
 
                default:    // displayable character 
 
                    ch = (TCHAR) wParam; 
                    HideCaret(hwndMain); 
 
                    // Retrieve the character's width and output 
                    // the character. 
 
                    hdc = GetDC(hwndMain); 
                    GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam, 
                        &nCharWidth); 
                    TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY, 
                        &ch, 1); 
                    ReleaseDC(hwndMain, hdc); 
 
                    // Store the character in the buffer.
 
                    pchInputBuf[cch++] = ch; 
 
                    // Calculate the new horizontal position of the 
                    // caret. If the position exceeds the maximum, 
                    // insert a carriage return and move the caret 
                    // to the beginning of the next line. 
 
                    nCaretPosX += nCharWidth; 
                    if ((DWORD) nCaretPosX > dwLineLen) 
                    { 
                        nCaretPosX = 0;
                        pchInputBuf[cch++] = 0x0D; 
                        ++nCaretPosY; 
                    } 
                    nCurChar = cch; 
                    ShowCaret(hwndMain); 
                    break; 
            } 
            SetCaretPos(nCaretPosX, nCaretPosY * dwCharY); 
            break; 
 
        case WM_KEYDOWN: 
            switch (wParam) 
            { 
                case VK_LEFT:   // LEFT ARROW 
 
                    // The caret can move only to the beginning of 
                    // the current line. 
 
                    if (nCaretPosX > 0) 
                    { 
                        HideCaret(hwndMain); 
 
                        // Retrieve the character to the left of 
                        // the caret, calculate the character's 
                        // width, then subtract the width from the 
                        // current horizontal position of the caret 
                        // to obtain the new position. 
 
                        ch = pchInputBuf[--nCurChar]; 
                        hdc = GetDC(hwndMain); 
                        GetCharWidth32(hdc, ch, ch, &nCharWidth); 
                        ReleaseDC(hwndMain, hdc); 
                        nCaretPosX = max(nCaretPosX - nCharWidth, 
                            0); 
                        ShowCaret(hwndMain); 
                    } 
                    break; 
 
                case VK_RIGHT:  // RIGHT ARROW 
 
                    // Caret moves to the right or, when a carriage 
                    // return is encountered, to the beginning of 
                    // the next line. 
 
                    if (nCurChar < cch) 
                    { 
                        HideCaret(hwndMain); 
 
                        // Retrieve the character to the right of 
                        // the caret. If it's a carriage return, 
                        // position the caret at the beginning of 
                        // the next line. 
 
                        ch = pchInputBuf[nCurChar]; 
                        if (ch == 0x0D) 
                        { 
                            nCaretPosX = 0; 
                            nCaretPosY++; 
                        } 
 
                        // If the character isn't a carriage 
                        // return, check to see whether the SHIFT 
                        // key is down. If it is, invert the text 
                        // colors and output the character. 
 
                        else 
                        { 
                            hdc = GetDC(hwndMain); 
                            nVirtKey = GetKeyState(VK_SHIFT); 
                            if (nVirtKey & SHIFTED) 
                            { 
                                crPrevText = SetTextColor(hdc, 
                                    RGB(255, 255, 255)); 
                                crPrevBk = SetBkColor(hdc, 
                                    RGB(0,0,0)); 
                                TextOut(hdc, nCaretPosX, 
                                    nCaretPosY * dwCharY, 
                                    &ch, 1); 
                                SetTextColor(hdc, crPrevText); 
                                SetBkColor(hdc, crPrevBk); 
                            } 
 
                            // Get the width of the character and 
                            // calculate the new horizontal 
                            // position of the caret. 
 
                            GetCharWidth32(hdc, ch, ch, &nCharWidth); 
                            ReleaseDC(hwndMain, hdc); 
                            nCaretPosX = nCaretPosX + nCharWidth; 
                        } 
                        nCurChar++; 
                        ShowCaret(hwndMain); 
                        break; 
                    } 
                    break; 
 
                case VK_UP:     // UP ARROW 
                case VK_DOWN:   // DOWN ARROW 
                    MessageBeep((UINT) -1); 
                    return 0; 
 
                case VK_HOME:   // HOME 
 
                    // Set the caret's position to the upper left 
                    // corner of the client area. 
 
                    nCaretPosX = nCaretPosY = 0; 
                    nCurChar = 0; 
                    break; 
 
                case VK_END:    // END  
 
                    // Move the caret to the end of the text. 
 
                    for (i=0; i < cch; i++) 
                    { 
                        // Count the carriage returns and save the 
                        // index of the last one. 
 
                        if (pchInputBuf[i] == 0x0D) 
                        { 
                            cCR++; 
                            nCRIndex = i + 1; 
                        } 
                    } 
                    nCaretPosY = cCR; 
 
                    // Copy all text between the last carriage 
                    // return and the end of the keyboard input 
                    // buffer to a temporary buffer. 
 
                    for (i = nCRIndex, j = 0; i < cch; i++, j++) 
                        szBuf[j] = pchInputBuf[i]; 
                    szBuf[j] = TEXT('\0'); 
 
                    // Retrieve the text extent and use it 
                    // to set the horizontal position of the 
                    // caret. 
 
                    hdc = GetDC(hwndMain);
                    hResult = StringCchLength(szBuf, 128, pcch);
                    if (FAILED(hResult))
                    {
                    // TODO: write error handler
                    } 
                    GetTextExtentPoint32(hdc, szBuf, *pcch, 
                        &sz); 
                    nCaretPosX = sz.cx; 
                    ReleaseDC(hwndMain, hdc); 
                    nCurChar = cch; 
                    break; 
 
                default: 
                    break; 
            } 
            SetCaretPos(nCaretPosX, nCaretPosY * dwCharY); 
            break; 
 
        case WM_PAINT: 
            if (cch == 0)       // nothing in input buffer 
                break; 
 
            hdc = BeginPaint(hwndMain, &ps); 
            HideCaret(hwndMain); 
 
            // Set the clipping rectangle, and then draw the text 
            // into it. 
 
            SetRect(&rc, 0, 0, dwLineLen, dwClientY); 
            DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT); 
 
            ShowCaret(hwndMain); 
            EndPaint(hwndMain, &ps); 
            break; 
        
        // Process other messages. 
        
        case WM_DESTROY: 
            PostQuitMessage(0); 
 
            // Free the input buffer. 
 
            GlobalFree((HGLOBAL) pchInputBuf); 
            UnregisterHotKey(hwndMain, 0xAAAA); 
            break; 
 
        default: 
            return DefWindowProc(hwndMain, uMsg, wParam, lParam); 
    } 
    return NULL; 
}