Programming Field - プログラミング Tips

RichEdit: Unicode 対応メッセージを使う

リッチ エディット コントロール/リッチ テキスト ボックス コントロール (以下 RichEdit) のバージョン 2 (RICHED20.DLL、ウィンドウクラス名: RichEdit20A、RichEdit20W)以降では、Unicode 文字のサポートが追加・強化されています。これにより、RichEdit20A を指定して作成した RichEdit でも、Unicode 文字をそのまま使うことが出来ます。

しかし GetWindowText 関数や EM_REPLACESEL メッセージなどは CreateWindowEx 関数の ANSI 文字版か Unicode 文字版かを呼び出したかで使用できる文字コードが決まってしまいます。そこで、このようなメッセージと同等の処理を(特に Windows 95/98/Me 上で) Unicode 文字を用いて直接行いたい場合は、以下の対応するメッセージ等を利用します。

EM_SETTEXTEX メッセージはバージョン 3 以降の RichEdit (Windows 2000 や Me 以降に標準で搭載、Windows Installer 2.0 のインストールで入手可能) で使用可能です。

EM_EXGETSELEM_EXSETSEL メッセージについては、文字位置の計算が Unicode で行われている可能性がありますが、まだ確認は出来ていません。代わりに ITextDocument インターフェイスの GetSelection メソッドを利用することも出来ます。これについては後述します。

以下では、各処理に使用できるメッセージ等について解説していきます。

RichEdit のテキスト全体を取得 - EM_GETTEXTEX

EM_GETTEXTEX メッセージは以下のように呼び出します。

[C/C++]

  GETTEXTEX gt;
  LPTSTR lpszBuffer;
  int nCopiedLen;
  nCopiedLen = PtrToInt(SendMessage(hWndRich, EM_GETTEXTEX,
      (WPARAM) &gt, (LPARAM) lpszBuffer));
wParam
GETTEXTEX 構造体のポインタを指定します。
lParam
テキストのコピー先となるバッファのポインタを指定します。バッファのサイズは GETTEXTEX で指定した分必要となります。

GETTEXTEX 構造体の定義は以下の通りです。

typedef struct _gettextex
{
    DWORD cb;
    DWORD flags;
    UINT codepage;
    LPCSTR lpDefaultChar;
    LPBOOL lpUsedDefChar;
} GETTEXTEX;
cb
EM_GETTEXTEXlParam で指定するバッファのサイズをバイト単位で指定します。
flags
以下の値を指定します。GT_SELECTION は他のフラグと組み合わせることが可能です。
名前 説明
GT_DEFAULT 0 改行文字を既定の CR で処理します。
GT_USECRLF 1 改行文字を CR-LF で処理します。
GT_SELECTION 2 現在選択されている文字を取得します。これを指定すると EM_GETSELTEXT の代替となります。
GT_RAWTEXT 4 生の RTF 文字列を取得します。(対応バージョン不明)
GT_NOHIDDENTEXT 8 「隠し文字」をコピーしないようにします。(対応バージョン不明)
codepage
取得する文字のコードページを指定します。CP_ACP (0) なら現在のロケールに基づいた ANSI 文字セット、1200 なら Unicode 文字セットを利用します。
lpDefaultChar, lpUsedDefChar
codepage 1200 でない場合は、文字列は WideCharToMultiByte 関数を使って Unicode から変換されます。この 2 つのメンバはその際に使用されます。詳しくは WideCharToMultiByte 関数を参照してください。

EM_GETTEXTEX ではコードページによってバッファにコピーされる文字列を変更できるので、codepage に 1200 を指定することによって、Unicode の文字を取得することが出来ます。

以上を利用した、RichEdit のテキスト全体を取得する方法は以下の通りになります。

    WCHAR szBuffer[256];
    GETTEXTEX gt;
    gt.cb = sizeof(WCHAR) * 256;
    gt.flags = GT_USECRLF;
    gt.codepage = 1200;
    gt.lpDefaultChar = NULL;
    gt.lpUsedDefChar = NULL;
    SendMessage(hWndRich, EM_GETTEXTEX, (WPARAM) &gt, (LPARAM) szBuffer);

RichEdit のテキスト全体の長さを取得 - EM_GETTEXTLENGTHEX

EM_GETTEXTLENGTHEX メッセージは以下のように呼び出します。

[C/C++]

  GETTEXTLENGTHEX gtl;
  int nLength;
  nLength = PtrToInt(SendMessage(hWndRich, EM_GETTEXTLENGTHEX,
      (WPARAM) &gtl, 0));
wParam
GETTEXTLENGTHEX 構造体のポインタを指定します。
lParam
使用しません。0 を指定してください。

GETTEXTLENGTHEX 構造体の定義は以下の通りです。

typedef struct _gettextlengthex
{
    DWORD flags;
    UINT codepage;
} GETTEXTLENGTHEX;
flags
以下の値を指定します。組み合わせられるフラグとそうでないフラグがあるので注意してください。
名前 説明
GTL_DEFAULT 0 改行文字を既定の CR で処理します。
GTL_USECRLF 1 改行文字を CR-LF で処理します。
GTL_PRECISE 2 正確な文字数を計算します。場合によっては変換が行われるため、処理が若干遅くなることがあります。GTL_CLOSE とは同時に使用できません。
GTL_CLOSE 4 実際の文字数に近い文字数を計算します。この場合処理が若干速くなるため、EM_GETTEXTEX と組み合わせて使用する際に、バッファのメモリを確保する上で有効となります。GTL_PRECISE とは同時に使用できません。
GTL_NUMCHARS 8 返される値が「文字数」になります。GTL_NUMBYTES とは同時に指定できません。
GTL_NUMBYTES 16 返される値が「バイト数」になります。GTL_NUMCHARS とは同時に指定できません。
codepage
取得する文字のコードページを指定します。CP_ACP (0) なら現在のロケールに基づいた ANSI 文字セット、1200 なら Unicode 文字セットを利用します。

EM_GETTEXTLENGTHEXEM_GETTEXTEX に似ています。例えば、以下のように呼び出す方法があります。

    WCHAR* pszBuffer;
    GETTEXTLENGTHEX gtl;
    GETTEXTEX gt;
    gtl.codepage = gt.codepage = 1200;
    gtl.flags = GTL_USECRLF | GTL_CLOSE | GTL_NUMBYTES;
    gt.cb = PtrToLong(SendMessage(hWndRich, EM_GETTEXTLENGTHEX, (WPARAM) &gtl, 0) + sizeof(WCHAR);
    pszBuffer = (WCHAR*) malloc((size_t) gt.cb);
    gt.flags = GT_USECRLF;
    gt.lpDefaultChar = NULL;
    gt.lpUsedDefChar = NULL;
    SendMessage(hWndRich, EM_GETTEXTEX, (WPARAM) &gt, (LPARAM) pszBuffer);
    ...
    free(pszBuffer);

RichEdit のテキスト全体を設定 - EM_STREAMIN

EM_STREAMIN はバージョン 1 からあるメッセージなので、それ自体の説明は省略しますが、wParam に指定できるフラグに SF_UNICODE (= 0x0010) が追加されたため、これを用いて選択範囲のテキストを設定することが出来ます。

なお、EM_GETTEXTEX と対になっている EM_SETTEXTEX はバージョン 3 で登場するメッセージのため、バージョン 2 と互換性を持たせるためには EM_STREAMIN を使用しなければなりません。

コード例は選択されているテキストの設定方法と同じなので、そちらをご覧ください。

RichEdit の選択されているテキストを取得 - EM_GETTEXTEX

上述の EM_GETTEXTEX で、GETTEXTEX 構造体の flags に GT_SELECTION を指定することによって、選択範囲のテキストを取得することが出来ます。コード例は以下の通りです。

    WCHAR szBuffer[256];
    GETTEXTEX gt;
    gt.cb = sizeof(WCHAR) * 256;
    gt.flags = GT_USECRLF | GT_SELECTION;
    gt.codepage = 1200;
    gt.lpDefaultChar = NULL;
    gt.lpUsedDefChar = NULL;
    SendMessage(hWndRich, EM_GETTEXTEX, (WPARAM) &gt, (LPARAM) szBuffer);

RichEdit の選択されているテキストを設定 - EM_STREAMIN

EM_STREAMIN (EM_STREAMOUT も) には SFF_SELECTION (= 0x8000) フラグがあり、これを利用して選択されているテキストを置き換えることが出来ます。これと上述の SF_UNICODE を組み合わせることによって、現在選択されている RichEdit のテキストを Unicode 文字で直接置き換えることが出来ます。

なお、SFF_SELECTION を指定した EM_STREAMIN の動作は、WM_UNDO などで「元に戻す」ことができます。

以下のコードは、RichEdit のテキスト全体を変更する関数と、選択されているテキストのみを変更する関数です。このコードをそのまま使用することで、簡単にそれぞれの動作を行うことが出来ます。

[C/C++]

struct CTextStreamBuffer
{
    LPCWSTR lpszPos;
    DWORD dwLeftLen;
};

以下の2つで利用するコールバック関数
DWORD CALLBACK EditStreamCallBackForSetText(CTextStreamBuffer* pData,
    LPBYTE pBuffer, LONG cb, LONG* pcb)
{
    if (!pData->dwLeftLen)
        *pcb = 0;
    else
    {
        // cb, pcb はバイト単位で計算
        cb /= sizeof(WCHAR);
        DWORD dw = min(pData->dwLeftLen, (DWORD) cb);
        memcpy(pBuffer, pData->lpszPos, (size_t) (sizeof(WCHAR) * dw));
        pData->dwLeftLen -= dw;
        pData->lpszPos += dw;
        *pcb = (LONG) dw * sizeof(WCHAR);
    }
    return 0;
}

// テキスト全体を変更する関数
void SetTextToRichEditW(HWND hWndRich, LPCWSTR lpszString)
{
    CTextStreamBuffer tsb;
    EDITSTREAM es;
    tsb.lpszPos = lpszString;
    tsb.dwLeftLen = (DWORD) wcslen(lpszString);
    es.dwCookie = (DWORD_PTR) &tsb;
    es.dwError = 0;
    es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallBackForSetText;
    SendMessage(hWndRich, EM_STREAMIN,
        (WPARAM) IntToPtr(SF_TEXT | SF_UNICODE), (LPARAM) &es);
}

// 選択されているテキストを変更する関数
void ReplaceSelOfRichEditW(HWND hWndRich, LPCWSTR lpszString)
{
    CTextStreamBuffer tsb;
    EDITSTREAM es;
    tsb.lpszPos = lpszString;
    tsb.dwLeftLen = (DWORD) wcslen(lpszString);
    es.dwCookie = (DWORD_PTR) &tsb;
    es.dwError = 0;
    es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallBackForSetText;
    SendMessage(hWndRich, EM_STREAMIN,
        (WPARAM) IntToPtr(SF_TEXT | SF_UNICODE | SFF_SELECTION), (LPARAM) &es);
}

RichEdit のテキストを検索 - EM_FINDTEXTW、EM_FINDTEXTEXW

EM_FINDTEXTWEM_FINDTEXTEXW の各メッセージは、名前を見て分かる通り、それぞれ EM_FINDTEXTEM_FINDTEXTEX の Unicode 版となっています。引数や構造体の内容などは型を除いてすべて一致するので、説明は省略します。

ちなみに、戻り値はバイト単位ではなく文字単位になっています。

補足: ITextDocument::GetSelection の利用

RichEdit のバージョン 2 以降では Text Object Model (TOM) と呼ばれる機能があり、COM の雰囲気で RichEdit を操作することが出来ます。

ITextDocument インターフェイスの取得

ITextDocument インターフェイスは、TOM のルートとなるインターフェイスで、以下のように取得します。

[C++]

#define DEFINE_MYGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
    EXTERN_C const GUID name \
        = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
DEFINE_MYGUID(IID_ITextDocument,0x8CC497C0,0xA1DF,0x11CE,0x80,0x98,0x00,0xAA,0x00,0x47,0xBE,0x5D);

    IRichEditOle* pRichOle;
    ITextDocument* pTextDoc;
    if (SendMessage(hWndRich, EM_GETOLEINTERFACE, 0, (LPARAM) &pRichOle))
    {
        if (SUCCEEDED(pRichOle->QueryInterface(IID_ITextDocument, (void**) &pTextDoc)))
        {
            // ここで pTextDoc を使用する
            pTextDoc->Release();
        }
        pRichOle->Release();
    }

ITextDocument::GetSelection

ITextDocument::GetSelection を呼び出すと、選択されているテキストに関するメソッドを利用できる ITextSelection インターフェイスのポインタを取得できます。

[C++]

    ITextSelection* pSelection;
    pTextDoc->GetSelection(&pSelection);

選択されている位置とテキストを取得・設定する

ITextSelection を使ったコードは以下の通りです。なお、ここで使用しているメソッドはほとんどが ITextSelection の親インターフェイスである ITextRange インターフェイスのものです。

[C++]

    BSTR bstr;
    long nStart, nEnd;

    // 選択範囲の開始と末尾の点を取得
    pSelection->GetStart(&nStart);
    pSelection->GetEnd(&nEnd);

    // 選択範囲のテキストを取得
    pSelection->GetText(&bstr);
    SysFreeString(bstr);

    // 選択範囲の開始と末尾の点を設定
    pSelection->SetRange(nStart, nEnd);

    // 選択範囲のテキストを設定
    bstr = SysAllocString(L"テキスト");
    pSelection->SetText(bstr);
    SysFreeString(bstr);

最終更新日: 2007/02/17