. 메시지크래커

Ime4 예제는 Ime3과 기능적으로 완전히 동일하다. 다만 앞으로의 실습을 위해 소스를 확장 가능하도록 정리하고 필요한 메시지 처리 함수를 미리 만들어 놓았다는 점만 다르다. 이 예제는 앞으로의 실습을 위한 시작점이 되므로 직접 입력하지 말고 만들어져 있는 예제를 구경만 해보도록 하자.

 

#include <windows.h>

#include <windowsx.h>

#include <imm.h>

#include <stdio.h>

 

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

HWND hWndMain;

LPCTSTR lpszClass=TEXT("Ime4");

 

// 전역변수

TCHAR *buf;

BOOL bComp;

int off;

int FontHeight;

 

// 함수 원형

BOOL OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);

void OnChar(HWND hWnd, TCHAR ch, int cRepeat);

void OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);

void OnPaint(HWND hWnd);

void OnSetFocus(HWND hWnd, HWND hwndOldFocus);

void OnKillFocus(HWND hWnd, HWND hwndNewFocus);

void OnDestroy(HWND hWnd);

LRESULT OnImeComposition(HWND hWnd, WPARAM wParam, LPARAM lParam);

LRESULT OnImeChar(HWND hWnd, WPARAM wParam, LPARAM lParam);

void OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);

void OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags);

void OnLButtonUp(HWND hWnd, int x, int y, UINT keyFlags);

void OnTimer(HWND hWnd, UINT id);

void OnSize(HWND hWnd, UINT state, int cx, int cy);

void OnHScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);

void OnVScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);

void OnContextMenu(HWND hWnd, HWND hwndContext, UINT xPos, UINT yPos);

void OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify);

BOOL OnSetCursor(HWND hWnd, HWND hwndCursor, UINT codeHitTest, UINT msg);

UINT OnGetDlgCode(HWND hWnd, LPMSG lpmsg);

 

int GetCharWidth(HDC hdc, TCHAR *ch, int len);

void SetCaret();

inline BOOL IsDBCS(int nPos);

int GetPrevOff(int nPos);

int GetNextOff(int nPos);

void Insert(int nPos, TCHAR *str);

void Delete(int nPos, int nCount);

 

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

       ,LPSTR lpszCmdParam,int nCmdShow)

{

     HWND hWnd;

     MSG Message;

     WNDCLASS WndClass;

     g_hInst=hInstance;

    

     WndClass.cbClsExtra=0;

     WndClass.cbWndExtra=0;

     WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);

     WndClass.hCursor=LoadCursor(NULL,IDC_IBEAM);

     WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

     WndClass.hInstance=hInstance;

     WndClass.lpfnWndProc=(WNDPROC)WndProc;

     WndClass.lpszClassName=lpszClass;

     WndClass.lpszMenuName=NULL;

     WndClass.style=CS_HREDRAW | CS_VREDRAW;

     RegisterClass(&WndClass);

 

     hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,

          CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

          NULL,(HMENU)NULL,hInstance,NULL);

     ShowWindow(hWnd,nCmdShow);

    

     while(GetMessage(&Message,0,0,0)) {

          TranslateMessage(&Message);

          DispatchMessage(&Message);

     }

     return (int)Message.wParam;

}

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     switch(iMessage) {

          HANDLE_MSG(hWnd, WM_CREATE, OnCreate);

          HANDLE_MSG(hWnd, WM_DESTROY, OnDestroy);

          HANDLE_MSG(hWnd, WM_CHAR, OnChar);

          HANDLE_MSG(hWnd, WM_KEYDOWN, OnKey);

        HANDLE_MSG(hWnd, WM_KEYUP, OnKey);

          HANDLE_MSG(hWnd, WM_PAINT, OnPaint);

          HANDLE_MSG(hWnd, WM_SETFOCUS, OnSetFocus);

          HANDLE_MSG(hWnd, WM_KILLFOCUS, OnKillFocus);

        HANDLE_MSG(hWnd, WM_LBUTTONDOWN, OnLButtonDown);

        HANDLE_MSG(hWnd, WM_LBUTTONDBLCLK, OnLButtonDown);

        HANDLE_MSG(hWnd, WM_MOUSEMOVE, OnMouseMove);

        HANDLE_MSG(hWnd, WM_LBUTTONUP, OnLButtonUp);

        HANDLE_MSG(hWnd, WM_TIMER, OnTimer);

        HANDLE_MSG(hWnd, WM_SIZE, OnSize);

        HANDLE_MSG(hWnd, WM_HSCROLL, OnHScroll);

        HANDLE_MSG(hWnd, WM_VSCROLL, OnVScroll);

        HANDLE_MSG(hWnd, WM_CONTEXTMENU, OnContextMenu);

        HANDLE_MSG(hWnd, WM_COMMAND, OnCommand);

        HANDLE_MSG(hWnd, WM_SETCURSOR, OnSetCursor);

        HANDLE_MSG(hWnd, WM_GETDLGCODE, OnGetDlgCode);

     case WM_IME_COMPOSITION:

          return OnImeComposition(hWnd, wParam, lParam);

     case WM_IME_CHAR:

          return OnImeChar(hWnd, wParam, lParam);

     case WM_IME_STARTCOMPOSITION:

          return 0;

     }

     return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

///////////////////////////////////////////////////////////////////////////////////

// 메시지 핸들러

 

BOOL OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)

{

     HDC hdc;

     TEXTMETRIC tm;

 

     bComp=FALSE;

     buf=(TCHAR *)malloc(65536);

     memset(buf,0,65536);

     hWndMain=hWnd;

     off=0;

     hdc=GetDC(hWnd);

     GetTextMetrics(hdc,&tm);

     FontHeight=tm.tmHeight;

     ReleaseDC(hWnd,hdc);

     return TRUE;

}

 

void OnDestroy(HWND hWnd)

{

     PostQuitMessage(0);

     free(buf);

}

 

void OnSize(HWND hWnd, UINT state, int cx, int cy)

{

}

 

void OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)

{

    if (fDown==FALSE)

        return;

 

     switch (vk)

     {

     case VK_LEFT:

          if (off > 0) {

              off=GetPrevOff(off);

              SetCaret();

          }

          return;

     case VK_RIGHT:

          if (off < (int)strlen(buf)) {

              off=GetNextOff(off);

              SetCaret();

          }

          return;

     case VK_HOME:

          off=0;

          SetCaret();

          return;

     case VK_END:

          off=lstrlen(buf);

          SetCaret();

          return;

     case VK_DELETE:

          if (IsDBCS(off)) {

              Delete(off, 2);

          } else {

              Delete(off, 1);

          }

          InvalidateRect(hWnd,NULL,TRUE);

          return;

     case VK_BACK:

          if (off == 0)

              return;

          off=GetPrevOff(off);

          SendMessage(hWnd,WM_KEYDOWN,VK_DELETE,0);

          SetCaret();

          return;

     }

}

 

void OnPaint(HWND hWnd)

{

     HDC hdc;

     PAINTSTRUCT ps;

 

     hdc=BeginPaint(hWnd,&ps);

     TextOut(hdc,0,0,buf,lstrlen(buf));

     EndPaint(hWnd,&ps);

}

 

void OnSetFocus(HWND hWnd, HWND hwndOldFocus)

{

     SetCaret();

}

 

void OnKillFocus(HWND hWnd, HWND hwndNewFocus)

{

     DestroyCaret();

}

 

void OnChar(HWND hWnd, TCHAR ch, int cRepeat)

{

     TCHAR szChar[3];

     int i;

 

     if (ch == 8)

          return;

     szChar[0]=ch;

     szChar[1]=0;

     for (i=0;i<cRepeat;i++) {

          Insert(off,szChar);

          off+=lstrlen(szChar);

     }

     bComp=FALSE;

     InvalidateRect(hWnd,NULL,TRUE);

     SetCaret();

}

 

LRESULT OnImeChar(HWND hWnd, WPARAM wParam, LPARAM lParam)

{

     TCHAR szChar[3];

 

     if (IsDBCSLeadByte((BYTE)(wParam >> 8))) {

          szChar[0]=HIBYTE(LOWORD(wParam));

          szChar[1]=LOBYTE(LOWORD(wParam));

          szChar[2]=0;

     } else {

          szChar[0]=(BYTE)wParam;

          szChar[1]=0;

     }

     if (bComp) {

          off-=2;

          Delete(off,2);

     }

     Insert(off,szChar);

     off+=lstrlen(szChar);

     bComp=FALSE;

     InvalidateRect(hWnd,NULL,TRUE);

     SetCaret();

     return 0;

}

 

LRESULT OnImeComposition(HWND hWnd, WPARAM wParam, LPARAM lParam)

{

     HIMC hImc;

     TCHAR *szComp;

     int len;

 

     hImc=ImmGetContext(hWnd);

     if (lParam & GCS_COMPSTR) {

          len=ImmGetCompositionString(hImc,GCS_COMPSTR,NULL,0);

          szComp=(TCHAR *)malloc(len+1);

          ImmGetCompositionString(hImc,GCS_COMPSTR,szComp,len);

          szComp[len]=0;

          if (bComp) {

              off-=2;

              Delete(off,2);

          }

          if (len == 0) {

              bComp=FALSE;

          } else {

              bComp=TRUE;

          }

 

          Insert(off,szComp);

          off+=len;

          ImmReleaseContext(hWnd,hImc);

          free(szComp);

          InvalidateRect(hWnd,NULL,TRUE);

          SetCaret();

     }

 

     return DefWindowProc(hWnd,WM_IME_COMPOSITION,wParam,lParam);

}

 

void OnHScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos)

{

}

 

void OnVScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos)

{

}

 

void OnContextMenu(HWND hWnd, HWND hwndContext, UINT xPos, UINT yPos)

{

}

 

void OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify)

{

}

 

void OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)

{

}

 

void OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags)

{

}

 

void OnLButtonUp(HWND hWnd, int x, int y, UINT keyFlags)

{

}

 

void OnTimer(HWND hWnd, UINT id)

{

}

 

BOOL OnSetCursor(HWND hWnd, HWND hwndCursor, UINT codeHitTest, UINT msg)

{

    return FORWARD_WM_SETCURSOR(hWnd,hwndCursor,codeHitTest,msg,DefWindowProc);

}

 

UINT OnGetDlgCode(HWND hWnd, LPMSG lpmsg)

{

    return 0;

}

 

///////////////////////////////////////////////////////////////////////////////////

// 일반 함수들

 

int GetCharWidth(HDC hdc, TCHAR *ch, int len)

{

     SIZE sz;

     GetTextExtentPoint32(hdc, ch, len, &sz);

     return sz.cx;

}

 

void SetCaret()

{

     SIZE sz;

     HDC hdc;

     int toff;

     int caretwidth;

 

     hdc=GetDC(hWndMain);

     if (bComp) {

          toff=off-2;

          caretwidth=GetCharWidth(hdc,buf+toff,2);

     } else {

          toff=off;

          caretwidth=2;

     }

     CreateCaret(hWndMain,NULL,caretwidth,FontHeight);

     ShowCaret(hWndMain);

 

     GetTextExtentPoint32(hdc,buf,toff,&sz);

     SetCaretPos(sz.cx,0);

     ReleaseDC(hWndMain,hdc);

}

 

inline BOOL IsDBCS(int nPos)

{

     return (IsDBCSLeadByte(buf[nPos]));

}

 

int GetPrevOff(int nPos)

{

     int n, size;

 

     if (nPos==0) {

          return 0;

     }

 

     for (n=0;;) {

          if (IsDBCS(n)) {

              size=2;

          } else {

              size=1;

          }

          n+=size;

          if (n >= nPos)

              break;

     }

     return n-size;

}

 

int GetNextOff(int nPos)

{

     if (IsDBCS(nPos)) {

          return nPos+2;

     } else {

          return nPos+1;

     }

}

 

void Insert(int nPos, TCHAR *str)

{

     int len;

     int movelen;

 

     len=lstrlen(str);

     if (len==0) return;

     movelen=lstrlen(buf+nPos)+1;

     memmove(buf+nPos+len,buf+nPos,movelen);

     memcpy(buf+nPos,str,len);

}

 

void Delete(int nPos, int nCount)

{

     int movelen;

 

     if (nCount == 0) return;

     if (lstrlen(buf) < nPos+nCount) return;

 

     movelen=lstrlen(buf+nPos+nCount)+1;

     memmove(buf+nPos, buf+nPos+nCount, movelen);

}

 

형태상으로 가장 큰 변화는 메시지크래커를 사용했다는 점이다. 프로젝트가 조금만 커지면 WndProc에서 모든 것을 다 처리하기는 어렵고 또 앞으로 점점 코드가 추가될 것이므로 아예 깔끔한 메시지크래커로 구조를 바꾸었다. 이제 메시지 하나당 처리하는 함수가 하나씩 배정되므로 지역변수를 쓰기가 훨씬 더 자유로워졌다.

Ime3 WndProc에 있던 모든 코드들이 OnCreate, OnPaint 등의 개별 메시지 처리 함수로 이동되었고 wParam, lParam 대신에 메시지크래커가 전달한 인수를 사용하였다. 예를 들어 WM_CHAR 메시지는 wParam으로 입력된 문자를 읽었지만 OnChar 함수는 메시지크래커가 전달한 ch 인수로부터 입력 문자를 구한다. 하지만 어디까지나 코드의 형태가 조금 바뀐 것일 뿐이지 논리적 변화는 전혀 없다. 다만 다음 한 가지가 조금 다르게 처리된다.

 

void OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)

{

     if (fDown==FALSE)

          return;

 

메시지 크래커는 키 누름과 놓음, 마우스 클릭과 더블클릭 등 비슷한 메시지를 같은 함수에 연결하는 특징이 있다. 실제 어떤 메시지가 전달되었는지는 인수로 알려 주는데 키 메시지의 경우 fDown으로 WM_KEYUP인지 WM_KEYDOWN인지를 구분한다. 이 예제는 아직 WM_KEYUP을 사용하고 있지 않으므로 fDown FALSE일 때는 아무 것도 처리하지 않도록 해야 한다. 그렇지 않으면 <BS> <Del>키를 눌렀다 뗄 때 두 글자씩 지워질 것이다.

일반함수들은 모두 소스 앞쪽에 원형 선언을 해두었다. 그리고 이후 여러분의 실습 편의를 위해 Ime3에서 처리하는 메시지 외에 앞으로 추가할 메시지들도 미리 핸들러를 작성해두었다. 메시지크래커 형식의 함수를 추가하는 것은 직접 하기에는 좀 까다롭기 때문이다. 만약 Ime3에서 Ime4로의 변화를 도저히 이해할 수 없다면 메시지크래커에 대해 먼저 연구해보기 바라며 CD-ROM Doc 폴더에 메시지크래커에 관한 문서를 참조하도록 하자.