RichTextBox(リッチエディットコントロール)のサンプルコードです。
全文を貼ると長くなるのでソースはリンク先を参照してください。
サンプルを起動すると下図のテキストが表示されます。
ツールバーには以下のボタンがあり、それぞれRichTextBoxクラスのメソッドに対応しています。
- Bold: 太字にする。
- UnderLine: 下線を引く。
- Font: フォントを設定する。
- BaseUp: ベースラインを上げる。
- BaseDown: ベースラインを下げる。
- F.Color: 文字色を変更する。
- B.Color: 背景色を変更する。
- ^X: 上付き文字にする。
- _X: 下付き文字にする。
- GetText: テキストを取得してメッセージボックスで表示する。
- InsText: [Insert Text] というテキストをカーソル位置に挿入する。
- GetRtf: 選択範囲のRTFデータを取得してメッセージボックスで表示する。
- GetSelNum: 選択範囲の文字数を取得してメッセージボックスで表示する。
- SelNum(5): カーソル位置から5文字分を範囲選択する。
テキストを範囲選択後、ツールバーのボタンを使って色々な書式を設定したものが冒頭の画像になります。 メッセージボックスは、全文選択してからGetRtfボタンを押したときのものです。
構造体のアラインメントの問題
1か月悩んだことを書いておきます。
バグったときに構造体のアラインメントを気にしないといけないのは辛い・・・。
1. リンク修飾の範囲が正しく得られない
リッチエディットコントロールの標準機能により、URLの自動検出機能を有効にすると画像のようにURL部分がリンク修飾されます。 ここをクリックするとEN_LINK通知コードが発行されるので、これをウィンドウプロシージャで捕まえるとリンク部分のテキストが得られます。
しかし、本来ならば、ENLINK構造体のメンバのCHARRANGE構造体のメンバであるcpMinとcpMaxで範囲が得られるはずなのですが、なぜか正しい範囲が得られません。 どうしても解決できないのでVisualStudioを使いC++でEN_LINK通知コードを捕まえる処理を書いて挙動を確認してみると、こっちではちゃんと正しい範囲が得られました。
そこで、C++とDのEN_LINK構造体のサイズを比較してみると、C++では52バイト、Dでは56バイトでした。
あのう・・・ENLINK構造体のサイズが違うのはなんで???
— はるえす@イカのフレンズ (@Rayerd) 2023年5月5日
左 MSVC、右 DMD
なおどちらもx64ビルド#dlang pic.twitter.com/P0X2oewZGR
WPARAMやLPARAMを別の型にキャストして使うのは、Windowsプログラミングではよく出てくる形ですが、DでLIBやDLLを使う場面では、構造体サイズが異なることによってまともに動かないことがあるようです。
下図のように、ENLINK構造体にalign(1):を追記することで構造体サイズがC++と一致し、バグは解消しました。
2. EM_STREAMOUT / EM_STREAMIN メッセージを送ると例外で落ちる
リッチエディットコントロールからRTFを取得する方法はいくつかあるようです。 DFLでは元々EM_STREAMOUTメッセージを使う方法が使われていました。 しかし、動作確認をしてみると全然動いておらず、実行時に謎の例外で落ちます。
- 猫でもわかるプログラミング - 第133章 RTFを保存する
こちらも色々と原因を探っていましたが、C++とDでEDITSTREAM構造体のサイズを比較すると異なっており、前述と同じアラインメントの問題でした。
下図のように、EDITSTREAM構造体にalign(1):を追記することで構造体サイズがC++と一致し、バグは解消しました。
変更点
今回のRichTextBoxの改修に伴い、読み込むリッチエディットコントロールのバージョンが3.0以下(Riched20.dll)から4.1以上(Msftedit.dll)に変更されています。