본문 바로가기

Application Programming Interface/Windows API

Windows NT 4.0 DDK 문서 - IMEAPPS.DOC [Part 1]

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Windows 95 / Windows NT 어플리케이션을 위한 다국어 IME 규격

Version 1.18

마지막 수정일: 1996년 5월 21일, 번역: Luciano Jeong


1 개요

 극동지역 버전의 윈도우를 위해 어플리케이션이 더블바이트 문자를 생성할 수 있도록 입력 방식 편집기(Input Method Editor, 이하 IME)가 특별 프로세스로서 도입되었습니다. IME를 인식하는 어플리케이션은 IME에 특성화괸 메시지를 처리하고 IME API를 호출해야 합니다. 그러나 인터페이스를 사용해 IME와 상호작용하는 프로그램을 개발하는 프로그래머에게는 몇 가지 어려움이 있습니다.

  • API와 메시지의 설계가 다른 윈도우 API와 너무 다릅니다. 이는 인터페이스를 이해하고 사용하는데 어려움을 야기합니다.

  • IME 사용자 인터페이스 대신 어플리케이션이 자신에게 특화된 사용자 인터페이스를 구현하기 위한 정보가 충분하지 않았습니다. 이는 IME와 어플리케이션 사이의 불일관성을 야기했습니디.

  • 극동 지역 버전의 윈도우들은 각기 조금씩 다른 IME API와 메시지를 가졌습니다. 이는 극동 지역 윈도우 각각을 동시에 지원하는 어플리케이션을 개발하는데 어려움이 있었습니다.

 Windows 95는 다른 Win32 API와 메시지와의 더 나은 일관성을 위해 IME 구조를 완전히 개정하였습니다. 더불어 한국어, 일본어, 간체 및 번체 중국어 버전별로 달랐던 처리를 공통의 Win32 인터페이스 안에서 다루도록 통합하였습니다.

 결과적으로 IME는 Win32 DLL의 일부가 되었고 Win32 어플리케이션은 Win32 IME API, 새로운 IME 메시지, 미리 정의된 전역 구조체를 통해 IME의 기능을 이용할 수 있습니다.

  • IME와 Win32 어플리케이션 사이에 모든 필요한 정보는 어플리케이션당 부여된 적어도 하나의 입력 컨텍스트 내에서 유지됩니다. Win32 어플리케이션은 IME API를 사용하여 입력 컨텍스트 내에서 필요한 정보를 가져오거나 설정할 수 있습니다. 각각의 어플리케이션 스레드는 서로 다른 IME와 입력 컨텍스트를 가질 수 있습니다.

  • IME 사용자 인터페이스는 미리 정의된 전역 클래스로서 제공됩니다. Win32 어플리케이션은 IME가 제공하는 사용자 인터페이스 또는 이를 바탕으로 어플리케이션이 확장한 인터페이스를 사용할 수 있습니다.

 다음 항목들은 입력 컨텍스트와 IME 사용자 인터페이스에 관련하여 더 자세한 정보를 제공할 것입니다.

 버전 3.51부터 Windows NT 극동 지역 언어 버전은 Windows 95와 호환되는 Win32 IMM API 함수집합을 지원합니다. Windows 95가 제공하는 ANSI 문자 버전의 IMM32 API 함수에 덧붙여 Windows NT는 유니코드 문자를 인식하는 IMM32 API도 지원합니다. Windows NT 4.0의 IMM32는 함수에서 오류가 발생 시 적절한 오류 코드를 기록합니다. 그러나 Windows NT 3.51 및 Windows 95의 IMM32는 오류 코드를 기록하지 않습니다. 어플리케이션에서는 이 오류 코드를 GetLastError() 함수를 호출함으로써 얻을 수 있습니다.

2 입력 컨텍스트에 대하여

 IME를 제어하기 위하여 Windows는 입력 컨텍스트를 제공합니다. 어플리케이션이 IME에 어떤 작업을 하고자 할 때, 어플리케이션은 입력 컨텍스트를 사용하며 IMM 함수를 호출합니다. 입력 컨텍스트은 IME의 상태를 나타내는 전후사정입니다. 입력 컨텍스트를 사용하는 것은 어플리케이션이 IME의 상태를 의도치 않기 바꾸는 것을 예방해줍니다. 이를 다중 입력 컨텍스트 환경이라 합니다. 입력 컨텍스트는 각 윈도우마다 할당되어있습니다. (즉 윈도우는 자기 자신만의 입력 컨텍스트를 소유할 수 있습니다.)

2.1 기본 입력 컨텍스트

 시스템은 기본적으로 각 스레드마다 입력 컨텍스트를 부여합니다. 이 컨텍스트는 IME를 인식하지 않는 모든 윈도우들이 공유하는 컨텍스트입니다.

2.2 어플리케이션 입력 컨텍스트

 어플리케이션 윈도우는 도중 컴포지션 문자열을 포함한 IME 상태를 설정하기 위하여 자신의 윈도우 핸들을 입력 컨텍스트에 적용할 수 있습니다. 일단 어플리케이션이 입력 컨텍스트를 윈도우 핸들에 연결시키면 시스템은 윈도우가 활성화될 때마다 자동으로 컨텍스트를 선택합니다. 이 기능을 사용하여 어플리케이션은 Windows 3.1 어플리케이션에서처럼 복잡한 포커스 인/아웃 처리로부터 자유로울 수 있습니다.

2.3 입력 컨텍스트 생성

 어플리케이션은 입력 컨텍스트를 생성하고 유지하고 활용하기 위해 IMM 함수를 사용합니다. 어플리케이션은 IMM 함수를 호출하여 자시 자신의 입력 컨텍스트를 생성하고 윈도우를 하나 이상의 입력 컨텍스트에 연결할 수 있습니다. (윈도우가 파괴되기 전에 이전에 쓰이거나 오래된 입력 컨텍스트는 이 윈도우로 다시 연결될 필요가 있습니다.)

2.3.1 입력 컨텍스트 생성

 입력 컨텍스트를 생성하기 위해 어플리케이션은 ImmCreateContext 함수를 호출해야 합니다. 어플리케이션은 이 함수를 호출함으로써 입력 컨텍스트의 핸들을 얻을 수 있습니다. 입력 컨텍스트 핸들은 HIMC라는 자료형으로 정의되어있습니다. 생성된 입력 컨텍스트를 더 이상 사용하지 않는다면 ImmDestroyContext를 호출하여 입력 컨텍스트를 파괴하십시오.

2.3.2 입력 컨텍스트 연결

 입력 컨텍스트가 작동되기 전에 어플리케이션은 ImmAssociateContext 함수를 호출하여 윈도우와 연결되어야 합니다. ImmAssociateContext 함수는 입력 컨텍스트와 윈도우를 연결시키는 역할을 합니다. 이 연결 작업을 수행한 뒤에 윈도우는 포커스가 놓이고 키보드 입력이 가능한 상태일 때 입력 컨텍스트를 사용할 수 있습니다.

2.4 IMM 함수

 어플리케이션 윈도우가 활성화될 때 시스템은 어플리케이션에게 WM_IME_SETCONTEXT 통지 메시지를 보냅니다. IME 전 인식 어플리케이션(full IME aware applications)의 경우 IMM은 입력 컨텍스트를 얻기 위한 함수 집합을 제공합니다. 어플리케이션이 IME로부터 관련 메시지를 얻을 때 어플리케이션은 현재의 IME 상태를 표시하기 위해 전체의 정보를 이들 함수를 통해 얻을 수 있습니다.

3 IME 사용자 인터페이스에 대하여

 IME 사용자 인터페이스는 IME 윈도우, 사용자 인터페이스 윈도우 및 사용자 인터페이스의 구성 요소(components)를 포함합니다.

3.1 특징

 IME 클래스는 미리 정의된 전역 클래스로서 IME 사용자 인터페이스의 어떤 부분과도 상위 클래스로서의 역할을 합니다. IME 클래스의 일반적인 특징은 다른 공용 컨트롤과 다르지 않습니다. 윈도우 인스턴스는 CreateWindowEx 함수로 생성될 수 있고 정적 컨트롤(static control)처럼 IME 클래스로 만들어진 윈도우는 사용자 입력 자체에 반응하지 않습니다. 대신 다양한 종류의 컨트롤 메시지를 수신하여 IME의 전체 사용자 인터페이스에게 이를 알려줍니다. 어플리케이션은 IME 클래스를 사용하여 고유의 IME 윈도우를 생성하거나 ImmGetDefaultIMEWnd 함수를 사용하여 기본 IME 윈도우를 얻을 수 있습니다. 자신의 윈도우 핸들로 IME를 제어하길 원하는 어플리케이션(IME를 인식하는-IME aware 어플리케이션)은 Windows 3.1과 달리 아래와 같은 이점을 얻을 수 있습니다.

  • 후보 목록 윈도우를 포함하여 각 어플리케이션은 고유한 사용자 인터페이스 윈도우 핸들을 가질 수 있습니다. 이는 사용자가 문자를 입력하는 도중에 다른 어플리케이션으로 포커스를 전환할 수 있게 해 줍니다. 이에 비해 Windows 3.1 극동 지역 버전은 사용자가 문자 입력 도중 다른 어플리케이션으로 전환하는 것을 금지하여 왔습니다.

  • IME 사용자 인터페이스는 어플리케이션의 윈도우 핸들을 통지받을 것이기에 이는 어플리케이션 창 이동에 따른 IME의 자동 이동, 윈도우의 캐럿 위치에 따른 자동 추적, 모드 표시와 같이 어플리케이션에게 기본 작동을 제공할 수 있습니다.

 시스템이 하나의 IME 클래스만을 제공하지만 IME 윈도우에는 두 가지가 존재합니다. 하나는 IME를 인식하지 않는(IME unaware) 어플리케이션을 위해 DefWindowProc 함수가 생성한 윈도우로 이를 기본 IME 윈도우(Default IME Window)라고 부릅니다. 이 IME 창은 IME를 인식하지 않는 윈도우들이 공유합니다. 다른 하나는 IME를 인식하는(IME aware) 어플리케이션이 생성한 윈도우로 이를 어플리케이션 IME 윈도우(Application IME Window)라 부릅니다.

3.2 IME 클래스

 Windows 95는 시스템 기본으로 "IME" 이름이 붙은 윈도우 클래스를 지원합니다. 시스템 "IME" 클래스는 IME의 사용자 인터페이스 전체를 다룹니다. 어플리케이션은 이 클래스를 이용해 자신만의 IME 윈도우를 생성할 수 있습니다. 시스템 IME 클래스는 그 자체로 어떤 IME에 의해서 대체될 수 없습니다. Windows 95는 이를 미리 정의된 클래스 그 자체로 둡니다. 시스템 또는 어플리케이션은 클래스 명을 "IME"로 지정하여 CreateWindowEx를 호출합니다. 또한 윈도우 스타일은 WS_POPUP | WS_DISABLE로 지정되어야 합니다.

3.3 기본 IME 윈도우

 시스템은 기본 IME 윈도우를 매 스레드마다 자동으로 생성합니다. 이 윈도우는 IME를 인식하지 않는 어플리케이션을 위해 IME 사용자 인터페이스를 다룰 것입니다. IME 또는 IMM이 WM_IME_xxxx 메시지를 생성할 때 IME를 인식하지 않는 어플리케이션은 이들 메시지를 DefWindowProc 함수로 통과시킵니다. DefWindowProc 함수는 필요한 메시지를 기본 IME 윈도우로 전달하고 이는 IME를 인식하지 않는 어플리케이션을 위한 사용자 인터페이스에게 기본 작동을 하도록 지시합니다. IME를 인식하는 어플리케이션도 IME로부터 메시지를 후킹하지 않을 때는 이 윈도우를 사용할 수 있습니다. 어플리케이션은 필요하다면 어플리케이션 IME 윈도우를 소유할 수 있습니다. 기본 IME 윈도우는 한 스레드에서 작동되는 윈도우라면 서로 공유합니다.

3.4 어플리케이션 IME 윈도우

 어플리케이션은 시스템 IME 클래스를 사용해 어플리케이션 IME 윈도우를 생성할 수 있습니다. IME를 스스로 제어하기 원하는 어플리케이션은 기본적으로 어플리케이션 IME 윈도우를 생성해야 합니다. 생성된 어플리케이션 IME 윈도우 핸들은 어플리케이션에 의해 유지되는데, 어플리케이션은 IME 관련 모든 메시지를 이 윈도우 인스턴스로 전달합니다. 아래 예는 어플리케이션이 어떻게 어플리케이션 IME 윈도우를 사용하는지를 알려줍니다.

3.4.1 WM_IME_NOTIFY / WM_IME_CONTROL / WM_IME_COMPOSITION

 어플리케이션은 IME / IMM가 보낸 메시지를 어플리케이션 IME 윈도우로 전달하기 위해 주어진 윈도우 핸들을 사용합니다. 이들 메시지는 어플리케이션에게 IME의 변환 처리에 재한 전체적인 정보를 알려줍니다. 어플리케이션은 모든 메시지를 이 IME 윈도우로 전달함으로써 기본 작동을 수행할 수 있습니다. 어플리케이션이 컴포지션 윈도우의 위치를 바꾸는 것과 같은 기본 작동을 수정하길 원한다면 어플리케이션은 WM_IME_CONTROL 메시지를 사용할 수 있습니다. 이는 IME 사용자 인터페이스의 상태를 지정하는데 쓰입니다. WM_IME_COMPOSITION / WM_IME_NOTIFY 메시지는 어플리케이션이 처리하지 않는다면 기본적으로 사용자 인터페이스로 전달되어야 합니다.

3.4.2 ImmIsUIMessage() 함수

 ImmIsUIMessage() API는 IME 사용자 인터페이스와 관련된 메시지만을 구분하여 처리하기 위해 어플리케이션 개발자에게 제공될 것입니다. IME 윈도우 핸들이 NULL이면 ImmIsUIMessage() API는 메시지의 실제 처리 없이 결과만을 반환할 것입니다. 어플리케이션은 메시지를 추출하여 IME 윈도우로 전달하기 위해 이 함수를 사용할 수 있습니다. 아래는 이 API를 사용하는 방법을 설명하는 예입니다.

/* 사용 예 */
HWND hIMEWnd; // IME 윈도우의 핸들
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ... 생략 ...
	If(ImmIsUIMessage(hIMEWnd, uMsg, wParam, lParam)==TRUE) {
		// 메시지가 IME 윈도우에 의해 처리되었을 경우
		// 여러분이 원하는 작업을 이어서 수행할 수 있습니다.
		switch(uMsg) {
		case WM_IME_COMPOSITION:
			if(lParam & GCS_RESULTSTR) {
				hIMC=ImmGetContext(hWnd);
				ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpBufResult, dwBufLen);
				ImmReleaseContext(hWnd, hIMC);
			}
			break;
		default:
			break;
		}

		return 0;
	} else {
	// IME 윈도우에 의해 메시지가 처리되지 않은 경우
		switch(uMsg) {
		// ... 생략 ...
		}
	}
}

3.5 UI 윈도우

 시스템 IME 윈도우 클래스는 IME와 어플리케이션로부터 전달된 메시지를 제어하며 일부 메시지를 사용자 인터페이스 윈도우로 전달합니다. "IME" 이름이 붙은 윈도우 클래스의 IME 윈도우는 어플리케이션 또는 시스템에 의해 생성될 것입니다. IME 윈도우가 생성될 때 IME 자체가 제공하는 사용자 인터페이스 윈도우가 생성될 것이며 이는 IME 윈도우가 소유합니다.

3.6 UI 윈도우의 구성요소

 IME의 사용자 인터페이스 윈도우는 사용자 인터페이스를 표시하기 위해 자식 윈도우를 생성할 수 있습니다. 이들 윈도우는 사용자 인터페이스 윈도우의 "구성요소"라고 부릅니다. 예를 들어 상태 윈도우, 컴포지션 윈도우, 후보 목록 등이 있습니다.

4 입력 컨텍스트와 IME 사용자 인터페이스 사용하기

결과 문자열 얻기

 IME가 결정한 조합 결과 문자열을 얻는데는 몇 가지 방법이 있습니다. 기본적으로 어플리케이션은 결과 문자열을 얻기 위해 특정 메시지를 받을때까지 기다려야 합니다.

4.1.1 WM_CHAR 사용하기

 어플리케이션이 WM_CHAR 메시지를 수신하였을 때 어플리케이션은 wParam으로부터 1바이트의 문자 코드를 얻을 수 있습니다. IME에 의해 더블 바이트 문자로 변환되었을 경우 WM_CHAR 메시지는 두 번 수신됩니다. 이 때 첫 번째 수신된 WM_CHAR 메시지에 동반된 wParam은 더블 바이트 중 상위 1바이트이고 두 번째 수신된 WM_CHAR 메시지에 동반된 wParam은 하위 1바이트입니다. IME에 의해 문자열로 변환되었을 때 어플리케이션은 문자열 크기의 바이트 수 만큼 WM_CHAR 메시지를 받게 됩니다.

4.1.2 WM_IME_CHAR 사용하기

 어플리케이션이 WM_IME_CHAR 메시지를 받았을 때 어플리케이션은 wParam으로부터 1 또는 2바이트의 문자코드를 받습니다. IME에 의해 더블 바이트 문자로 변환되었을 경우에도 WM_IME_CHAR 메시지는 한 번만 수신됩니다. 이 메시지와 함께 동반된 wParam은 더블바이트 문자 셋에서의 문자코드를 나타냅니다.

/* 사용 예 */
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	WORD wChar;

	switch(uMsg) {
	case WM_IME_CHAR:
		wChar = LOWORD(wParam);
		if (IsDBCSLeadByte(HIBYTE(wChar) == TRUE) {
			// wChar가 더블바이트 문자인 경우 처리
		} else {
			// wChar가 싱글바이트 문자인 경우 처리
		}
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}

 어플리케이션이 WM_IME_CHAR 메시지를 다룰 때, 더블바이트를 위한 WM_CHAR 메시지는 어플리케이션으로 전달되지 않을 것입니다.

4.1.3 WM_IME_COMPOSITION 사용하기

어플리케이션이 WM_IME_COMPOSITION 메시지를 수신한 경우 어플리케이션은 한번에 결과 문자열을 얻을 수 있습니다. lParam이 GCS_RESULTSTR 비트를 포함한 경우 WM_IME_COMPOSITION을 수신한 윈도우와 연결된 입력 컨텍스트는 결과 문자열을 갖습니다.

/* 사용 예 */
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HIMC hIMC:
	HGLOBAL hstr;
	LPSTR lpstr;
	DWORD dwSize;

	switch(uMsg) {
	case WM_IME_COMPOSITION:
		if (lParam & GCS_RESULTSTR) {
			if (hIMC = ImmGetContext(hWnd)) {
				// 결과 문자열의 길이 얻기
				dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
				// 여기에서 dwSize 만큼의 문자열 버퍼로 쓰기 위한 메모리를 할당
				dwSize++; // 문자열 끝에 붙을 NULL 문자를 고려하여 버퍼의 크기 1만큼 증가

				// dwSize만큼 메모리 블록 할당
				hstr = GlobalAlloc(GHND, dwSize);
				if (hstr == NULL) MyError(ERROR_GLOBALALLOC);

				// 할당받은 메모리 블록에 대한 핸들로 메모리 고정
				lpstr = GlobalLock(hstr);
				if (lpstr == NULL) MyError(ERROR_GLOBALLOCK);

				// IME가 생성한 결과 문자열을 lpstr 버퍼로 복사
				ImmGetCompositionString(hIMC,GCS_RESULTSTR,lpstr,dwSize);
				ImmReleaseContext(hWnd,hIMC);

				// 이 곳에서 문자열 관련 처리를 합니다.

				// 할당된 메모리 해제
				GlobalUnlock(hstr);
				GlobalFree(hstr);
			}
		}
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}

 어플리케이션이 WM_IME_COMPOSITION 메시지를 받았고 lParam이 GCS_RESULTSTR 비트를 포함할 때 결과 문자열을 위한 WM_IME_CHAR 메시지는 어플리케이션으로 전달되지 않을 것입니다.

4.2 IME 상태 전환하기

4.2.1 열기 상태 전환하기

ImmGetOpenStatus 함수를 사용하면 어플리케이션은 현재의 입력 컨텍스트 열기 상태를 가져올 수 있습니다. ImmSetOpenStatus 함수를 사용하면 어플리케이션은 입력 컨텍스트의 열기 상태를 설정할 수 있습니다.

/* 사용 예 */
HIMC hIMC;
BOOL bOpen;
if (hIMC = ImmGetContext(hWnd)) {
	// 입력 컨텍스트의 상태 얻기
	bOpen = ImmGetOpenStatus(hIMC);
	// 현재의 상태를 반전시켜 설정하기
	ImmSetOpenStatus(hIMC, !bOpen);
	ImmReleaseContext(hWnd,hIMC);
}

4.2.2 변환 상태 전환하기

 어플리케이션은 현재의 변환 모드 및 문장 모드를 ImmGetConversionStatus 함수를 호출함으로써 얻을 수 있습니다. 새로운 상태로 설정하려면 어플리케이션은 먼저 ImmGetConversionStatus를 사용하여 현재의 상태를 얻어야 합니다. 새로운 변환 상태를 만들려면 어플리케이션은 이 함수의 반환값에서 몇 가지 비트를 설정해야 합니다. ImmSetConversionStatus 함수는 어플리케이션이 입력 컨텍스트로 상태를 설정할 수 있도록 합니다.

/* 사용 예 */
HIMC hIMC;
DWORD dwConvMode;
DWORD dwSentence;
if (hIMC = ImmGetContext(hWnd)) {
	// 입력 컨텍스트의 현재 상태를 가져오기
	ImmGetConversionStatus(hIMC, &fdwConversion, &fdwSentence);

	// 전각문자 입력 모드를 위한 값 설정
	fdwConversion |= IME_CMODE_FULLSHAPE;

	// 현재 상태를 새로운 상태로 설정
	ImmSetOpenStatus(hIMC, fdwConversion, fdwSentence);
	ImmReleaseContext(hWnd,hIMC);
}

4.3 어플리케이션으로 컴포지션 문자열 그리기

 어플리케이션이 IME 사용자 인터페이스 없이 컴포지션 문자열을 그리고자 할 때, 어플리케이션은 WM_IME_COMPOSITION, WM_IME_STARTCOMPOSITION 및 WM_IME_ENDCOMPOSITION 메시지를 처리해야 합니다. lParam이 GCS_COMPSTR 비트를 가질 경우 WM_IME_COMPOSITION을 수신한 윈도우와 연결된 입력 컨텍스트는 컴포지션 문자열을 갖습니다.

/* 사용 예 */
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HIMC hIMC:
	LPSTR lpCompStr;
	LPBYTE lpbAttr;
	LPSTR lpResultStr;
	DWORD dwStrSize;
	DWORD dwAttrSize;
	switch(uMsg) {
	case WM_IME_COMPOSITION:
		if (lParam & GCS_RESULTSTR) {
			if (hIMC = ImmGetContext(hWnd)) {
				// 결과 문자열의 크기 얻기
				dwSize = ImmGetCompositionString(hIMC,GCS_RESULTSTR,NULL,0);
				// dwSize로 문자열 버퍼를 위한 메모리 할당
				// IME가 생성한 결과 문자열을 lpstr 버퍼로 복사
				ImmGetCompositionString(hIMC,GCS_RESULTSTR,lpResultStr,dwSize);
				// 결과 문자열 그리기
				// 문자열 버퍼를 사용하기 위해 할당한 메모리 해제
				// 입력 컨텍스트 해제
				ImmReleaseContext(hWnd,hIMC);
			}
		}
		if (lParam & (GCS_COMPSTR | GCS_COMPATTR)) {
			if (hIMC = ImmGetContext(hWnd)) {
				// 컴포지션 문자열 및 그 속성 정보에 대한 크기 얻기
				dwStrSize = ImmGetCompositionString(hIMC,GCS_COMPSTR,NULL,0);
				dwAttrSize = ImmGetCompositionString(hIMC,GCS_COMPATTR,NULL,0);

				// 이 곳에서 dwSize로 문자열 버퍼 할당 과정을 기술할 수 있음

				// IME가 생성한 컴포지션 문자열을 lpCompStr로 복사
				ImmGetCompositionString(hIMC,GCS_COMPSTR,lpCompStr,dwSize);
				ImmGetCompositionString(hIMC,GCS_COMPATTR,lpAttrStr,dwSize);

				// 속성 정보를 참조하여 문자열 그리기


				// 이 곳에서 문자열 버퍼 해제합니다.

				// 입력 컨텍스트 해제
				ImmReleaseContext(hWnd,hIMC);
			}
		}
		break;
	case WM_IME_SETCONTEXT:
		// 설정 컨텍스트 다루기
		// lParam의 비트를 지울 필요 있습니다.
		// 이 윈도우는 컴포지션 윈도우를 그릴 수 있습니다.
		lParam &= ~(ISC_SHOWUICOMPOSITIONWINDOW);
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}

4.4 IME 윈도우 다루기

4.4.1 IME 윈도우 생성하고 ImmIsUIMessage 사용하기

/* 사용 예 */
HWND hIMEWnd; // IME 윈도우의 핸들
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	// ... 생략 ...
	If(ImmIsUIMessage(hIMEWnd, uMsg, wParam, lParam)==TRUE) {
		// 이 메시지는 IME 윈도우가 다루지만 개발자도 필요한 경우 처리할 수 있습니다.
		switch(uMsg) {
		case WM_IME_COMPOSITION:
			if(lParam & GCSRESULTSTR) {
				hIMC = ImmGetContext(hwnd);
				ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpBufResult, dwBufLen);
				ImmReleaseContext(hwnd, hIMC);
			}
			break;
		default:
			break;
		}
	return 0;
	} else {
		// IME 윈도우가 처리하지 않은 메시지에 대하여
		switch(uMsg) {
		case WM_CREATE:
			// 어플리케이션이 소유한 IME 윈도우 생성
			hIMEWnd = CreateWindowEx(
				"IME", // 클래스명: IME
				NULL, // 윈도우 제목 없음
				WS_DISABLED | WS_POPUP, // 포커스가 주어지지 않을 윈도우
				0, 0, 0, 0, // 크기를 설정할 필요 없음
				hWnd, // 부모 윈도우
				(int)NULL,
				(HINSTACE)GetWindowLong(hWnd,GWL_HINSTANCE),
				NULL);
			break;
		case WM_DESTORY:
			DestroyWindow(hIMEWnd);
			break;
		// ... 후략 ...
		}
	}
}

4.4.2 IME 윈도우 제어하기

 IME 윈도우를 위해 WM_IME_CONTROL 메시지를 사용하면 어플리케이션은 IME 사용자 인터페이스에 영향을 줄 수 있습니다. 어플리케이션은 컴포지션 문자열의 위치를 IMC_SETCOMPOSITIONWINDOW를 동반한 WM_IME_CONTROL 메시지로 설정할 수 있습니다. 또한 어플리케이션은 IMC_SETCOMPOSITIONFONT를 동반한 WM_IME_CONTROL 메시지로 컴포지션 문자열의 글꼴을 설정할 수 있습니다.

/* 사용 예 */
HWND hIMEWnd; // IME 윈도우 핸들
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	COMPOSITIONFORM cf;
	CANDIDATEFORM cdCandForm;
	POINT ptMyPoint;
	if(ImmIsUIMessage(hIMEWnd, uMsg, wParam, lParam)==TRUE) {
		// 생략
	} else {
		// IME 윈도우가 처리하지 않은 메시지
		switch(uMsg) {
			// 생략
			// WM_IME_CONTROL 메시지를 IMC_GETCOMPOSITIONWINDOW와 함께 hIMEWnd로 보내기
			SendMessage(hIMEWnd, WM_IME_CONTROL, IMC_GETCOMPOSITIONWINDOW, &cf);
			// WM_IME_CONTROL 메시지를 IMC_GETCANDIDATEPOS와 함께 hIMEWnd로 보내기
			SendMessage(hIMEWnd, WM_IME_CONTROL, IMC_GETCANDIDATEPOS, &cdCandForm);
			// 생략
		}
	}
}

4.5 IME 전 인식(full aware) 어플리케이션 만들기

 IME 전 인식 어플리케이션은 스스로 IME 사용자 인터페이스를 그릴 수 있는 어플리케이션을 뜻합니다. 이들 어플리케이션은 IME 윈도우를 사용하지 않고 생성하지도 않습니다. IME 전 인식 어플리케이션은 IME의 모든 메시지를 직접 처리해야하며 DefWindowProc으로 이들 메시지를 전달하지 않습니다.

/* 사용 예 */
HIMC hOldIMC;
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	COMPOSITIONFORM cf;
	POINT ptMyPoint;
	LOGFONT lf;

	// IME 윈도우가 처리하지 않음
	switch(uMsg) {
	case WM_CREATE:
		hIMC = ImmCreateContext();
		hOldIMC = ImmAssociateContext(hWnd ,hIMC);
		break;
	case WM_IME_STARTCOMPOSITION:
		// WM_IME_COMPOSITION 메시지를 받기 위한 준비 작업
		break;
	case WM_IME_ENDCOMPOSITION:
		// 컴포지션 문자열을 마무리하는 작업
		break;
	case WM_IME_COMPOSITION:
		// Get the composition string, the result string and the information to display these strings.
		// 출력을 위해 컴포지션 문자열, 결과 문자열, 정보를 얻습니다.
		break;
	case WM_IME_SETCONTEXT:
		// 설정 컨텍스트를 처리합니다.
		// lParam의 비트를 제거할 필요가 있습니다.
		// 이 윈도우는 컴포지션 문자열과 인덱스 0번의 후보 목록을 그릴 수 있습니다.
		lParam &= ~(ISC_SHOWUICOMPOSITIONWINDOW & ISC_SHOWUICANDIDATEWINDOW);
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	case WM_IME_NOTIFY:
		// IMN_으로 시작하는 서브 메시지를 처리
		// 이 메시지에서 IME와 후보 목록의 상태는 출력, 변경 또는 삭제되어야 함
		switch (wParam)
		{
		case IMN_OPENCANDIDATE:
		case IMN_CHANGECANDIDATE:
		case IMN_CLOSECANDIDATE:
			// 이 어플리케이션은 인덱스 번호 0인 한 개의 후보만을 출력할 것입니다.
			// Bit 0이 1로 되어 있으면 인덱스 0을 의미
			if (lParam == 0x01) // Bit 0 is On, it is index 0.
			{
				// 후보 목록 그리기
			}
			else return DefWindowProc(hWnd, uMsg, wParam, lParam);
			break;
		default:
			// IME 윈도우에 통지하기
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
		}
		break;
	case WM_IME_COMPOSITIONFULL:
		// 그리고자 하는 컴포지션 문자열의 크기 구하기
		// 어플리케이션은 컴포지션 문자열을 이 곳에서 그려야 합니다.
		break;
	// ... 생략 ...
	case WM_DESTROY:
		ImmAssociateContext(hWnd, hOldIMC);
		ImmDestroyContext(hIMC);
		break;
	}
}

4.6 WM_IME_SETCONTEXT에 대하여

 스스로 컴포지션 문자열을 그리는 어플리케이션 윈도우의 경우 WM_IME_COMPOSITION, WM_IME_STARTCOMPOSITION, WM_IME_ENDCOMPOSITION 메시지를 후킹하며 WM_IME_COMPOSITION 메시지를 DefWindowProc 또는 ImmIsUIMessage로 보내지 않습니다.

/* 사용 예 */
long CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg) {
	case WM_IME_COMPOSITION:
		// WM_IME_COMPOSITION 메시지 처리
		break;
	case WM_IME_STARTCOMPOSITION:
		// WM_IME_STARTCOMPOSITION 메시지 처리
		break;
	case WM_IME_ENDCOMPOSITION:
		// WM_IME_ENDCOMPOSITION 메시지 처리
		break;
	// WM_IME_SETCONTEXT 메시지를 처리해야 합니다.
	case WM_IME_SETCONTEXT:
		lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}

4.6.1 컴포지션 문자열을 직접 그리는 어플리케이션

 어플리케이션은 아래와 같은 메시지를 DefWindowProc이나 ImmIsUIMessage로 보내지 않습니다.

  • WM_IME_STARTCOMPOSITION
  • WM_IME_ENDCOMPOSTION
  • WM_IME_COMPOSITION

 이들 어플리케이션은 WM_IME_SETCONTEXT 메시지를 직접 다루어야 하고 DefWindowProc 또는 ImmIsUIMessage를 호출하기 전에 ISC_SHOWUICOMPOSITIONWINDOW 비트를 제거해야 합니다.

4.6.2 후보 목록을 그리는 어플리케이션

 어플리케이션은 아래의 메시지를 DefWindowProc이나 ImmIsUIMessage로 보내지 않습니다.

  • WM_IME_NOTIFY/IMN_OPENCANDIDATEWINDOW
  • WM_IME_NOTIFY/IMN_CLOSECANDIDATEWINDOW
  • WM_IME_NOTIFY/IMN_CHANGECANDIDATEWINDOW

 어플리케이션이 후보를 출력할 때 WM_IME_SETCONTEXT 메시지를 처리해야 하며 DefWindowProc 또는 ImmIsUIMessage 함수를 호출하기 전 ISC_SHOWUICANDIDATEWINDOW 비트를 제거해야 합니다. 다음과 같이 제거 가능합니다.

lParam &= ~ISC_SHOWUIALLCANDIDATEWINDOW.

 이 어플리케이션은 인덱스 번호 2번인 하나의 후보 목록을 보일 것입니다. DefWindowProc 또는 ImmIsUIMessage를 호출하기 전 (ISC_SHOWUICANDIDATEWINDOW << 2) 비트를 제거하십시오.

4.6.3 가이드라인을 출력하는 어플리케이션

 이 어플리케이션은 다음의 메시지를 받았을 때 DefWindowProc 또는 ImmIsUIMessage 함수를 호출하지 않습니다.

  • WM_IME_NOTIFY/IMN_GUIDELINE

 이 어플리케이션은 WM_IME_SETCONTEXT 메시지를 처리해야 하며 DefWindowProc 또는 ImmIsUIMessage를 호출하기 전에 ISC_SHOWUIGUIDEWINDOW 비트를 제거해야 합니다.

4.6.4 소프트키보드를 감추고자 하는 어플리케이션

 이 어플리케이션은 WM_IME_SETCONTEXT 메시지를 처리해야 하며 DefWindowProc 또는 ImmIsUIMessage를 호출하기 전에 ISC_SHOWUISOFTKBD 비트를 제거해야 합니다.

4.6.5 IME의 사용자 인터페이스를 사용하는 어플리케이션

 WM_IME_SETCONTEXT를 직접 처리하지 않아도 되며 lParam 값의 수정 없이 바로 DefWindowProc 또는 ImmIsUIMessage를 호출할 수 있습니다.