이미 만들어져 있는 프로젝트를 UNICODE로 전환한다는 것은... 어려운 작업까지는 아니지만 상당한 노가다와 시간을 필요로 하는 일임에는 틀림 없다. 약간의 요령과 정형화된 절차가 있다면 조금이나마 시간과 노력을 절약할 수 있다.

다음은 MBCS로 쓰여진 코드를 UNICODE로 변환하는 순서를 설명한다.
(여기서 말하는 UNICODE란 Generic T-TYPE이다. W-Type 아님... ^^)


1. UNICODE 매크로 선언
Visual Studio (6.0) 실행 -> Project -> C++ 탭에서 Preprocessor definitions: 항목에 _MBCS를 삭제하고 _UNICODE와 UNICODE를 추가

사용자 삽입 이미지


2. UNICODE가 불가한 부분이 있는지 검토
코드 전체를 검토하여 UNICODE로 전환할 수 없는 부분이 있는지 체크한다. 예를 들면 유니코드가 아닌 외부모듈을 사용하는 부분이라던지... 통신 프로토콜 문제로 전환이 불가한 부분, GetProcAddress 와 같이 T-TYPE을 지원하지 않는 API를 호출하는 부분 등이 되겠다. 이런 부분은 억지로 유니코드화 하는 것보다 차라리 ANSI 코드를 내버려 두고 결과 스트링을 UNICODE로 한번 더 변환해서 쓰는게 여러 모로 낫다.
다음의 API를 사용한다.
int WideCharToMultiByte( UINT CodePage, // code page
                     DWORD
dwFlags, // performance and mapping flags
                          LPCWSTR lpWideCharStr, // wide-character string          
                     int cchWideChar, // number of chars in string.
                     LPSTR lpMultiByteStr, // buffer for new string          
                     int cbMultiByte, // size of buffer              
                     LPCSTR lpDefaultChar, // default for unmappable chars
                     LPBOOL lpUsedDefaultChar // set when default char used
);
int MultiByteToWideChar( UINT CodePage, // code page
                          DWORD dwFlags, // character-type options         
                          LPCSTR lpMultiByteStr, // string to map
                          int cbMultiByte, // number of bytes in string
                          LPWSTR lpWideCharStr, // wide-character buffer
                          int cchWideChar // size of buffer
);
제대로 Generic하게 Conversion하려면, UNICODE 전환 불가 부분에 대해 다음과 같이 해줘야 한다.
(... blah~ blah~...)
#ifndef _UNICODE
        lstrcpyn(szBuffer1, szBuffer, _countof(szBuffer1));
#else
        MultiByteToWideChar(CP_ACP, FALSE, szBuffer, nLen, szBuffer1, _countof(szBuffer1)-1);
        szBuffer1[_countof(szBuffer1) - 1] = NULL;
#endif
(... blah~ blah~...)


3. Quoted String 전환
다음과 같이 Quoted String을 전환한다.
"TEXT DATA" --> _T("TEXT DATA")
'C' --> _T('C')

           한꺼번에 전환하려면 다음과 같이 한다. 
   1) Visual Studio에서 ctrl + H 를 눌러 변환 창을 띄운다.
   2) "Regular String"에 체크를 한다.
   3) Find What 에는 Quoted String을 의미하는 "\:q" 를 입력한다.
       (혹은 우측 [▶]를 클릭해 "Quoted String"을 선택하면 입력된다.)
   4) Replace With: 에는 _T(\0) 를 입력한다. (\0 는 발견된 스트링을 의미한다.)
   5) 찾아지는 Quoted String을 확인하면서 Replace 를 시작한다.
       "Replace All"을 해버리고 나서 에러나는 부분을 수정해나가는 방법도 괜찮다.
       (보통 #include 등에서 에러가 난다.)
   6) 소스코드 내의 모든 파일에 대해 1~5를 반복한다. 이 때 위에서 확인된 변환불가 부분은 제외한다.
   7) 에러없이 빌드가 되는지 확인한다.

※ #include "HeaderFile.h" 같은 부분이 #include _T("HeaderFile.h") 로 잘못 전환되지 않도록 조심. ^^;


사용자 삽입 이미지



4. Data Type과 문자열 함수를 T-Type 버젼으로 전환
Data Type과 문자열 함수를 T-Type 버젼으로 전환한다.
이때 API는 100% 대체가 가능한 API로 변환해야 하므로 주의하여 MSDN을 참고하여 진행해야 한다. 예를 들어 strncpy는 _tcsncpy 로 전환해야지 lstrcpyn 으로 전환해서는 안된다. (동작이 미묘하게 다르다. 자세한 내용은 여기 참조) 

      1) Data Type을 전환한다. 
    몇가지 예를 들면 다음과 같다.       
    다음의 항목에 대해서는 울트라에디트 등 텍스트 에디터의 Replace in Files 기능으로 한꺼번에 작업해도 된다. ("완전한 단어" 옵션에 체크하면 실수를 줄일 수 있다.)
"LPSTR" -> "LPTSTR"
"LPCSTR" -> "LPCTSTR"
"PSTR" -> "PTSTR"
"PCSTR" -> "PCTSTR"
    다음의 타입들은 VisualStudio의 Replace기능으로 하나하나 확인하며 파일별로 전환해야 한다.
"CHAR" -> "TCHAR"
"char" -> "TCHAR"

※ 소켓 통신이나 파일 I/O 등에서 CHAR Array를 버퍼로 사용하는 경우는 TCHAR로 전환해선 안된다. (이럴 땐 CHAR Array보다 BYTE array를 쓰는 게 맞다.)
※ 혹시 "unsigned char" 와 같이 쓰여진 코드를 전환하면 이상하게 되므로.. 조심. ㅡ.ㅡ

     2) 문자열 핸들링 함수들을 전환한다.   
문자열 핸들링 함수들이 무지 많아서 다 적을 수는 없고... 대표적인 것 몇가지만 적으면 다음과 같다. 다음의 API들은 Replace in Files로 한꺼번에 작업해도 된다.
"strcpy" -> "_tcscpy"
"strncpy" -> "_tcsncpy"
"strcat" -> "_tcscat"
"strncat" -> "_tcsncat"
"strcmp" -> "_tcscmp"
"strncmp" -> "_tcsncmp"
"strstr" -> "_tcsstr"
"strchr" -> "_tcsstr"
(기타 등등 strXXX 류의 함수들은 대부분 _tcsXXX 로 전환하면 된다.)

"wsprintf" -> "_stprintf"
"_snprintf" -> "_sntprintf"
"printf" -> "_tprintf"
(기타 등등.. 무지 많다.)

※ memset, memcpy 등은 문자열 함수가 아닌 메모리 함수이므로 건드리면 안된다.
※ lstrcpyn 계열의 함수들은 원래 T-Type을 지원하므로 건드릴 필요 없다.
※ 위의 함수들은 인자의 종류와 순서가 완전히 동일하므로 함수 이름만 Replace하면 유니코드로 전환이 된다. 만약 strcpy와 같이 버퍼체크를 안하는 함수를 버퍼체크 하는 버젼으로 변경하고 싶다면... 먼저 _tcscpy로 변환하여 유니코드로 컨버젼을 끝낸 다음에 _tcscpy_s (혹은 _tcsncpy) 로 다시한번 전환하는 것이 낫다.

   3) 문자열 핸들링 함수의 버퍼 사이즈에 sizeof가 사용된 곳이 없는지 체크하고, 있다면 _countof로 변환
문자열 함수에서 버퍼사이즈는 버퍼의 바이트수가 아닌 버퍼의 글자수를 지정해야 하며, memcpy 등 메모리 함수는 글자수가 아닌 바이트수를 지정해야 한다. MBCS일 때는 1글자가 1바이트이므로 sizeof가 문제가 안되지만, 유니코드일 때는 지켜주어야 한다.
VisualStudio 6.0의 경우 _countof를 지원하지 않으므로 다음과 같이 Define해서 사용하면 된다.
#ifndef _countof
#define _countof(X) sizeof(X)/sizeof(X[0])
#endif
_tcsncpy (szBuffer, lpSource, sizeof(szBuffer));  // Wrong!!
_tcsncpy (szBuffer, lpSource, _countof(szBuffer));  // OK!!

memcpy (lpBuffer, lpSource, sizeof(szBuffer)); // OK!
memset (lpBuffer, NULL, _countof(szBuffer)); // Wrong!! _countof가 아닌 sizeof 를 사용~!!



5. 기능테스트를 진행하면서 오류나는 부분을 체크하고 수정
모.. 사람마다 생각이 틀리겠지만... 코드 전환을 꼼꼼히 해봐도 어차피 컨버젼하고 나면 결함투성이고 에러를 다 잡아야 한다. 내생각엔 차라리 코드 전환을 대충(?) 해놓고, 테스트를 꼼꼼하게 하는게 낫지 않을까 싶다. ㅎㅎ


※ 모 아시겠지만... 위의 내용에 대해 책임지지 않습니다. 이 내용을 무단으로 사용하여 문제가 발생할 경우 즉시 뼈와 살이 분리됩니다.. ㅎㅎ




Posted by kuaaan
,


사랑합니다. 편안히 잠드소서