How to Programming by Unicode in windows programming - 3

자, 이제 실전이다. 뭐, 다들 책이나 기본적인 윈도우즈 프레임 툴은 어떻게 짜는지 알것이다.

내가 배운것은

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCSTR lpszClass="First";

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)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc=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,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 switch(iMessage)
 {
 case WM_DESTROY : PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

요코드다. 자, 그렇다면 유니코드를 해놓고 비주얼 스튜디오 2010에서 돌리면 어디가 에러가 나는가?
바로 hWnd=CreateWindow 요 줄에 있는 두번째 배개변수 lpszClass가 문제다. 왜냐하면 윈도우즈 함수에서는 분명히 유니코드를 이용하겠다고 했는데 이 lpszClass는 아직 유니코드가 아닌갑다. 그래서 나는 이 lpszClass의 유니코드명이 뭔지 한참이나 찾았으나 인터넷에는 도저히 나와있지 않다. 왜냐하면!! lpszClass는 내가 코드안에서 생성해준 변수명이잖아-_ -;;;;

자, 5번줄을 보면
LPCSTR lpszClass="First";
요게 있었어..
복습을 해보자.
LPCSTR은 Const하고 long pointer하고 str인것이다. 유니코드의 흔적조차 없다.
그러면 어떻게 해야할까?

그거야 바로 LPC와 STR사이에
W나 T를 넣어주면 된다. 다른 코드들과의 이식성을 위해 일부러 연습을 위해서라도 LPCTSTR을 사용해주기로 한다.
자, 이제 해결?!

5번줄만 바꿔서
LPCTSTR lpszClass="First";
자, 디버깅 해보자.

으악! 에러다. 에러다. 에러다. ㅠㅜㅠ 아 도대체 왜!!!
자, 읽어보자면
error C2440: '초기화 중' : 'const char [6]'에서 'LPCTSTR'(으)로 변환할 수 없습니다.
라고 뜬다.
즉슨, 그럼 "First" 가 5자리+NULL의 문자열 상수일테고 왼쪽은 LPCTSTR이다.
음.. LPCTSTR은 틀릴리가 없고, 그럼 const char [6]이 틀렸다는 건데.
일단 내가 짠것은 유니코드니 wchar_t const * != const char[6] 라는 소리겠지.
틀린말은 아니다, "First"는 분명한 문자열 상수고 그것을 다른 자료형에 넣을순 없는거니까.
자, 그러면 형변환이다!

자, 그럼 유니코드 안에서 LPCTSTR == wchar_t const * 이니까
LPCTSTR lpszClass == (wchar_t const *)"First";

라고 고 치고 해보자.... 와우!!! 디버깅 에러 없고, 컴파일 제대로 되..... 었지만 출력물이 이상하다. 무슨 이상한 한자하고 잡스러운게 많이 나온다. ㅠㅠㅠㅠㅠㅠ 앙데 ㅠㅠ

음... 이것의 이유는 찾아본 결과 일단, 강제 형변환에 있다. 일반적인 char 형과 유니코드의 공간 할당 크기가 다르고 이것을 강제적으로 형변환을 할때 그 할당공간 크기 차이의 빈자리를 쓰레기값이 들어가 쓰레기 글자가 나오게 되는 것이다.

 그래서 자연스레 형변환을 해주는 접두어가 있었으니 그거슨 바로 L이다.
 라이토는 아니고, 음... 정확히 찾아보진 못했지만. L이다.

 사용방법은. 그냥 일반 접두어처럼 사용하면 된다.
 
LPCTSTR lpszClass=L"First";

요롷게 하면 디버깅도 되고 컴파일도 되고 실행도 된다. 제대로 출력도 된다.
하지만 이렇게 하면 사실 LPCTSTR을 쓴 의미가 없어진다. 왜냐하면 이 L은 유니코드형으로의 형변환이다. 선택적 형변환을 하기 위해선 TEXT()함수를 사용해야한다. 이 TEXT한수를 사용할 경우엔 이것도 T와 마찬가지로 알아서 유니코드면 유니코드, 일반 ANSI면 일반 char로 알아서 변환한다.

다시 말해

LPCTSTR lpszClass=TEXT("First");

가 최종 답안이라는거지 :)
아.. 여기까지 달려오시느라 참 수고 많으셨고(다 읽었을리는 없겠지만)
최종 코드를 공개하겠다.(거창하진 않지만)

아래 코드로 컴파일 하여 실행한다면 유니코드든 멀티바이트든 에러없이 실행 된다:)

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("First");

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)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc=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);
 
 //MessageBox(hWnd,L"하이",TEXT("Test"),NULL);

 while(GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 switch(iMessage)
 {
 case WM_DESTROY : PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}


+ Recent posts