リッチエディットの特定箇所の文字にすばやく色をつけたい

解決


紅輝  2008-12-19 06:53:34  No: 69279

C++  2005  MFCでのプログラムです。
リッチエディットコントロールに英語をカタカタと入力しています。入力文字は黒色です。
少し遅れて最初の文字から順に0.5秒毎程度で順次文字色を青色に変えています。
m_rich.SetSel(i, i+1); //iは0から文字列の最後の数まで増加させます。
re = m_rich.SetSelectionCharFormat(cf[1]);// 青色 cf[1]  黒色  cf[0]
をCHARFORMAT2であらかじめ設定しています。
len = m_rich.GetWindowTextLengthW();
re = m_rich.SetSel(len,len);
re = m_rich.SetSelectionCharFormat(cf[0]);// 黒色に戻す。

このプログラムでは問題が発生しています。
1)SetSelで青色に変える文字の範囲確定している瞬間に、キー入力すると本来は文字列の最後に入るべき文字が、範囲確定させた所に入ってしまいます。
2)SetSelectionが遅いのか、極まれにキー入力と重なるとRuntime エラーが出る。
これらの問題を回避する方法をお教えいただければ幸いです。


紅輝  2008-12-19 06:56:54  No: 69280

m_rich.SetSel(len, len);
でした。  re = は不要です。書き間違いでした。


gak  2008-12-20 01:57:15  No: 69281

> 1)SetSelで青色に変える文字の範囲確定している瞬間に、キー入力すると本来は
>     文字列の最後に入るべき文字が、範囲確定させた所に入ってしまいます。
> 2)SetSelectionが遅いのか、極まれにキー入力と重なるとRuntime エラーが出る。
複数スレッドから m_rich にアクセスしているにも関わらず、スレッド間排他制御を
行っていないため起きている症状と思われる。

> 少し遅れて最初の文字から順に0.5秒毎程度で順次文字色を青色に変えています。
コレどうやって実装している?マルチメディアタイマ(timeSetEvent)使ってる?
もしそうなら、timeSetEvent で指定した処理から共有リソースにアクセスしてる場合は
排他制御してやる必要がある。timeSetEvent は独自スレッドで処理を実行するので。

以下マルチメディアタイマを使っているという前提での回答。

> これらの問題を回避する方法をお教えいただければ幸いです。
・きちんと(メッセージ処理も含め完璧に)排他制御してやる
・マルチメディアタイマを使わずに WM_TIMER を使う

目的が「少し遅れて...順次文字色を青色に変えています」なら、それほど厳密に
「0.5秒毎」を守ってやる必要はなさそうな感じだよね。
具体例を挙げると、「キー入力が激しく為されている場合は、キー入力を優先して
青色変更はキー入力が落ち着いてからでも問題無し」て感じの仕様。
んで、もし↑通りなら WM_TIMER の方が適当だと思われる。
WM_TIMER は優先度が低いメッセージなので忙しい時は発生しない(後回しされる)し、
基本、m_rich が所属するスレッドで処理されるので排他制御も必要ない。


紅輝  2008-12-20 06:30:57  No: 69282

gakさん、詳しく有難うございます。  今日は、出かけており、返事が遅くなり申し訳ありません。
ところで、0.5秒程度と書きましたが、タイマーは正確でなければならない所に使っていますので、TimeSetEventを使って30から700ミリセカンドのタイミング制御をさせています。  タイマーは変えたくありませんが、
青色に変化させるのは、大まかで好いので、キー入力があって、リッチエディットにアクセスする間の短時間は、一回青色に変えるのを休んで、次のときに
m_rich.SetSel(i, i + 2)とするのは可能です。
例えば、リッチエディットにキー入力が有ったときは
::OnEnChangeRichedit21()の関数で処理させていますので、
これが呼ばれたときに何かのフラグAをTRUEに設定し、この関数から抜ける前にフラグAをFALSEにしておけば、フラグがFALSEの時だけ青色を書かせる様にすると、うまく行きそうですね。
キー入力が先に有ったときは好いのですが、
逆に青色に変えている時に、キー入力が有った場合はどうすれば好いのでしょうか?    青色に変えている間は、フラグBをTRUEにして、青色に変え終わればFALSEにしておけば、::OnEnChangeRichedit21()が呼ばれても何もせずにすぐにreturnさせれば好いのでしょうか?
でも、キー入力した文字はリッチエディットに書き込まれるでしょうね。
リッチエディットへのキー入力そのものを無視する方法はありますか?
以上。よろしくお願いします。


gak  2008-12-22 21:30:35  No: 69283

> タイマーは正確でなければならない所に使っていますので
状況を把握できてないんで何ともいえんが、「青色に変化」する処理専用のタイマを
作成すれば良いと思うのだが…
タイマは複数同時利用できるし、マルチメディアタイマと WM_TIMER の併用も問題ないはず。

↓「どうしてもタイマーは変えたくありません」という場合の回答。

> 逆に青色に変えている時に、キー入力が有った場合はどうすれば好いのでしょうか?
青色に変えている処理が完了するまで待機すれば良い。
で、それを実現するには先に述べた「排他制御」を行えば良い。
http://wisdom.sakura.ne.jp/system/winapi/win32/index.html
の 138〜141 辺りの記事に情報・サンプルが載ってそうなので参考にすれば良さげ。

質問解決の参考となるように今回の要点を纏めとく
→ 「複数スレッドから同一リソースに同時アクセスしてはいけない」というルールが存在する
→ TimeSetEvent() で指定したコールバック関数は独自スレッドで実行される
→ 現在は排他制御していないので「同一リソースに同時アクセス」する状況が発生し得る
→ ルールに違反するため Runtime エラーが出たりしてる
リッチエディット云々よりは、マルチスレッドへの対応不足が症状発生に繋がっている感じ。


紅輝  2008-12-23 07:44:34  No: 69284

gakさん、ご教授頂き有難うございました。  リッチエディットは読み書きが遅いようなので、並列処理している箇所を大幅に書き換えてメモリー処理を行うことで、1スレッドにしたほどすっきりしたプログラムになりました。  エラーも無くなった位に改善されましたが、キー入力された瞬間の処理にグローバル変数を使いましたので、同時に参照が行われる可能性が残ります。(全くこれには気が付きませんでした。)
青色変更中にはCreticalSection()を使うことを勉強してみます。または、キー入力された場合の処理は単純にグローバル変数2個に書き込んでいるだけですから、こちら優先でもテストして見ます。  
Many Thanks


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加