. 입력 컨텍스트

입력 컨텍스트(Input Context) IME가 내부적으로 사용하는 구조체이며 이 구조체에는 조립 문자열, 변환 모드, IME 윈도우의 위치 등 IME의 현재 상태에 대한 정보들이 저장된다. 화면에 무엇인가를 그리려면 DC의 핸들이 필요한 것처럼 IME의 상태를 조사하거나 변경하고 싶다면 먼저 입력 컨텍스트의 핸들을 구해야 한다. 이 핸들은 다음 함수로 구하고 해제한다.

 

HIMC ImmGetContext(HWND hWnd);

BOOL ImmReleaseContext(HWND hWnd,HIMC hIMC);

 

문자입력을 받아들이는 윈도우들은 모두 입력 컨텍스트를 가지는데 윈도우 핸들을 주면 입력 컨텍스트의 핸들인 hImc 값을 구한다. 시스템은 별도의 지정이 없어도 스레드마다 디폴트 입력 컨텍스트를 자동으로 생성하며 이 컨텍스트는 스레드 내의 모든 윈도우에 의해 공유된다. ImmGetContext 함수로 구한 입력 컨텍스트 핸들로부터 IME의 정보를 구하거나 변경할 수 있으며, 입력 컨텍스트도 메모리를 차지하므로 다 사용하고 난 후에는 반드시 ImmReleaseContext 함수로 해제해야 한다.

입력 컨텍스트로부터 상태를 구하고 변경하는 것 중에 가장 실용적인 예는 입력모드를 소프트웨어적으로 변환하는 것이다. 디폴트 입력 컨텍스트는 영문 입력모드로 시작되기 때문에 프로그램을 실행하면 항상 영문 입력모드에서 시작한다. 그래서 한글을 입력받아야 할 경우 항상 한글키나 <Shift+Space>키로 모드를 변경한 후 입력해야 하는 불편함이 있는데 입력 컨텍스트를 조작하면 모드를 강제로 변경할 수도 있다. 이때는 다음 두 함수를 사용한다.

 

BOOL ImmGetConversionStatus(HIMC hIMC,LPDWORD lpfdwConversion,LPDWORD lpfdwSentence);

BOOL ImmSetConversionStatus(HIMC hIMC,DWORD fdwConversion,DWORD fdwSentence);

 

입력 컨텍스트의 핸들을 주고 변환 모드와 문장 모드를 지정하거나 조사하는데 문장 모드는 한글에서는 별 의미가 없고 변환 모드 중 IME_CMODE_NATIVE 플래그만 알아 두면 된다. 이 플래그가 있으면 한글 입력모드고 그렇지 않으면 영문 입력모드다. 다음 ImeStatus예제는 소프트웨어적으로 한글 입력모드를 변환하는데 직접 만들어 볼 필요는 없고 CD-ROM에 있는 예제를 참고만 하도록 하자.

 

#include <windows.h>

#include <imm.h>

#include "resource.h"

 

BOOL CALLBACK MainDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam);

HINSTANCE g_hInst;

HWND hDlgMain;

 

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

     ,LPSTR lpszCmdParam,int nCmdShow)

{

     g_hInst=hInstance;

    

     DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP, MainDlgProc);

    

     return 0;

}

 

BOOL MyGetImeMode(HWND hEdit)

{

     HIMC hImc;

     DWORD Con, Sen;

 

     hImc=ImmGetContext(hEdit);

     ImmGetConversionStatus(hImc,&Con,&Sen);

     ImmReleaseContext(hEdit,hImc );

     if (Con & IME_CMODE_NATIVE) {

          return TRUE;

     } else {

          return FALSE;

     }

}

 

void MySetImeMode(HWND hEdit, BOOL bHan)

{

     HIMC hImc;

     hImc=ImmGetContext(hEdit);

 

     if (bHan == TRUE) {

          ImmSetConversionStatus(hImc,IME_CMODE_NATIVE,IME_SMODE_NONE);

     } else {

          ImmSetConversionStatus(hImc,0,IME_SMODE_NONE);

     }

 

     ImmReleaseContext(hEdit,hImc );

}

 

BOOL CALLBACK MainDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     static HWND hEdit1, hEdit2;

 

     switch(iMessage)

     {

     case WM_INITDIALOG:

          hDlgMain = hDlg;

          hEdit1=GetDlgItem(hDlg,IDC_EDIT1);

          hEdit2=GetDlgItem(hDlg,IDC_EDIT2);

          MySetImeMode(hEdit1,TRUE);

 

          return TRUE;

     case WM_COMMAND:

          switch (LOWORD(wParam))

          {

          case IDCANCEL:

              EndDialog(hDlg,0);

              return TRUE;

          case IDC_BTNHANGUL:

              MySetImeMode(hEdit1,TRUE);

              SetFocus(hEdit1);

              return TRUE;

          case IDC_BTNENGLISH:

              MySetImeMode(hEdit1,FALSE);

              SetFocus(hEdit1);

              return TRUE;

          case IDC_BTNGETMODE:

              if (MyGetImeMode(hEdit1) == TRUE) {

                   MessageBox(hDlg,"한글 입력모드입니다","알림",MB_OK);

              } else {

                   MessageBox(hDlg,"영문 입력모드입니다","알림",MB_OK);

              }

              return TRUE;

          }

          return FALSE;

     }

     return FALSE;

}

 

IME 관련 함수와 상수는 imm.h에 선언되어 있으므로 이 헤더를 반드시 인클루드해야 하며 관련 함수의 정보는 imm32.lib에 정의되어 있으므로 임포트 라이브러리도 반드시 링크시켜야 한다. 프로젝트의 속성 대화상자에서 링커/입력/추가 종속성에 imm32.lib를 연결했다. 이후의 예제들은 물론이고 IME를 사용하는 모든 프로젝트는 이 헤더와 연결 파일들이 필요하므로 반드시 기억해놓도록 하자.

MyGetImeMode 함수는 hEdit 윈도우의 입력모드가 한글 모드인지 영문 모드인지 조사한다. MySetImeMode 함수는 입력모드를 변경하는데, bHan 인수가 TRUE이면 hEdit의 입력모드를 한글로 바꾸어 준다. 간단한 예제라 대화상자 기반으로 작성했다. 직접 실행해보자.

프로그램 시작시에 MySetImeMode를 호출하여 한글 모드로 변환해두었으므로 프로그램을 시작하자 마자 곧바로 한글이 입력된다. 한글키로 입력모드를 변환할 수도 있고 아래쪽에 있는 버튼을 클릭하여 소프트웨어적으로 입력모드를 변환하는 것도 가능하다. 한글을 입력받는 에디트라면 미리 한글 입력모드로 바꿔 주고 영문을 입력받아야 하는 에디트라면 영문 입력모드로 바꿔 주는 것은 사용자를 위한 상당한 배려가 된다.

그런데 이 예제는 문제점이 있다. 두 개의 에디트 컨트롤이 있는데 한쪽에서 입력모드를 변경하면 다른 에디트의 입력모드도 같이 변경된다는 점이다. 위쪽 에디트에서 한글 모드로 변경한 후 아래쪽 에디트에 입력해보면 역시 한글이 입력되며 반대도 마찬가지다. 한쪽은 한글로 다른 쪽은 영문으로 입력받도록 하고 싶지만 그렇게는 안된다. 왜냐하면 시스템이 만드는 디폴트 입력 컨텍스트는 스레드 내의 모든 윈도우가 공유하기 때문이다.

그래서 한 윈도우가 입력 컨텍스트를 구한 후 상태를 변경하면 다른 윈도우들도 이 변경에 영향을 받게 된다. 만약 윈도우 별로 입력 컨텍스트를 만들어 상태를 따로 유지하고 싶다면 그렇게 할 수도 있다. 다음 세 함수는 입력 컨텍스트를 생성, 해제하고 윈도우와 연결시킨다.

 

HIMC ImmCreateContext(void);

HIMC ImmAssociateContext(HWND hWnd,HIMC hIMC);

BOOL ImmDestroyContext(HIMC hIMC);

 

ImmCreateContext 함수는 입력 컨텍스트를 위한 메모리를 할당하고 그 핸들을 리턴한다. 이렇게 만들어진 입력 컨텍스트는 ImmAssociateContext 함수로 특정 윈도우와 연결할 수 있는데 이렇게 되면 해당 윈도우는 자기만의 입력 컨텍스트를 가지고 고유의 상태를 유지할 수 있다. ImmAssociateContext 함수는 이전에 연결되어 있던 입력 컨텍스트 핸들을 리턴하는데 이 핸들은 복구를 위해 잘 보관해야 한다.

직접 만든 입력 컨텍스트는 반드시 해제해야 하는데 해제하기 전에 윈도우와의 연결 관계를 먼저 끊어야 하며, 그러기 위해서는 이전의 입력 컨텍스트 핸들을 다시 연결해야 한다. 마치 브러시나 펜을 사용할 때 OldPen, OldBrush 변수가 복구를 위해 필요한 것처럼 이전 컨텍스트 핸들도 같은 이유로 필요하다. 예제를 다음과 같이 수정해보자.

 

BOOL CALLBACK MainDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     static HWND hEdit1, hEdit2;

    static HIMC hImc, OldImc;

 

     switch(iMessage)

     {

     case WM_INITDIALOG:

          hDlgMain = hDlg;

          hEdit1=GetDlgItem(hDlg,IDC_EDIT1);

          hEdit2=GetDlgItem(hDlg,IDC_EDIT2);

          MySetImeMode(hEdit1,TRUE);

 

        hImc=ImmCreateContext();

        OldImc=ImmAssociateContext(hEdit2,hImc);

          return TRUE;

     case WM_COMMAND:

          switch (LOWORD(wParam))

          {

          case IDCANCEL:

               EndDialog(hDlg,0);

            ImmAssociateContext(hEdit2,OldImc);

            ImmDestroyContext(hImc);

               return TRUE;

 

소스에서 굵게 표시된 부분만 새로 입력한다. 이런 소스 표기법은 이 책 전반에 걸쳐 적용되는데 새로 입력되었거나 변경된 코드, 또는 내용상 강조할만한 소스 부분을 굵게 인쇄하였다. 새로운 입력 컨텍스트를 생성하고 hEdit2에 연결했으므로 hEdit2는 주 스레드의 입력 컨텍스트와는 다른 IME 상태를 유지할 수 있다. 그래서 hEdit2의 변환 모드나 기타 상태는 모두 독립적으로 유지된다.