質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Q&A

解決済

2回答

7934閲覧

MFCでEditにログを吐き出す処理でどちらが良いと思いますか?

kuma_dansyaku

総合スコア18

MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

0グッド

1クリップ

投稿2017/02/17 03:59

よろしくおねがいします。
MFCのエディットボックスにprintf(...)みたいな感じでログ表示をして欲しいと言われたのでこんな感じで作りました。
どっちが良いか(あるいはどっちも悪いか)を判定してください。

設定

OS:Windows7 pro 64bit
開発環境:VisualStudio 2005

プロジェクト環境: MFC > ダイアログベース
構成プロパティ: 文字セット > マルチバイト文字セットを使用する
リソースプロパティ: AutoVScroll > True

提案1

冗長な箇所は省略しています。

C++

1#define _LOG(a) { \ 2 printf("[%s:%d]", __FUNCTION__, __LINE__); \ 3 _thisPtr->__printedit a; \ 4 printf("\n"); \ 5 } 6#define CRLF "\r\n" 7 8static CsampleDlg *_thisPtr; // Dialog Class Pointer 9 10// 標準コンストラクタ 11CsampleDlg::CsampleDlg(CWnd* pParent /*=NULL*/) 12 : CDialog(CsampleDlg::IDD, pParent) 13{ 14 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 15 _thisPtr = this; 16} 17 18void CsampleDlg::OnBnClickedClog() 19{ 20 for(int i=0; i < 60 ; i++){ 21 _LOG(("test message output for LogMsg Editbox.")); 22 23 } 24 25} 26 27void CsampleDlg::__printedit(CHAR* format, ...) 28{ 29 /* 2バイト文字を使った場合の動作については保証しない */ 30 static unsigned int _LineCnt = 0; 31 32 unsigned int limit; // テキストの最大値 33 char szTextBuff[250] = {0}; 34 char tmpBuff[250] = {0}; 35 36 va_list list; 37 char *p; 38 int len; 39 40 // エディットボックスに表示できる最大バイト数を取得する 41 limit = m_EditLogMsg.GetLimitText(); 42 43 // 古いログを削除 44 if( _LineCnt > 500 ){ 45 // 一番上の行を""に置換 46 len = m_EditLogMsg.GetLine(0, szTextBuff, 250-1) + sizeof(CRLF); 47 m_EditLogMsg.SetSel( 0, len, FALSE ); 48 m_EditLogMsg.ReplaceSel("",0); /* 置換 */ 49 } 50 sprintf(szTextBuff, "%05d ", _LineCnt++); 51 52 va_start( list, format ); 53 54 for(len = (int)strlen(szTextBuff), p=format;*p; ++p){ /* formatの先頭から末尾まで */ 55 if(*p != '%' ){ /* フォーマット指定子に必ず付く % 以外なら、そのまま出力 */ 56 szTextBuff[len++] = (char)*p; 57 continue; 58 } 59 /* % だったら、その直後の文字に応じて出力 */ 60 switch( *(++p) ){ 61 case 'x': sprintf(tmpBuff, "%x", va_arg(list, int) ); break; 62 case 'd': sprintf(tmpBuff, "%d", va_arg(list, int) ); break; 63 case 'c': sprintf(tmpBuff, "%c", va_arg(list, char) ); break; 64 case 's': sprintf(tmpBuff, "%s", va_arg(list, char*) ); break; 65 default: sprintf(tmpBuff, "%c", *p ); break; 66 } 67 len += (int)strlen(tmpBuff); 68 strcat(szTextBuff, tmpBuff); 69 } 70 va_end( list ); 71 strcat(szTextBuff, CRLF); 72 73 // カーソルを最後に移動させる 74 m_EditLogMsg.SetSel( limit, limit, FALSE ); 75 m_EditLogMsg.ReplaceSel(szTextBuff, 0); 76}

提案2

C++

1#define _LOG(a) { \ 2 printf("[%s:%d]", __FUNCTION__, __LINE__); \ 3 _thisPtr->__printedit a; \ 4 printf("\n"); \ 5 } 6#define CRLF "\r\n" 7 8static CsampleDlg *_thisPtr; // Dialog Class Pointer 9 10// 標準コンストラクタ 11CsampleDlg::CsampleDlg(CWnd* pParent /*=NULL*/) 12 : CDialog(CsampleDlg::IDD, pParent) 13{ 14 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 15 _thisPtr = this; 16} 17 18void CsampleDlg::OnBnClickedClog() 19{ 20 for(int i=0; i < 60 ; i++){ 21 _LOG(("test message output for LogMsg Editbox.")); 22 23 } 24 25} 26 27void CsampleDlg::__printedit(CHAR* format, ...) 28{ 29 static unsigned int lcnt; 30 va_list list; 31 char szTextBuff[250]; 32 int limit,k,l,m; 33 34 // 行番号 35 sprintf(szTextBuff, "%05d ", lcnt++); 36 int len = (int)strlen(szTextBuff); 37 38 // 文字列のフォーマット 39 va_start(list, format); 40 vsprintf(szTextBuff+len, format, list); 41 strcat(szTextBuff, CRLF); 42 43 limit=m_EditLogMsg.GetLimitText(); // Editに表示できる最大バイト数 44 len = m_EditLogMsg.GetWindowTextLength(); // Editに表示されている文字数を取得します 45 46 //新しい文字列がログに追加できない時は 47 if(len+(k=strlen(szTextBuff))>limit){ 48 //古いログを削除します。 49 m_EditLogMsg.SetRedraw(FALSE); //再描画フラグをクリアします。 50 51 if((k=k-(limit-len))<0) k=0; //削除しなくてはならないバイト数を求めます。 52 m=m_EditLogMsg.LineFromChar(k); //削除しなくてはならないバイト数を含む行数を求めます。 53 l=m_EditLogMsg.LineIndex(m); //その行のインデックスを求めます。 54 if(l<k) l=m_EditLogMsg.LineIndex(m+1); //その行の先頭では削除しなくてはならないバイト数に満たない時、、、 55 //次の行のインデックスを求めます。 56 m_EditLogMsg.SetSel(0,l); //削除す範囲を選択します。 57 m_EditLogMsg.ReplaceSel(""); //選択した範囲を削除します。 58 59 len=m_EditLogMsg.GetWindowTextLength(); //現在のログの長さを取得します。 60 61 m_EditLogMsg.SetSel(len,len); //ログの最後を選択します。 62 m_EditLogMsg.ReplaceSel(szTextBuff); //文字列を追加します。 63 64 m_EditLogMsg.SetRedraw(); //再描画フラグをセットします。 65 }else{ 66 m_EditLogMsg.SetSel(len,len); //ログの最後を選択します。 67 m_EditLogMsg.ReplaceSel(szTextBuff); //文字列を追加します。 68 } 69}

あとエディットボックスに表示できる限界があるので古い行は削除していますが、表示しっぱなしに出来る方法はないでしょうか?
ご教授お願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ttyp03

2017/02/17 04:16

要点はどこですか?可変引数の展開方法ですか?ただソース見せられても…。
kuma_dansyaku

2017/02/20 07:24 編集

> 要点はどこですか?可変引数の展開方法ですか?ただソース見せられても…。 要点は__printedit()のリファクタです。 この方法で動作する事は確認しましたが、__printedit()の中身をもう少し綺麗 & 簡単に出来ないかな?と思い質問を投げてみました。
guest

回答2

0

MFCを使ってるのだからCStringのFormat関数を使えばいいのにと思うのですが。つまり、CHAR*で受けるのではなくconst CString&でフォーマット文を受け取ったらどうでしょうかということです。(CStringのFormat関数はprintfと構文がほとんど同じなのでそのまま使えると思います)

それと、これは前提を壊すような提案ですが、私がログデータを画面に表示する場合は、ListBoxをよく使います。ListBoxだと行単位なので、追加と削除が行単位になって簡単になります。その代わり、1ログ1行にしておかないと削除が難しくはなります。通常、ログ表示はその場で修正することはないのでListBoxで十分機能は果たせます。
コピーや部分的な切り取りがListBoxだと出来ない、ということは言われるかもしれませんが、ログ表示は最新の表示に更新されることが多いので、コピー選択がし難いことが多いですので(更新頻度にもよりますが)、それほど問題になることはないかと思います。

投稿2017/02/20 09:09

PineMatsu

総合スコア3579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kuma_dansyaku

2017/02/21 05:20

回答ありがとうございます。 代案2をリファクタし直しました。 これ以上は簡略化出来そうもないです。 ```C void CsampleDlg::__printedit(CHAR* format, ...) { static unsigned int lcnt; va_list list; char szTextBuff[250]; // 行番号 sprintf(szTextBuff, "%05d ", lcnt++); // 文字列のフォーマット va_start(list, format); vsprintf(&szTextBuff[7], format, list); va_end (list); strcat(szTextBuff, CRLF); int limit = m_EditLogMsg.GetLimitText(); // Editに表示できる最大文字数 int len = m_EditLogMsg.GetWindowTextLength(); // Editに表示されている文字数を取得します int k = strlen(szTextBuff); //新しい文字列がログに追加できない時は if((len+k)>limit){ int d, r, l; //古いログを削除します。 m_EditLogMsg.SetRedraw(FALSE); //再描画フラグをクリアします。 d=k-(limit-len); //削除しなくてはならないバイト数を求めます。 r=m_EditLogMsg.LineFromChar(k); //削除しなくてはならないバイト数を含む行数を求めます。 l=m_EditLogMsg.LineIndex(r); //その行のインデックスを求めます。 if(l<d){ l=m_EditLogMsg.LineIndex(r+1); //その行の先頭では削除しなくてはならないバイト数に満たない時、、次の行のインデックスを求めます。 } m_EditLogMsg.SetSel(0,l); //削除する範囲を選択します。 m_EditLogMsg.ReplaceSel(""); //選択した範囲を削除します。 len=m_EditLogMsg.GetWindowTextLength(); // Editに表示されている文字数を取得します。 m_EditLogMsg.SetRedraw(); //再描画フラグをセットします。 } m_EditLogMsg.SetSel(len,len); //ログの最後を選択します。 m_EditLogMsg.ReplaceSel(szTextBuff); //文字列を追加します。 } ``` あと代案1は可変長のログの場合だと止まる事があって使い物になりませんでした。
guest

0

ベストアンサー

案1と案2で中身が違い過ぎてるので何とも言えないのですが、現状どちらも同じ結果が得られているという前提且つ、「__printedit()の中身をもう少し綺麗 & 簡単に」ということなので、次のようにしてはいかがでしょうか。

案1をベースに可変引数の展開部分を案2のようにvsprintfを使うようにする。
これだけでスッキリするのではと思います。

投稿2017/02/20 07:33

ttyp03

総合スコア16996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kuma_dansyaku

2017/02/21 05:20

回答ありがとうございます。 確かにStringクラスが使えるのに何故char*を使ってたのかなぁ?と思って調べていたら そもそもCソースから呼び出す事を前提としていたのでこの形となっていました。 UI部分をMFC(C++)にして、実際に処理を行うAPIをCソースにしていたのでこの形となっています。 結局、CソースからMFC側の関数(__printedit)が呼び出せず、最終的にはMFCでコマンドプロンプトを別窓で開く方法を使ってprintfで出力していたのを思い出しました。 既存のログを変更すると面倒なのでこのままで行きます。 あとCListBox使った版も作ってみました。 こっちの方が楽ですね。 クリップボードにコピーとか考えなければ、メモリがあるだけ追加し続けられるこっちの方が良いかも。 ```C void CsampleDlg::__printedit(CHAR* format, ...) { va_list list; char szTextBuff[250]; // 行番号 sprintf(szTextBuff, "%05d ", m_list.GetCount()+1); // 文字列のフォーマット va_start(list, format); vsprintf(&szTextBuff[7], format, list); va_end (list); strcat(szTextBuff, CRLF); m_list.AddString(szTextBuff); // ログ表示 m_list.SetTopIndex(m_list.GetCount()-1); // 末尾移動 } ```
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問