none
ツールバーのアイコンにカーソルが乗ったときのステータスバー表示について RRS feed

  • 質問

  • 初めて投稿します。宜しくお願いします。

    VC++ 6.0
    MFCでダイアログベースで開発をしております。OSはXPです。

    現在メインフレームのツールバーには2つのアイコンが実装されており、
    アイコンにカーソルが乗るとそれぞれステータスバーに、
    リソースビューのツールバーボタンプロパティ-プロンプトで設定した
    固定のメッセージが表示されています。

    ここに3つめのアイコンを追加しました。
    この3つ目のアイコンにカーソルが乗ったときの
    ステータスバーに表示させるメッセージ実装方法を教えて下さい。

    3つ目のアイコンがポイントされた時に、表示させるメッセージは
    固定のメッセージではなく、
    例えば『現在時刻:13:00』のように値が変化してくようなメッセージを
    表示させたいのです。

    色々調べてみましたが、実装できず困っています。
    VC++初心者で若輩ものですがご教授の程宜しくお願いします。
    2009年7月1日 13:56

回答

  • 3つ目のアイコンがポイントされた時に、表示させるメッセージは
    固定のメッセージではなく、
    例えば『現在時刻:13:00』のように値が変化してくようなメッセージを
    表示させたいのです。

    CFrameWnd::OnSetMessageString()を以下のようにオーバーライドしてください。

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
        …
        ON_MESSAGE(WM_SETMESSAGESTRING, OnSetMessageString)
    END_MESSAGE_MAP()
    
    LRESULT CMainFrame::OnSetMessageString(WPARAM wParam, LPARAM lParam)
    {
        UINT nID = (UINT)wParam;
        if (nID == 3番目のボタンのID){
            wParam = 0;
            lParam = (LPARAM)_T("表示したい文字列");
        }
        return CFrameWnd::OnSetMessageString(wParam, lParam);
    }
    

    このとき渡す文字列はOnSetMessageString()を抜けた後も有効でなければなりませんので、
    OnSetMessageString()内のローカル変数CStringなどを渡してはいけません。

    • 回答の候補に設定 ミッヒー 2009年7月8日 2:21
    • 回答としてマーク sk7474 2009年7月23日 7:39
    2009年7月2日 0:54
  • このとき渡す文字列はOnSetMessageString()を抜けた後も有効でなければなりませんので、
    OnSetMessageString()内のローカル変数CStringなどを渡してはいけません。

    すみません。これはウソでした。
    CFrameWnd::OnSetMessageString()で、pMessageBar->SetWindowText(lpsz); とするだけなので、
    ローカル変数でもかまいません。
    • 回答としてマーク sk7474 2009年7月23日 7:39
    2009年7月2日 1:46
  • すいません。横から割り込んだ者です。

    上で afxpriv.h について触れていますが、WM_SETMESSAGESTRING というのは、Windows 定義のウィンドウメッセージではなく、MFC 内部で使われているウィンドウメッセージの事です。

    独自のウィンドウメッセージを定義して、ON_MESSAGE マクロで独自のメッセージマップ関数を作った経験はありませんか?

    独自のウィンドウメッセージなので、他のアプリケーションでは意味を持たないメッセージなのですが、MFC も内部で MFC 独自のメッセージを定義しています。

    それが MFC プライベートメッセージと呼ばれるウィンドウメッセージです。
    このウィンドウメッセージは MFC 独自のものであり、正式にはドキュメント化されていないメッセージです。WM_SETMESSAGESTRING もその一つなので、

    'WM_SETMESSAGESTRING' : 定義されていない識別子です。
    というエラーメッセージが出るわけです。

    エラーメッセージの出るヘッダ、よりは.cpp の方がいいと思いますが、

    #include <afxpriv.h>

    によって、MFC プライベートメッセージを含む、MFC 内部定義値等がインクルードされ、エラーは直るはずです。
    • 編集済み ミッヒー 2009年7月2日 18:41 追記&インクルード場所補足
    • 回答としてマーク sk7474 2009年7月23日 7:39
    2009年7月2日 17:50

すべての返信

  • ダイアログの場合ではなく、VC++ 6.0 MFC の通常のウィンドウで CBRS_FLYBY フラグ付きのツールバーの話ですが・・・。

    確か、MFC の中を転々として CControlBar::SetStatusText(int nHit) に行き着いたと思います。
    この関数は仮想関数になっているので (CToolBar クラスはオーバーライドしていない) 、MFC のソースを参考にオーバーライド、となるのではないでしょうか。
    • 編集済み ミッヒー 2009年7月1日 17:20 開発ツール情報追記
    2009年7月1日 15:11
  • ※修正:ダイアログではなくフォームビューでした。失礼しました。
    2009年7月1日 17:55
  • 早速の回答ありがとうございます。
    CControlBar::SetStatusTextをキーワードに調べてみます。
    2009年7月1日 17:56
  • 3つ目のアイコンがポイントされた時に、表示させるメッセージは
    固定のメッセージではなく、
    例えば『現在時刻:13:00』のように値が変化してくようなメッセージを
    表示させたいのです。

    CFrameWnd::OnSetMessageString()を以下のようにオーバーライドしてください。

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
        …
        ON_MESSAGE(WM_SETMESSAGESTRING, OnSetMessageString)
    END_MESSAGE_MAP()
    
    LRESULT CMainFrame::OnSetMessageString(WPARAM wParam, LPARAM lParam)
    {
        UINT nID = (UINT)wParam;
        if (nID == 3番目のボタンのID){
            wParam = 0;
            lParam = (LPARAM)_T("表示したい文字列");
        }
        return CFrameWnd::OnSetMessageString(wParam, lParam);
    }
    

    このとき渡す文字列はOnSetMessageString()を抜けた後も有効でなければなりませんので、
    OnSetMessageString()内のローカル変数CStringなどを渡してはいけません。

    • 回答の候補に設定 ミッヒー 2009年7月8日 2:21
    • 回答としてマーク sk7474 2009年7月23日 7:39
    2009年7月2日 0:54
  • このとき渡す文字列はOnSetMessageString()を抜けた後も有効でなければなりませんので、
    OnSetMessageString()内のローカル変数CStringなどを渡してはいけません。

    すみません。これはウソでした。
    CFrameWnd::OnSetMessageString()で、pMessageBar->SetWindowText(lpsz); とするだけなので、
    ローカル変数でもかまいません。
    • 回答としてマーク sk7474 2009年7月23日 7:39
    2009年7月2日 1:46
  • 横からお邪魔してすいませんが・・・。

    Atsushi777 さん、お世話になっております。私の回答より遥かに的を得た、スマートな回答ですね。私も頑張って、質問者の方に判りやすい回答が出来るよう、励みたいと思います。

    それで、WM_SETMESSAGESTRING ですが、afxpriv.h で定義される MFC プライベートメッセージですよね。どのバージョンだったか忘れましたが、「以降のリリースでは afxpriv.h は MFC に組み込まれます」という感じのアナウンスがあったと記憶していますが、共有 DLL で MFC を使う場合、もう他のウィンドウメッセージと衝突しておかしな事になる、という心配はなくなっているのでしょうか?

    つまり、OS 側の MSVC???.dll で問題なく動くのでしょうか?


    なお、

    UNICODE とマルチメディアファイルに関して

    の半ばパニくった質問に回答して頂き、本当にありがとうございました。

    2009年7月2日 1:50
  • すいません。またやってしまいました。
    慎重に確認してから書き込もうと決心したはずが・・・。

    VS 2008 の afxpriv.h より引用
    // Note: This header file contains useful classes that are documented only
    //  in the MFC Technical Notes.  These classes may change from version to
    //  version, so be prepared to change your code accordingly if you utilize
    //  this header.  In the future, commonly used portions of this header
    //  may be moved and officially documented.
    とありました。多分、VC++ 4.0 当時にこのドキュメントを見て、次のリリースぐらいで正式ドキュメント化されるのかな、と思っていたのではないかと・・・。

    気を付けます。

    とは言え、このドキュメントから行くと、まだ正式にドキュメント化されていない事になり、共用 DLL で MFC を使う場合、スタティックリンクしておかないと、将来的には新しいウィンドウメッセージと衝突しておかしな事になる、という感じがしますが・・・。

    一般的に MFC プライベートメッセージを使う MFC アプリケーションはスタティックリンクが推奨されていたりするのでしょうか?

    (何か、完全に別の話題になってしまい、すいません・・・。)
    2009年7月2日 4:54
  • Atshi777さん有難うございます。
    教えて頂いたコードを引用し
    ビルドしたところ、以下のエラーメッセージがでました。
    'WM_SETMESSAGESTRING' : 定義されていない識別子です。

    使えない?ということなのでしょうか?
    調べる時間がなく
    コードを埋め込んだだけになってしまているのですが、
    とりあえずご報告まで。

    2009年7月2日 17:30
  • すいません。横から割り込んだ者です。

    上で afxpriv.h について触れていますが、WM_SETMESSAGESTRING というのは、Windows 定義のウィンドウメッセージではなく、MFC 内部で使われているウィンドウメッセージの事です。

    独自のウィンドウメッセージを定義して、ON_MESSAGE マクロで独自のメッセージマップ関数を作った経験はありませんか?

    独自のウィンドウメッセージなので、他のアプリケーションでは意味を持たないメッセージなのですが、MFC も内部で MFC 独自のメッセージを定義しています。

    それが MFC プライベートメッセージと呼ばれるウィンドウメッセージです。
    このウィンドウメッセージは MFC 独自のものであり、正式にはドキュメント化されていないメッセージです。WM_SETMESSAGESTRING もその一つなので、

    'WM_SETMESSAGESTRING' : 定義されていない識別子です。
    というエラーメッセージが出るわけです。

    エラーメッセージの出るヘッダ、よりは.cpp の方がいいと思いますが、

    #include <afxpriv.h>

    によって、MFC プライベートメッセージを含む、MFC 内部定義値等がインクルードされ、エラーは直るはずです。
    • 編集済み ミッヒー 2009年7月2日 18:41 追記&インクルード場所補足
    • 回答としてマーク sk7474 2009年7月23日 7:39
    2009年7月2日 17:50
  • afxpriv.hを追加することで、見事ビルドできました!
    思っている通りの動きで目から鱗状態。
    ツールバーのイベントがこれほど簡単に取れるとは。。
    もっと複雑なロッジ想像していましたので。
    お二人の的確なアドバイスのおかけです。

    ミッヒーさん
    afxpriv.hを詳しく記載して頂きありがとうございます。
    afxpriv.hを追加するだけの回答ではなく、
    なぜ追加する必要があるかまで解説して頂きとても参考になりました。
    また最初から最後まで相談に乗って頂いた事に感謝いたします。

    Atshi777さん
    シンプルでわかり易い、且つ的確な実装例ありがとうございました。
    的確すぎて感動すらおぼえます。
    おかげさまで無事解決に至りました。
    個人的には主要部分だけでなく、以下のような部分まで記載していただいた
    ことで初心者としてはスムーズに解決することができました。
    >BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    >    …
    >    ON_MESSAGE(WM_SETMESSAGESTRING, OnSetMessageString)
    >END_MESSAGE_MAP()


    お二人ともお忙しい中時間を割いて頂き、誠にありがとうございました。

    さて実装した後ようやくこの意味がわかってきたのですが、
    MFCのプライベートメッセージを拾うことから
    ミッヒーさんがおっしゃっていた、以下のことはなにか考慮する必要があるのでしょうか?
    >将来的には新しいウィンドウメッセージと衝突しておかしな事になる、という感じがしますが・・・。

    2009年7月4日 6:28
  • ドキュメント化されていないだけで、afxpriv.h の存在は以前から知られていましたし、当然 Microsoft 社の Visual C++ チームも考慮に入れて、次のバージョンリリースを行うとは思います。

    以前、何かの書籍で読みましたが、Internet Explorer 3.0 リリース時に追加されたコンポーネントが WM_USER からそう離れていない数値でウィンドウメッセージを定義し、問題になった、という事があった、らしいです・・・。物凄くうろ覚えなので間違っている可能性は高いですが。

    基本的にウィンドウメッセージは #define WM_??? 100 等という形式で宣言されます。要は数値なわけです。

    その数値の衝突を防ぐ為に、RegisterWindowMessage 関数が用意されていますが、afxpriv.h を見る限り、使われている様子はありません。

    つまり、将来的に、共用 DLL で MFC を使う場合、MFC プライベートメッセージだと思って処理をした場合、Windows で新たに設定されたウィンドウメッセージであり、当然まったく違うパラメーターを使用してしまう、という危険性が残る、と思うのです。

    前述したとおり、Visual C++ チームも MFC プライベートメッセージを考慮に入れて下さっているとは思います。しかし、元々ドキュメント化されていない MFC プライベートメッセージなわけですから、将来の Windows でエラーが起きても文句は言えません。そんな状況下で共用 DLL を使って安全なのでしょうか?と皆さんにお聞きしたかったわけです。

    皆さんのご意見を拝聴できれば、と思っています。
    • 編集済み ミッヒー 2009年7月4日 6:56 誤字訂正
    2009年7月4日 6:53
  • えーっと。すいません。改めてドキュメントをよーく読み返した結果、RegisterWindowMessage 関数はプロセス間でのやり取りに使うウィンドウメッセージを登録する関数でした。

    MFC 共有 DLL はアプリケーションと同じプロセス空間に配置されますから、プロセス境界は全く関係のない話になります・・・。

    つまり、本来の目的はプロセス間通信であり、同一プロセス内でのやり取りは普通に #define WM_USER + ??? で行うべきだ、という事でしょう。

    論点がかなりずれていました。すいません。

    結論としては、MFC プライベートメッセージを使う場合、MFC とスタティックリンクを行うか、afxpriv.h が更新される度に、ビルドし直す必要がある、という所でしょうか。

    お騒がせしました・・・。
    2009年7月6日 13:23

  • ご指摘のように、MFCで定義されているプライベートメッセージを利用する際には、以下のドキュメントに書かれたような注意が必要です。
    http://msdn.microsoft.com/en-us/library/xkd95027.aspx
    http://msdn.microsoft.com/ja-jp/library/xkd95027.aspx

    その点に触れていませんでした。申し訳ありません。

    えーっと。すいません。改めてドキュメントをよーく読み返した結果、RegisterWindowMessage 関数はプロセス間でのやり取りに使うウィンドウメッセージを登録する関数でした。

    MFC 共有 DLL はアプリケーションと同じプロセス空間に配置されますから、プロセス境界は全く関係のない話になります・・・。

    つまり、本来の目的はプロセス間通信であり、同一プロセス内でのやり取りは普通に #define WM_USER + ??? で行うべきだ、という事でしょう。
    WM_USERはプライベートウィンドウクラス固有のメッセージですので、通常そのような用途では使いません。
    WM_APPの方を使うのが普通だと思います。

    結論としては、MFC プライベートメッセージを使う場合、MFC とスタティックリンクを行うか、afxpriv.h が更新される度に、ビルドし直す必要がある、という所でしょうか。


    そうなるでしょうか。
    Side by Sideが使えれば、DLLでも行けると思います。
    • 回答の候補に設定 ミッヒー 2009年7月8日 2:21
    2009年7月7日 2:15
  • WM_USERはプライベートウィンドウクラス固有のメッセージですので、通常そのような用途では使いません。
    WM_APPの方を使うのが普通だと思います。
    誤解を招きそうなので、一応。

    WM_APPも要するにアプリケーションが自由に使えるメッセージ領域なので、
    たとえばMFCのプライベートメッセージが引っ越してくるのには適当ではないです。

    言いたかったことは、
    「同一プロセス内でのやり取りは普通に」と言われたら、WM_USERではなく、WM_APPだということです。
    2009年7月7日 2:36
  • WM_APPの方を使うのが普通だと思います。
    おっしゃる通りでした。WM_USER の方は Windows メッセージの最後を意味する感じですね・・・。大変参考になりました。まだ修正可能なアプリケーションで WM_USER を使っているものは、即刻修正したいと思います。

    Side by Sideが使えれば、DLLでも行けると思います。
    未だに VC++ 6.0 をうろついているので、Side by Side に付いては無知ですが、VC++ 6.0 リリース時に MFC42.dll が上書きされたと記憶しております。同一名称でしかも過去のバージョンは上書きされて無くなっている場合、Side by Side でも解決できないのではないか、と思ったのですが・・・。

    ともあれ、非常に参考になりました。Atsushi777 さん、ありがとうございました。


    追記

    まーた間違って書き込んでいました。私の 2 回目の書き込み
    つまり、OS 側の MSVC???.dll で問題なく動くのでしょうか?
    これ、MFC???.dll の間違いです・・・。

    たびたびすいません。
    • 編集済み ミッヒー 2009年7月7日 6:12 以前の誤記に関するお詫び
    2009年7月7日 2:43
  • 未だに VC++ 6.0 をうろついているので、Side by Side に付いては無知ですが、VC++ 6.0 リリース時に MFC42.dll が上書きされたと記憶しております。同一名称でしかも過去のバージョンは上書きされて無くなっている場合、Side by Side でも解決できないのではないか、と思ったのですが・・・。
    Private Assemblyとしてアプリケーションフォルダ以下に配置し、それを参照するようにしていれば、同名のファイルであっても他のアプリケーションによって上書きされることはありません。

    詳細な説明は省略させて下さい。(下記のページを糸口にして下さい。)
    http://msdn.microsoft.com/ja-jp/library/ms235531(VS.80).aspx




    # 事情があるのかもしれませんが、Visual C++ 6.0は既にサポート対象外ですので、早めに移行を検討されることをお薦めしておきます。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答の候補に設定 ミッヒー 2009年7月8日 2:28
    2009年7月7日 14:21
    モデレータ
  • Azulean さん、様々な所で的確な回答を頂き、感謝しております。
    Private Assemblyとしてアプリケーションフォルダ以下に配置し、それを参照するようにしていれば、同名のファイルであっても他のアプリケーションによって上書きされることはありません。
    Side by Side が使用可能な環境下であれば、アプリケーションフォルダ以下に MFC???.dll をコピーして、それを参照すればいい、という事でしょうか?

    一応、私が誤解していないか、ご回答頂ければ、と思います。
    2009年7月7日 16:18
  • Side by Side が使用可能な環境下であれば、アプリケーションフォルダ以下に MFC???.dll をコピーして、それを参照すればいい、という事でしょうか?
    同じフォルダにコピー(配置)するだけでは足りません。
    OSに合わせてmanifestを用意したり、.localファイルを用意したりする必要があります。
    詳細は、Side by Side関連のトピックを参照して下さい。


    私の突っ込み自体もそうですが、脱線気味なので、さらにSide by Sideについてご質問があるのであれば、ご自身でスレッドを立てて下さい。
    # スレッドを立てた上、こちらからリンクすること、新規スレッド側でどこのスレッドから派生しているか記入することをしておくと良いかも。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答の候補に設定 ミッヒー 2009年7月8日 2:21
    2009年7月7日 21:24
    モデレータ
  • Azulean さん、回答下さり、ありがとうございます。
    同じフォルダにコピー(配置)するだけでは足りません。
    OSに合わせてmanifestを用意したり、.localファイルを用意したりする必要があります。
    まだ Side by Side が使える環境下での開発を行っていないので、Side by Side 関連のドキュメントもろくに読んでいないのですが・・・。とりあえず、Side by Side が使える環境下では、アプリケーション開発時の MFC???.dll (デバッグバージョンではありません。念の為。) をコピーして使う事で、開発時と同じバージョンの MFC 共有 DLL が使える、という事ですね。
    私の突っ込み自体もそうですが、脱線気味なので、さらにSide by Sideについてご質問があるのであれば、ご自身でスレッドを立てて下さい。
    確かに脱線した書き込みでした。ただ、質問者の サンマルチ さんも VC++ 6.0 という環境だったので、Side by Side の使えない開発環境下で、MFC プライベートメッセージを扱う場合は・・・という感じもしていました。

    ともかく、Azulean さん、ありがとうございました。Side by Side が使える環境下では、細かい設定は必要なものの、MFC DLL のバージョンを心配しなくて済む方法がある、と判っただけでも非常に参考になりました。

    サンマルチ さん、横からスレッドを奪ったような形になってすいませんでした。私の疑問は全て解消したので、サンマルチ さんの方で問題ないようなら、Atsushi777 さんと Azulean さんに回答としてマークを設定し、スレッドを閉じた方がいいと思います・・・。
    2009年7月8日 2:21
  • こんにちは。中川俊輔です。

    皆様、とても詳しい回答ありがとうございます。

    サンマルチさん、はじめまして。フォーラムのご利用ありがとうございます。
    追加の質問で書かれている疑問は解決しましたでしょうか?
    勝手ながら、有用な情報と思われる回答へ回答マークをつけさせていただきました。

    何かまだわからないことがありましたら、ぜひまた質問を投稿してみてください。

    今後ともフォーラムをよろしくお願いします。
    それでは!
    マイクロソフト株式会社 フォーラム オペレータ 中川 俊輔
    2009年7月23日 7:44