UNICODEプログラミングのススメ
Visual C++を使う場合、Chicago系(Windows 95/98/98SE/Meのこと。以降Windows 9xとする)では、プロジェクトは_MBCSがデフォルトで定義されており、文字列に関してはMBCSで組むのが普通でした。
しかし、Windows 9xに対応しなくてもよい時代がもうすぐ到来します。実際、Windows 9xに対応しないソフトも店頭市場でも販売されてきています。
そこで、Windows 2000以降のWindows NT系のみの動作を考えると、UNICODEプログラミングの方が、圧倒的に問題点が少なくなります。 ※ Windows NT4もUNICODEプログラミングは可能ですが、ユーザーはほとんどいないので、対応を考えなくてもよい状態です。
例えば、MBCSでは認識できない文字列も、UNICODEでは認識できるようになります。
Webから保存したファイルの場合、ファイル名はTITLEタグにある名前がデフォルトですが、これはMBCSとは限らないのです。
言語が日本語だとしても、記号はUNICODE専用の可能性もあります。
このようなファイルをMBCSコンパイルしたプログラムから読み込もうとしても、パスを認識できません。
"?"に置換され、判別できなくなります。UNICODEにすれば、当然読み込むことができます。
Windows Vistaでは通常のフォントのバージョンが変更となり、日本語でも、UNICODEでないと読み込めない文字も多くなってきました。
他にも、"\"区切りのとき、"表"のような"\"をASCIIコードで含む文字列の認識もMBCSコンパイルでは失敗しますが、UNICODEコンパイルでは、問題ありません。
また、NT系は内部的に文字操作がUNICODEで動作しているため、UNICODEコンパイルした方が実行速度は速いです。
これだけではありません。ActiveXコントロール等は、デフォルトで、UNICODE文字列を使用しているものが多く、MBCSコンパイルでは、毎回変換が必要になってしまいます。
UNICODEコンパイルであれば、シームレスにプログラムすることが可能です。
UNICODEでないとバグを誘発するAPIもいくつもあります。
また、Windows CE系は元々UNICODEコンパイルでしたので、UNICODEでWindowsプログラムを行うと互換性を保てます。
以上のような理由から、9xに対応しない製品を作成する場合は、UNICODEプログラミングをお勧めいたします。
ただし、UNICODEプログラミングは注意する点がいくつかあります。
(1) 文字数とバイト数が異なるため、メモリ初期化やAPI呼び出し時の文字数指定を注意すること。
(2) プリプロセッサの定義で、_MBCSを消し、UNICODEを定義する。
(3) 文字列を使用するAPIはA形式ではなくW形式がdefineされます。LoadLibrary ()する場合もWの方を呼び出すようにする。
下記は、GetWindowText () APIについて、winuser.hで定義されているAPIの情報です。
■ winuser.h から抜粋
WINUSERAPI
int
WINAPI
GetWindowTextA(
__in HWND hWnd,
__out_ecount(nMaxCount) LPSTR lpString,
__in int nMaxCount);
WINUSERAPI
int
WINAPI
GetWindowTextW(
__in HWND hWnd,
__out_ecount(nMaxCount) LPWSTR lpString,
__in int nMaxCount);
#ifdef UNICODE
#define GetWindowText GetWindowTextW
#else
#define GetWindowText GetWindowTextA
#endif // !UNICODE
UNICODEを定義していないとき、GetWindowText () を呼び出すと、GetWindowTextA () がコールされます。
UNICODEを定義しているときは、GetWindowTextW () がコールされます。
(4) 文字列は、L"..."として記載する。
以下、サンプルコードになります。
「ファイルに関連する情報をHDDに保存するには(ファイルストリーム)」のサンプルをUNICODE版に書き換えたソースコードになります。
■■■ hardlink_unicode プロジェクト
■ main.cpp
// Windows 2000以降 ( CreateHardLink のため )
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <shlobj.h>
#include <string>
int WINAPI wWinMain ( HINSTANCE hThisInstance,
HINSTANCE,
LPTSTR,
int )
{
enum
{
MY_MAX_PATH = 2048
};
wchar_t szDesktopPath[ MY_MAX_PATH ];
SHGetSpecialFolderPath ( NULL, szDesktopPath, CSIDL_DESKTOPDIRECTORY, TRUE );
wchar_t szSrcPath[ MY_MAX_PATH ];
lstrcpy ( szSrcPath, szDesktopPath );
lstrcat ( szSrcPath, L"\\a.txt" );
wchar_t szDstPathBuf[ MY_MAX_PATH ];
lstrcpy ( szDstPathBuf, szDesktopPath );
lstrcat ( szDstPathBuf, L"\\a" );
// すでにファイルがあれば、ファイル名を変えて、10個まではハードリンクを作成する
// "a00.txt","a01.txt", ... ,"a09.txt"
UINT i = 0;
while ( 10 > i )
{
HANDLE hFindFile;
WIN32_FIND_DATA win32FindData;
std::basic_string < wchar_t > strPath ( szDstPathBuf );
wchar_t szNum[ 80 ];
wsprintf ( szNum, L"%02u", i );
strPath += szNum;
strPath += L".txt";
hFindFile = FindFirstFile ( strPath.c_str (), &win32FindData );
if ( INVALID_HANDLE_VALUE == hFindFile )
{
// ファイルがないので作成
if ( CreateHardLink ( strPath.c_str (), szSrcPath, NULL ) )
{
MessageBox ( NULL,
L"ハードリンクを作成しました",
L"確認",
MB_OK );
}
else
{
MessageBox ( NULL,
L"ハードリンクの作成に失敗しました",
L"エラー",
MB_OK | MB_ICONSTOP );
}
return 0;
}
else
{
FindClose ( hFindFile );
}
i++;
}
MessageBox ( NULL,
L"ハードリンクをこれ以上作成できません",
L"確認",
MB_OK | MB_ICONSTOP );
return 0;
}
|
もし、すぐMBCSから切り替えない場合は、UNICODEでもコンパイルできるようにコードを書いておくと後々ラクになります。
※ これは慣れなので、慣れれば開発速度はあまり落ちません。
方法としては下記の通りです。
(1) _TCHAR 型を使用する。
(2) 文字列は_TEXT ( "..." )として記載する。
(3) APIなどは、MBCS・UNICODEの両方で動作するように記載する。
たとえば、メモリ初期化するときは下記のように記載する。
enum
{
MY_MAX_BUFFER = 2048
};
_TCHAR sz[ MY_MAX_BUFFER ];
memset ( sz, 0, MY_MAX_BUFFER * sizeof ( _TCHAR ) );
Cの関数は、たいてい_tXXXX ()として定義されているので、そちらを使います。
たとえば、ファイルを開くには、下記のように記載する。
int nHandle;
nHandle = _topen ( _TEXT ( "c:\\sample.txt" ), .....
APIでは、A形式とW形式は #ifndef UNICODE ... #else ... #endif で切り替えてあるので、AやWを指定しないで呼び出す。
たとえば、ウィンドウの文字列を取得したいのであれば、GetWindowTextAでもGetWindowTextWでもなく、GetWindowText () を使用します。
(4) どうしてもMBCSとUNICODEでコードが異なるときは、#ifndef UNICODE ... #else ... #endif のように記載し、UNICODEと_MBCS両方わけてコードを記載する。
以下、「カスタムリソースを使用するには」のサンプルから抜粋しています。
_TCHAR *psz;
#ifndef UNICODE // _MBCS のとき
psz = new char[ lstrlen ( reinterpret_cast < char * > ( pRes ) ) + 1 ];
wsprintf ( psz, _TEXT ( "%s" ), pRes );
#else // _UNICODE のとき
UINT nSize;
nSize = mbstowcs ( NULL, reinterpret_cast < char * > ( pRes ), 0 );
psz = new wchar_t[ nSize + 1 ];
memset ( psz, 0, sizeof ( wchar_t ) * ( nSize + 1 ) );
mbstowcs ( psz, reinterpret_cast < char * > ( pRes ), nSize );
#endif
MessageBox ( NULL,
psz,
_TEXT ( "リソースの中身" ),
MB_OK );
delete[] psz; |