Visual C++/MFCに関するFAQ

Scot Wingo
Stingray Software

バージョン5.0 1997年5月15日改訂
バージョン5.6 1998年7月14日改訂 ダウンロード可能なファイルとして本稿に追加
MSDN編集部のメモ(1997年5月)

本稿は、Stingray Softwareが提供するScot Wingo編著の「Microsoft Visual C++/Microsoft Foundation Classes(MFC)Frequently Asked Question(別名MFC FAQ)」を、許可の元に複製しています。Stingray Software製品の詳細についてはwww.stingray.com/をご覧ください。

最新版のMFC FAQはWorld Wide Webのwww.stingray.com/mfc_faq/でご覧になれます。MFC FAQに関するご意見、ご質問は mfc_faq@stingsoft.com 宛にお送りください。

このFAQは、Microsoft Corporationが制作したものではなく、技術的な正確さについてもMicrosoftが確認をしているわけではありません。

MSDN編集部のメモ(1998年8月)

好評のVisual C++/Microsoft Foundation Classes(MFC)Frequently Asked QuestionがStingray Softwareのみなさんにより改訂されました。Stingray社は、最新改訂版をHTML Helpドキュメント形式(.chmファイル)で提供しています。そこで私たちは、これをダウンロード可能なファイルとして以下に取り入れることにしました。バージョン5.6は、ダウンロード後にmfcfaq.chmをダブルクリックするだけで開けます。

Stingray Softwareについて

MFC FAQは、Stingray Softwareの厚意により提供されています。Stingray Softwareは、Windowsプログラマのために最良のオブジェクト指向開発ツールを作成することを目標として、1995年7月に創立されました。Stingray社は設立当初から、MFCでカバーしきれていない領域を埋めることに専念してきました。最近では、MicrosoftのVisual J++やSymantecのCafe製品系列と互換性のある好評のオブジェクト指向ツールを、Javaのクラス ライブラリの形でJava開発者向けツール市場へ提供しています。

著作権

本稿の編集著作権はScot Wingoにあります(copyright (c) 1997 by Scot Wingo)。この著作権表示を削除しない限り、自由に本稿の全文を複写、配布してかまいません。ただし、本稿は著者から文書による許諾を受けずに営利目的の販売を行ったり、商業製品に添付することはできません。また編集著作権とは、本稿の個々のセクションは自由に利用できるが、これらのセクションの集合を利用する場合は著作権に従う必要がある、ということを意味しています。いつの日か、これを本や映画にすることも考えられますのでね! このFAQは、Microsoftとはまったく関係ありません。Microsoft製品に関する、よくある質問に対する答えが入っていますが、それらの答えがみな正確かと言われたら、著者はまったく保証しかねます。このFAQは、平均的MFCプログラマを支援するために公開オンライン フォーラムに投稿された情報を集めたものに過ぎないのですから。

このFAQのメンテナンス担当者とは mfc_faq@stingsoft.com で連絡が取れます。

目次

汎用クラスに関する質問
GDIクラスに関する質問
ウィンドウ、コントロール、およびダイアログ クラスに関する質問
ビュー、ドキュメント、およびフレーム クラスに関する質問
OLEクラスに関する質問
WOSAクラスに関する質問
DLLおよびビルドに関する質問
MFCに関するヒント、秘訣、注意
ウィザードに関する質問
Visual C++に関する質問

汎用クラスに関する質問

CException-例外および例外処理

CException-例外および例外処理

CUserExceptionから派生した例外をthrowする方法は?

派生した例外をcatchしようとすると、次のエラーが出ます。「error C2039: 'classCMyException':'CMyException'のメンバではありません。'未定義のclassCMyException'が使われています。'IsKindOf:1番目の引数を'int*'から CRuntimeClass*に変換できません」

DECLARE_DYNAMIC()とIMPLEMENT_DYNAMIC()マクロを使って、CMyExceptionクラスを動的に作成可能にする必要があります。CATCHマクロは、throwされたクラスに関するランタイム情報にアクセス可能であることを前提としています。

Mike Blaszczak、blaszczak@bix.com comp.os.ms-windows.programmer.misc(1995年6月5日)

自作の例外はCUserExceptionから派生させなければならないのでしょうか?

いいえ。CUserExceptionという名前の「User」の部分は、単に例外がユーザーのアクションによって発生するということです。例外を派生させることができるのはCUserExceptionだけだという話をよく聞きますが、それは誤解です。

nuj@kruger.dk、電子メールにて(1995年11月23日)

GDIクラスに関する質問

CDC
CBitmap

CDC

HDCからCDCを作成する方法は?

Windows APIはDCへのハンドルを返すだけなので、HDCからCDCを作成したいと思うことがありますね。たとえば、オーナー描画リスト、コンボ、ボタンなどの場合、hDCを持つ描画項目 メッセージを受け取ります。そのhDCを、より馴染みあるCDCに変換するコードを、次に示します。

このテクニックは、ほかのあらゆるMFCクラス/Windowsハンドルのペアにも使えます。


void MyODList::DrawItem(LPDRAWITEMSTRUCT lpDrawItem)
{
    CDC myDC;
    myDC.Attach(lpDrawItem->hDC);
    // ここでほかの処理もする。

    // デタッチしないと、削除されるが、dc を削除すると
    // Windows としてはうれしくない。

    myDC.Detach();
}
もう1つのアプローチは、CDCのFromHandleメソッドを呼び出すことです。


    CDC * pDC = CDC:FromHandle(lpDrawItem->hDC);
どちらのほうが「よい」なのか、はっきりは言えませんが、FromHandle()を使うほうが「デタッチ」する必要がない分、間違いも少ないでしょう。

Jim McCabe、jmccabe@portage1.portup.com(1995年6月5日)

CBitmap

ディスクから256色ビットマップ ファイルを読み込む方法は?

現在、MFCにはDIBやBMPを読み込んだり、表示したりするための直接的なサポート機能が含まれていません。ですが、その方法を示すサンプル アプリケーションは数多くあります。

第一のサンプルは、MFCのサンプル アプリケーション、DIBLOOKです。MULTDOCSサンプルも、DIBLOOKと同じソース コードを使って、DIBとBMPファイルの読み込みと表示ができるようにしています。

Visual C++には、ほかにも2つ、DIBVIEWとSHOWDIBというSDKのサンプルがあります。

comp.lang.c++に投稿された質問(1995年6月5日)。これはMS FAQにありました(1995年6月25日)

ウィンドウ、コントロール、およびダイアログ クラスに関する質問

ウィンドウ
コントロール
ダイアログ
コントロール バー、ステータス バー、ツール バー、ダイアログ バー
メニュー
Windowsコモン コントロール(別名Windows 95コントロール)
CSplitterWndに関するFAQ

ウィンドウ

ウィンドウ用のカスタム アイコンを使用できるようにする方法は?

Microsoft Foundation Class Libraryには、メイン フレーム ウィンドウとMDIフレーム ウィンドウ用のアイコンがリソースの形で用意されています。リソースID AFX_IDI_STD_MDIFRAMEを持つアイコンは、MDIフレーム ウィンドウ用のアイコンであり、リソースID AFX_IDI_STD_FRAMEを持つアイコンは、メイン フレーム ウィンドウ用のアイコンです。アプリケーションにおけるこれらのアイコンを置き換えるためには、該当するIDを持つアイコンをリソース ファイルに加えます。

そのアプリケーションは、テンプレートの生成時にMDIの子ウィンドウにあるビュー用のアイコンを指定します。ユーザーが対応するビューを格納するMID子ウィンドウを最小化すると、アプリケーションは指定されたリソースIDを持つアイコンを使用します。

このテクニックは、1つのアイコンをアプリケーションに指定してこれらのウィンドウと関連づけられるようにします。またWindowsでは、最小化されたウィンドウの動的な描画もサポートしています。MFCでこれを行うためには、AfxRegisterWndClass()を使用してNULLアイコン ハンドルを持つウィンドウ クラスを登録します。アイコンを動的に描画するためにウィンドウ クラスにあるPreCreateWindow()関数をオーバライドし、AfxRegisterWndClass()によって返された名前をCREATESTRUCTのメンバIpszClassNameにコピーします。これでNULLアイコンを持つクラスを使用しているウィンドウが生成されます。ユーザーがこのウィンドウを最小化すると、アイコンはWM_PAINTメッセージを受け取ります。WM_PAINTメッセージは表示情報を適切に処理します。そうすることでOnPaint()メッセージ ハンドラをオーバーライドし、ウィンドウが最小化されているかどうかを確認するためにIsIconic()関数を呼び出します。ウィンドウが最小化されていれば、CPaintDCオブジェクトを生成してアイコンの描画に使用します。ウィンドウが最小化されていない場合は、基本クラス バージョンのOnPaint()を呼び出して、普通にウィンドウを更新します。

Visual C++ Knowledge Base記事 Q109039(1994年6月4日)

MFCで生成されたウィンドウのスタイルを変更する方法は?

AppWizardが生成したフレームワーク アプリケーションが使用するウィンドウの、デフォルトのウィンドウ属性を変更するために、ウィンドウの仮想メンバ関数PreCreateWindow()をオーバーライドします。PreCreateWindow()は、通常CDocTemplateクラスによって内部的に処理される生成処理に、アプリケーションがアクセスできるようにします。フレームワークは、ウィンドウを生成する直前にPreCreateWindow()を呼び出します。PreCreateWindow()のCREATESTRUCT構造体のパラメータを変更することで、ユーザーのアプリケーションがウィンドウを生成するために使用する属性を変更できます。

Microsoft Foundation Class Library Version 2.0で提供されるCTRLBARSサンプル アプリケーションでは、ウィンドウ属性を変更するこのテクニックを例示しています。ユーザーのアプリケーションがPreCreateWindow()の何を変更するかによって、時にはベース クラスの実装を呼び出す必要も出てきます。注意してください。詳細については、Visual C++ Knowledge Base記事Q99847を参照してください。

Visual C++ Knowledge Base記事Q109039(1995年6月7日)

MFCを使用してウィンドウを最小化する方法は?

WM_GETMINMAXINFO用のハンドラを記述します。

Mike Blaszczak、blaszczak@BIX.com, via programmer.misc(1995年6月12日)

ウィンドウのタイトルを変更する方法は?


AfxGetApp()->m_pMainWnd->SetWindowText("My Window Title");
-あるいは-

AfxGetMainWnd()->SetWindowText ( "My Own Title" );
aj536@freenet.toronto.on.ca, mfc-l(1995年7月9日)

メイン ウィンドウのキャプションにある「無題」を消す方法は?

MainFrameクラスにあるPreCreateWindow()関数をオーバライドして、中で以下を実行します。


cs.style &= ~FWS_ADDTOTITLE;
この方法で、ウィンドウの初期位置(cs.x, cs.y, cs.cx, cs.cy)を設定したり、クラス(cs.IpszClass)を変更したりもできます!最後にCFrameWnd::PreCreateWindowを呼び出すのを忘れないでください・・・。

netninja@svpal.org、programmer.misc(1995年/7月29日)

MDI子ウィンドウを最大化する方法は?


void CMainFrame::ActivateFrame(int nCmdShow)
{
    if (!m_bActivated)
    {
        m_bActivated = TRUE;
        nCmdShow = SW_SHOWMAXIMIZED;
    }
    CFrameWnd::ActivateFrame(nCmdShow);
}
m_bActivatedには、フレーム オブジェクトのメンバ変数を指定します。

duane@anasazi.com、programmer.win32(1995年8月3日)

CSplitterWndでフォーカスがおかしくなるわけは?

スプリッタ バーを移動させると、必ずエディット コントロールの中からIビーム カーソルが消えてしまいます。カーソルを再表示させるために、エディット コントロールの中でもう一度クリックしなければなりません。

Knowledge Base記事Q108434、「FIX:CSplitterWnd Class Does Not Handle All Focus Cases」に、分割ウィンドウに関連するフォーカスの問題と、その問題の回避法がいくつか載っています。多分役に立つと思います。

Ramesh(NetQuest)、MSMFC(1995年8月3日)

最初のMDI子ウィンドウを最大化された状態で開始する方法は?

私の場合の解決策を以下に示します。


class CChildFrame : public CMDIChildWnd
{
   // .. 一部省略 ...
   // これで MDI 子ウィンドウが最大化される。

     virtual void ActivateFrame(int nCmdShow) {
            // 別のウィンドウが開いていれば標準設定を使用
       if(GetMDIFrame()->MDIGetActive())
      CMDIChildWnd::ActivateFrame(nCmdShow);
       else
      CMDIChildWnd::ActivateFrame(SW_SHOWMAXIMIZED); // でなければ、最大化して開く
  }
// ... 一部省略 ...
};
Stephen Bade, bade@convergent-design.com

コントロール

Dialog TemplateからCControlを取得する方法は?

GetDlgItemの戻り値を型変換するだけで、作成済みのダイアログ コントロールからコントロールへのポインタを取得することができます。以下は、IDC_CHECK1というIDを持つチェック ボックスからCButtonを生成する例です。


void my_function(CDialog * pDialog)
{
    CButton * pButton = (CButton *)pDialog->GetDlgItem(IDC_CHECK1);
    ASSERT(pButton != NULL);
    pButton->SetCheck(m_bShowState);
}
安全のために、GetDlgItemの戻り値の有効性は必ず調べるようにしましょう。

Scot Wingo、scot@stingsoft.com

MFCを使ってコントロールをサブクラス化する方法は?

SubClassDlgItemに関する文書を読みましょう。以下はSubClassDlgItemを呼び出す方法の一例です。


BOOL CMyDialog::OnInitDialog()
{
    // 先にサブクラス化をする。

    m_MyControl.SubClassDlgItem(ID_MYCONTROL, this);
    // 基本クラスに、やるべきことをやらせる。

    CDialog::OnInitDialog();
    // 必要ならほかの処理もここでする。

    // Ctl3d は必ず最後に呼び出すこと。さもなければ、サブクラス化を
    // 複数を行うことによるアサーションが生じてしまう。

    Ctl3dSubclassDlg(m_hWnd, CTL3D_ALL);
}
Mike Williams、mikew@marlin.ssnet.com、mfc-l(1995年6月1日)

コントロールをサブクラス化すると、ASSERTが出るわけは?

コントロールは、Ctl3dSubclassDlgを呼び出す「前に」サブクラス化しておかなければなりません。3DコントロールDLLが先にロードされると、それがコントロールをサブクラス化してしまうのでASSERTが出るのです。

Mike Williams、mikew@marlin.ssnet.com、mfc-l(1995年6月1日)

フォーカスが外れたコントロールの内容を検査する方法は?

メモ
これはMicrosoft Software Library(MSL)にあります。

FCSVALサンプル アプリケーションは、ダイアログ ボックスの中でコントロールごとに検証をする方法を示すために作成されています。

アプリケーション自体は、CWinApp::InitInstanceによって表示される、モーダル ダイアログ ボックスに過ぎません。ダイアログ ボックスを表示した後に、InitInstance()は単純にアプリケーションを終了します。

このサンプルの重要な部分はダイアログボックス クラスの実装にあります。ダイアログ ボックスには2つのエディット コントロールがあり、1つは1から20までの整数値の入力を受け付けます。もう1つは、5文字以下の文字列を受け付けます。表示されているダイアログ ボックスの中でコントロールからコントロールへとタブ キーやクリックでフォーカスを移動するとき、フォーカスを失うコントロールの内容が検証されます。

CfocusDlgクラス

アプリケーションの機能は、CFocusDlgクラスと、その4つのメッセージ ハンドラ(後述します)の実装に集約されています。MFCで提供されたルーチンを使用する通常のデータ交換(DDX)と検証(DDV)は、ダイアログ ボックスが最初に表示される時と、入力を確定するためにユーザーが[OK]ボタンを押した時に、OnInitialUpdate()の中で行われます。これは、メンバ変数がダイアログ ボックスのコントロールと連結されていて、ダイアログ クラスのDoDataExchange()関数で検査できる場合にClassWizardによって提供されるデフォルトの動作です。

あるコントロールから次のコントロールへとフォーカスを切り替える時には、フォーカスを失うエディット コントロールによって送信されるEN_KILLFOCUS通知を処理することでコントロールの内容を検証します。ここでは、内容をチェックし、それが適切でない場合は、メッセージ ボックスを表示してユーザーに知らせ、その後にフォーカスを元のコントロールに戻します。残念ながら、killfocusメッセージ ハンドラの中でフォーカスをセット(あるいはメッセージ ボックスを表示)しようとすると、厄介な問題が生じてしまいます。この時点のWindowsは、あるコントロールから別のコントロールにフォーカスが移動中という不確定な状態にあります。したがって、この段階で検証をしてSetFocus()を呼び出すのはよくありません。

ここでの解決策は、ユーザー定義メッセージをダイアログ ボックス(親)に通知して、そこで検証とSetFoucus()を実施することです。これにより、安全なタイミングで処理ができます(FOCUSDLG.CPPファイルのCFocusDlg::OnEditLostFocus()と、FOCUSDLG.HファイルのM_EDITLOSTFOCUSユーザ定義メッセージを参照)。

この関数についてもう1つ気付く点は、TRY/CATCHを使って検証をしているという点です。コントロールのデータの検証やロードに失敗すると、提供されているDDX/DDVルーチンはCUserExceptionsをthrowします。これらをCATCHブロックの中でcatchして、SetFocus()を行わなければなりません。

メモ
このサンプルにはほかにも素晴らしい要素があるのですが、ネット上で私が今までに見た質問の中で一番多かったのがこれです。

Knowledge Base記事Q142481(1995年6月25日)

1連のチェックボックスを有効/無効にする方法は?

単独のHWNDでこれを行う魔法は知りません。ですが、私が長い間使ってきた、単純で、コードを見ればわかるテクニックがあります。UINT(コントロールID)の配列と可視性を示すフラグを受け取るルーチンを作成するのです。

この関数は独立型の関数にすることも、クラスの中に置くこともできます。私は、このような小さなユーティリティ関数を集めてCDialogBaseクラスに入れておくようにしています。ClassWizardで新しいダイアログ ボックスを作成するときは、CDialogの代わりにCDialogBaseから派生をするコードを書きます。

たとえば、関数は次のようになります。


void CDialogBase::ShowControls(UINT* pControls, UINT cControls, BOOL fVisible)
{
    for (UINT uIndex = 0; uIndex < cControls; uIndex++)
    {
        CWnd* pwnd = GetDlgItem(pControls[uIndex]);
        if (pwnd)
        {
            pwnd->ShowWindow(fVisible ? SW_SHOW : SW_HIDE);
            pwnd->EnableWindow(fVisible);
        }
    }
}
これを用意しておけば、次のようにして、(多くの場合はOnInitDialogハンドラの中で)コントロール グループを対象に関数を呼び出せます。


#define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(a[0]))
{
    static UINT aGroup1[] = { DLG_CHBOX1, DLG_CHBOX2,
            DLG_STATIC1 };
    static UINT aGroup2[] = { DLG_LABEL2, DLG_LABEL7 };
    ShowControls(aGroup1, SIZEOF_ARRAY(aGroup1), TRUE);
    ShowControls(aGroup2, SIZEOF_ARRAY(aGroup2), FALSE);
}
こうしておけば、これらのコントロール配列をほかの様々な形で利用できるでしょう(一連のコントロールのフォントを変更するなど)。幸運を祈ります。

jmccabe@portage1.portup.com、mfc-l(1995年7月18日)

コントロールの背景色を変更する方法は?

ダイアログは、WM_CTLCOLORメッセージをトラップするとよいでしょう。MFCのヘルプ ファイルでCWnd::OnCtlColorを調べてください。コントロールが自身をペイントしようとする直前に、その親ウインドウはテキストと背景ブラシの標準の色をセットするチャンスを得ます。

jmccabe@portage1.portup.com、mfc-l(1995年7月18日)
Microsoft Knowledge Base記事Q117778「Changing the Background Color of an MFC Edit Control」もチェックしてください。

Ramesh、MSMFC(1995年7月19日)

コントロールでキーをトラップする方法は?

WM_GETDLGCODEを処理して、適切な値を返します。ただし、リスト ボックス(だけでなく、ほかのあらゆるコントロール)は、フォーカスがある場合にのみ、キーボード入力を処理できる点に注意してください。

Joe Janakovic、joej@golddisk.com, programmer.misc(1995年8月21日)

複数選択リスト ボックスでDDXする方法は?

CISのMSMFCライブラリからMLBDDX.ZIPをダウンロードしてください。必要なコードが全部入手できますよ。ダイアログを閉じるときに、指定されたCStringListが、選択されている項目で埋められます。フリーウェアです。

Patrick Philippot、CIS、電子メールにて(1995年8月3日)

ボタンの背景色を変更する方法は?

注意:
「コントロールの背景色を変更する方法は?」の方法は、ボタンには通用しません!
ダイアログ ボタンの色を変更したければ、オーナー描画ボタンを使用しなければなりません(ビットマップ ボタンが使えます)。OnCtlColor()を使って色を変える方法はボタンには通用しません。次のKnowledge Base記事が役に立つかもしれません:Q32685「Using the WM_CTLCOLOR Message」とQ64328「SAMPLE:Owner-Draw:3-D Push Button Made from Bitmaps with Text」。この記事は、オーナー描画ボタン用のサンプル コードを説明しています。

Ramesh(NetQuest)、MSMFC(1995年8月3日)

CEditに複数行が入らないわけは?

行が\nでなく\r\nで区切られていることを確認してください。

sutor@watson.ibm.com、mfc-l(1995年8月7日)

コンボ ボックスのCeditにアクセスする方法は?


CComboCox combo;
CEdit edit;
// Combobox を作成 ...
// ...
POINT tmpPoint = {1,1};
edit.SubclassWindow( combo.ChildWindowFromPoint(tmpPoint)
->GetSafeHwnd());
jahans@slb.com、mfc-l(1995年8月25日)
または:

MFCサンプルのnpp-npview.cppを調べてください!どうやらコンボ ボックスはエディットをすべて1001(10進)というIDで作成するようです。したがって、コンボを挿しているpWndオブジェクトをpComboBoxとすれば、次のように指定するだけで事足ります。


pComboBox->GetDlgItem(1001);

エディット コントロールに64K以上をロードする方法は?

Visual C++ 2.1以降で使えるRich Edit Controlは64K以上をサポートしています。この課題について詳しく知るには、WordPadサンプルがよいでしょう。もしあなたが16ビット プログラミングを余儀なくされているのなら、Magma System社がこれを行うための16ビット対応DLLを提供していたと思います。詳細については、75300.2062@compuserve.comでMarc Adlerに連絡してください。

Scot Wingo-scot@stingsoft.com

コンボ ボックスのリスト ボックス部分をサブクラス化する方法は?

コンボ ボックスのリスト ボックス部分は、COMBOLBOX型("L"に注意)です。ComboLBoxウィンドウはComboBoxの子ウィンドウではないので、COMBOLBOXコントロールをサブクラス化する方法はわかりにくくなっています。幸い、Win32 APIの下では、Windowsはリスト ボックスを描画する前にWM_CTLCOLORLISTBOXと呼ばれるメッセージをCOMBOBOX("L"が無いことに注意)に送信します。このメッセージで渡されるlParamには、リスト ボックスのハンドルが含まれています。たとえば、次のようにします。


LRESULT CFileUpdateCombo::OnCtlColorListBox(WPARAM wParam,
   LPARAM lParam)
{
    if ( ! m_bSubclassedListBox )
{
        HWND hWnd = (HWND)lParam;
        CWnd* pWnd = FromHandle(hWnd);
        if ( pWnd && pWnd != this )
        {
            // m_ListBox is derived from CListBox.
            m_ListBox.SubclassWindow(hWnd );
            m_ListBox.SetOwner(this);
            m_bSubclassedListBox = TRUE;
        }
    }
    return (LRESULT)GetStockObject(WHITE_BRUSH);
}
mikem@abelcomputers.com、電子メールにて(1995年9月7日)

MFCの標準コントロール クラスから継承して、サブクラス化されたコントロールとサブクラス化されていないコントロールの両方で正しく動作する初期化コードを書く方法は?

編集者のメモ(Scot Wingo):
これはFAQとは言えないかもしれませんが、かなり面白い質問なのであえて入れました。

対処方法を1つ知っていますが、あなたは気に入らないでしょう。それでも、両方のサブクラス化の方法に対応できます。

SubclassWindow()が仮想関数ならば、SubclassDlgItemがSubclassWindow()を呼び出し、共通の初期化処理をこの時点と、OnCreate()から呼び出せるので、問題はすべて解決できます。Microsoftのコードによって、すべての初期化ポイントから呼び出せる仮想関数SetupWindow()があればなおよいでしょう。

しかし、人生はそううまくはいきません。私の対処方法は、問題のコントロールのメッセージ ループを遅くするかもしれませんが、これまでのところでは、パフォーマンスで問題が生じたことはありません。次のようにして、コントロールのWindowProc()仮想関数をオーバーライドします(OnCreate()でSetupWindow()も呼び出します)。


LRESULT CExtendControl::WindowProc( UINT message, WPARAM wParam,
   LPARAM lParam)
{
    if (!m_bSetup)
        SetupWindow();
    return CEdit::WindowProc(message, wParam, lParam );
}
// これは仮想関数。これを継承するすべてのクラスで Hwnd をセットアップ
// するときにこれを使うこと。サブクラス化されたウィンドウにも使える。

void CExtendControl::SetupWindow()
{
    ASSERT( m_hWnd );
    m_bSetup = TRUE;
    *** この初期化用のコードを入れる!***
}
Jody Power、jodyp@andyne.on.ca)

ダイアログ

ダイアログを中央に表示させる方法は?

それには、CWnd::CenterWindowメソッドを使います。通常、私はこれをオーバーロードしたOnlnitDialog関数の中に入れています。CDialogはCWndの親なので、直接メソッドを呼び出すことができます。


 BOOL CMyDialog::OnInitDialog()
{
    // ダイアログのほかの初期化処理はここでする。

    CenterWindow();
    return TRUE;
}
Scot Wingo、scot@stingsoft.com(1995年6月1日)

Windows 95で「古いスタイル」のコモン ダイアログを表示する方法は?

MFCはWindows 95上で実行されているかどうかを検出し、もしそうなら標準のファイル オープン ダイアログ ボックスをエクスプローラー版のファイル オープン ダイアログ ボックスに置き換えます。MFCがエクスプローラー版のファイル オープン ダイアログ ボックスを使わないようにするには、CFileDialogから派生させたクラスのコンストラクタに次の行を加えます。


m_ofn.Flags &= ~OFN_EXPLORER;
andyd@andyne.on.ca(Andy DeWolfe)、programmer.win32より(1995年5月10日)

Windows 95のコモン ダイアログをサブクラス化する方法は?

できないことはないのですが、MicrosoftはWindows 95で、これをものすごく難しくしてしまいました。「子ダイアログ テンプレート」(WS_CHILDスタイルで)を作成し、m_ofn.lpTemplateNameにこれをセットしなければなりません(m_ofn.hlnstanceがアプリケーションのインスタンスにセットされることを確認してください)。このテンプレートは、ダイアログに加えているコントロールだけを含んでいるのでなければなりません(つまり、Windows 3. xのように重複する標準コントロールを持つダイアログ全体ではないということです)。

ダイアログが起動されると、テンプレートは通常のファイル ダイアログ コントロールの下に(標準で)表示されます。stc32(include\dlgs.hで定義されています)というIDのスタティック コントロールを加えた場合、コモン ダイアログのコードは、オリジナルのコントロールがstc32コントロールのある場所に表示されるように、ダイアログの内容を再配置します(大きさが合うようにサイズを変える必要はありません。コモン ダイアログ コードがあなたの代わりにやってくれます)。

m_ofn.IpfnHookを用意して、追加したコントロールをそのフック プロシージャで処理する必要があります。システムは、ダイアログ テンプレートを通常のダイアログの「上層」に加えるので、MFCメッセージのルーティングはコントロールに到達しなくなります。このため、CFileDialogから派生させたメッセージ マップを通してそれらを処理することはできません。この問題を回避する方法を見つけた人は、私に教えてください!! これは非常に厄介な問題であり、Microsoftもそれを認識しています。MicrosoftはMFC4.0でこれを修正することを約束しています。

編集者のメモ(Scott Wingo)
この問題は、4.0でとてもよくなっています。コールバックしてもらうためにオーバーライドできる仮想関数が用意されています。その上、従来のスタイルと新しいスタイルのテンプレートをどちらも処理できます。これはかなり賢い!
joej@golddisk.com、programmer.win32より(1995年6月10日)

CDialog::Create()が失敗してしまいます。何がいけないのでしょうか?

無効なHWNDを親として渡します。

無効なダイアログのリソースIDを渡しています(数字のIDと文字列のIDがあることに注意してください。#define ID_MYDIALOG 0x1234というような定義にはご注意を。これは、リソース コンパイラによっては「文字列」IDです)。

ダイアログの1つまたは複数のコントロールを作成できなかったということです。これは通常、登録されていないカスタム コントロールを使ったためです。OnInitDialogメッセージの処理中に、EndDialogを呼び出しています(あるいは、別のハンドラを呼び出すのが早すぎます)! ダイアログがWS_CHILDスタイルのときに、親としてNULLのHWNDを渡しています。

今思い当たる答えは、これで全部です。

Dean McCrory、MSMFC(1995年6月16日)

ダイアログの中にツールバー/ステータス バーを作成する方法は?

Microsoft Software Libraryには、その方法を示すDLGCBRというサンプルがあります。基本的に、4つのステップがあります。概要を説明してから、コードを書いておきます。

ダイアログにコントロール バーを加えるために、まずは通常通りコントロール バーを作成し、次にダイアログのクライアント領域にコントロール バー用の領域を空けなければなりません。コントロール バーを正しく機能させるためには、ダイアログはフレーム ウインドウと同じ機能をいくつか実現する必要があります。コントロール バーでON_UPDATE_COMMAND_UIハンドラを動作させたい場合は、さらに新しいコントロール バークラスを派生させ、WM_IDLEUPDATECMDUIメッセージを処理する必要があります。ダイアログがアプリケーションのメイン ウィンドウではない場合は、親フレーム ウィンドウも修正して、ダイアログのコントロール バーへWM_IDLEUPDATECMDUIメッセージを渡せるようにします。

ダイアログのクライアント エリアの中にコントロール バー用の領域を空けるために、ダイアログのOnInitDialog()関数で、次に示すステップを実行します。

  1. コントロール バーを作成します。

    
    CRect rcClientStart;
    CRect rcClientNow;
    GetClientRect(rcClientStart);
    RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
    AFX_IDW_CONTROLBAR_LAST,
    ,reposQuery,
    rcClientNow);
    
  2. RepositionBars()のreposQueryオプションを使用して、コントロール バーがどのくらいの領域を占有するかを計算します。
    
    CPoint ptOffset(rcClientStart.left - rcClientNow.left,
    rcClientStart.top - rcClientNow.top);
    ptOffset.y += ::GetSystemMetrics(SM_CYMENU);
    CRect rcChild;
    CWnd* pwndChild = GetWindow(GW_CHILD);
    while (pwndChild)
    {
    pwndChild->GetWindowRect(rcChild);
    rcChild.OffsetRect(ptOffset);
    pwndChild->MoveWindow(rcChild, FALSE);
    pwndChild = pwndChild->GetNextWindow();
    }
    
  3. クライアント領域の上部または左側でコントロール バーが占有する領域を考慮して、ダイアログ中のすべてのコントロールを移動します。ダイアログにメニューがある場合は、メニューが使用う空間も考慮する必要があります。

  4. コントロール バーが使う空間の広さに応じて、ダイアログの大きさを増やします。

    
    CRect rcWindow;
    GetWindowRect(rcWindow);
    rcWindow.right += rcClientStart.Width() - rcClientNow.Width();
    rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height();
    MoveWindow(rcWindow, FALSE);
    
  5. RepositionBars()を使用して、コントロール バーを配置します。

ステータス バーの最初のペインをメニュー項目のテキストで更新するためには、ダイアログ クラスのWM_MENUSELECT、WM_ENTERIDLE、WM_SETMESSAGESTRINGを処理しなければなりません。これらのメッセージについて、CFrameWndハンドラと同じ機能を実現する必要があります。これらメッセージ ハンドラのサンプルについては、サンプル プログラムのCModelessMainクラスを参照してください。

ON_UPDATE_COMMAND_UIハンドラを、ほかのステータス バー ペインやツールバー ボタンで使えるようにするには、新しいコントロール バー クラスを派生させて、WM_IDLEUPDATECMDUI用のメッセージ ハンドラを実装する必要があります。これは、コントロール バーのOnUpdateCmdUI()の標準実装では、親ウィンドウがフレーム ウィンドウであると想定しているからです。しかし、OnUpdateCmdUI()は、CCmdTargetポインタだけを受け取る関数に、親ウインドウのポインタを渡すことしかしません。したがって、OnupdateCmdUI()に渡す親ウィンドウのポインタがCFrameWndポインタであると指定して、コンパイル エラーを回避することができます。例を示します。


LRESULT CDlgToolBar::OnIdleUpdateCmdUI(WPARAM wParam,LPARAM lParam)
{
    if (IsWindowVisible())
    {
        CFrameWnd* pParent = (CFrameWnd*)GetParent();
        if (pParent)
            OnUpdateCmdUI(pParent, (BOOL)wParam);
    }
    return 0L;
}
メイン ウインドウ以外のダイアログにWM_IDLEUPDATECMDUIメッセージを渡すためには、フレーム ウインドウ クラスでダイアログ ポインタをセーブし、そのクラスでWM_IDLEUPDATECMDUIハンドラを作成します。このハンドラは、CWnd::SendMessageToDescendants()を使用して、ダイアログの子ウィンドウにWM_IDLEUPDATECMDUIメッセージを送らなければなりません。その後、フレーム ウィンドウ内で、メッセージの標準処理を実行します。

Visual C ++ Knowledge Base記事Q123158(1995年6月25日)

CDialog::PreCreateWindow()が呼び出されないわけは?

ダイアログ ボックスを作成したときには、PreCreateWindowは呼び出されません。ダイアログ ボックス用のデータやコントロールを初期化したい場合は、OnInitDialogメッセージをトラップし、そこで必要な処理をします。PreCreateWindowは、作成しているウィンドウのパラメータを変更するために使用するものです。

ewalker@tezcat.com、mfc-l(1995年7月12日)

プロパティ ページにコモン ダイアログを埋め込む方法は?

この質問は、しょっちゅうCompuServeのMFCフォーラムに出てきますが、答えは簡単です。残念ながら方法はありません:-(
chris@chrism.demon.co.uk、programmer.win32(1995年7月12日)
[そのとおり。問題は、Windowsではこれらのダイアログがモーダル ダイアログとして実装されている点です。プロパティシートに何かを埋め込むためには、コモン ダイアログがモードレスな子ダイアログ ウインドウとして実装されている必要があります。Windows APIは、いずれのコモン ダイアログでもモードレスな子としての機能を公開しないので、プロパティ シートにそれらを埋め込むことができないのです。この機能を実願するには、これらのページを「自分なりに」実装するしかありません。

MSDN編集部の問い合わせに対するDean McCroryの回答(1996年4月25日)]

CDialogコントロールのDDX/DDVが初期化できないわけは?

ダイアログが生成されるまで、つまりDoModal()が実行されるまでは、ダイアログのコントロールに対して何も行えません。問題の解決法として、データ用のメンバ変数を作成し、それらをDoModalの呼び出しの前に初期化して、その後OnInitDialogまたはUpdateData()で値を渡すという手段があります。ClassWizardで行うメンバ変数の初期化とほとんど同じです。

したがって、ダイアログにCStringListかCStringArrayを組み込み、そこにリストボックス用の値を入れ、それらをOnInitDialog(など)にあるリストボックスに渡します。

Niels Ull Jacobsen、null@diku.dk、programmer.controls(1995年7月11日) ダイアログをOnInitDialogで初期化します。必要に応じて、ダイアログのコンストラクタに、ドキュメントのポインタを渡します(private/protected m_pDocメンバにそのポインタを保存します)。

jhasling@gascad.co.at、programmer.controls(1995年7月11日)

CPropertyPageのキャプションを変更する方法は?

次のようにすれば、プロパティー シートにページを加える前にラベルを変更できます。CPropertyPageからクラスを派生させ、キャプションをセットするpublic関数SetCaptionを追加する必要があります。


void CPage1::SetCaption(char *str)
{
    m_strCaption = str; // m_strCaption は、CpropertyPage の
                        // protected メンバ
}
このようにしておけば、次のようにSetCaption()関数が使用できます。


CMySheet my("My PropSheet");
CPage1 p1;
p1.SetCaption(str); // キャプションの設定。

my.AddPage(&p1);
CAnotherSheet newps("New Sheet");
CPage1 p2;
p2.SetCaption(newstr);
newps.AddPage(&p2);
my.DoModal();
Ramesh(NetQuest)、MSMFC(1995年8月3日)

ダイアログでF1キーをトラップする方法は?

Knowledge Base記事Q117563「How to Trap WM_KEYDOWN Messages in a CDialog」に、ダイアログ ボックスでWM_KEYDOWNメッセージをトラップする方法が説明されています。

「SAMPLE:Context Sensitive Help in a CDialog」では、ダイアログでコンテキスト センシティブ ヘルプを提供する方法が説明されています。サンプ ルコードも示されています(Knowledge Base記事Q110506)。

Ramesh(NetQuest)、MSMFC(1995年8月31日)

ダイアログ専用のMFCアプリケーションのアイコンを変更する方法は?

CWinAppから派生したクラスのInitInstance()に次のコードを加えます。


BOOL CDialogTestApp::InitInstance()
{
    // ...
#if(_MFC_VER >= 0x0300)
    SetClassLong(m_pMainWnd->m_hWnd,GCL_HICON,
        (LONG)LoadIcon(IDC_ICONDIALOGAPP));
#else
    SetClassWord(m_pMainWnd->m_hWnd,GCW_HICON,
        (WORD)LoadIcon(IDC_ICONDIALOGAPP));
#endif
    // ...
    m_pMainWnd->ShowWindow(m_nCmdShow);
    return TRUE;
}

コントロール バー、ステータス バー、ツール バー、ダイアログ バー

ツール バーにコンボ ボックスを加える方法は?

CToolBar::SetButtonInfo()メソッドを使えばできます。MFCのサンプルCTRLBARSのmainfrm.cppファイルを見れば、その方法がわかります。基本的にはSetButtonInfoを呼び出してコンボ ボックス用の空き領域(TBBS_SEPARATOR)を作成します。このとき、コンボ ボックス用のヘルプとツール チップのリソースIDを使用します。その後、GetItemRectを使ってコンボ ボックスの境界矩形を取得し、その中に、自分のコンボ ボックス ウィンドウを作成します。

Scot Wingo、scot@stingsoft.com(1995年6月1日)、martynl@cix.compulink.co.uk-改訂

ステータス バーにあるペインのテキストを更新する方法は?

標準では、ペインが生成された時点でCStatusBarのペインは使用可能なっていません。ペインをアクティブにするためには、ステータス バー上にある各ペインに対してON_UPDATE_COMMAND_UI()マクロを呼び出し、ペインを更新しなければなりません。ペインはWM_COMMANDメッセージを送信しないので、ClassWizardを使用してペインをアクティブにするすることはできません。コードは手で入力しなければなりません。たとえば、あるペインのIDがID_INDICATOR_PAGEで、ドキュメントの現在のページ番号が含まれているとします。ID_INDICATOR_PAGEペインにテキストを表示させるには、ヘッダ ファイル(おそらくMAINFRM.Hファイル)に以下を追記します。


afx_msg void OnUpdatePage(CCmdUI *pCmdUI);
アプリケーションのメッセージ マップには以下を追記します。


ON_UPDATE_COMMAND_UI(ID_INDICATOR_PAGE, OnUpdatePage)
ソース コードのファイル(おそらく、MAINFRM.CPP)には、以下を追記します。


void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI){    pCmdUI->Enable();}
ペインにテキストを表示させるために、OnUpdate()関数内でSetPaneText()またはCCmdUI::SetText()のどちらかを呼び出します。たとえば、現在のページ番号を格納する整数型の変数m_nPageを設定したい場合、OnUpdatePage()関数は、次のようになるでしょう。


void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI){    pCmdUI->Enable();    char szPage[16];    wsprintf((LPSTR)szPage, "Page %d", m_nPage);    pCmdUI->SetText((LPSTR)szPage);}
これにより、アイドル時の処理の間にアプリケーションは他のインジケータを更新するのと同じ方法で、ペインにページ番号を表示します。

Visual C++ Knowledge Base記事109039(1994年6月4日)

実行時にCToolBarをカスタマイズ可能にする方法は?

Visual C++ 2.1の製品ドキュメントにある「CToolBarCtrl: Handling Customization Notifications」を読むとよいでしょう。

該当する個所を、ここに引用しておきます。

「Windowsツールバーのコモン コントロールには、カスタマイズ機能が組み込まれています。これには、ユーザーがツールバー ボタンの挿入、削除、再配置できるようにする、システムで定義されたカスタマイズ ダイアログ ボックスが含まれています。アプリケーションは、カスタマイズ機能が使用可能であるかどうか判断し、ユーザーがツールバーをカスタマイズできる範囲をコントロールします。これらのカスタマイズ機能は、CToolBarCtrlクラスでは使用できますが、現在のCToolBarクラスでは使用できません」

「ツールバーにCCS_ADJUSTABLEスタイルを指定することで、これらのカスタマイズ機能をユーザーが使えるようにできます。カスタマイズ機能によって、ユーザーはボタンを別の場所にドラッグしたり、ツールバーからボタンをドラッグしてはずすことで削除したりできます。また、ツールバーをダブルクリックして、Customize Toolbarダイアログ ボックスを表示させることができます。このダイアログでは、ツールバー ボタンの追加、削除、再配置ができます。アプリケーションはCustomizeメンバ関数を使用してダイアログ ボックスを表示することができます」

R. Rajendran(NetQuest)、76041.2245@compuserve.com、MSMFC Forum(1995年5月9日)
標準のMFC CToolBarをカスタマイズ可能にしたい場合は、CompuServeのMSMFCライブラリからCUSBAR.ZIPがダウンロードできます。このパッケージには、CCustomToolbarと実行時にカスタマイズ可能なツールバーの実装、そして、必要なユーザー ツール(ビットマップ化されたリスト ボックス用のコードを含むカスタマイズ ダイアログ ボックス)が含まれています。フリーウェアです。

Patrick Philippot、CSERVEの電子メールにて(1995年8月3日)

ツールバーやステータス バーを消す方法は?

次のコードを使えば、任意のビュー(たとえば、上であなたが説明しているOnViewStatusBar()メソッドなど)にあるステータス バーが消せます。


if( ((CMainFrame*)GetParent())->m_wndToolBar.IsWindowVisible() )
{
    GetParent()->SendMessage(WM_COMMAND, ID_VIEW_TOOLBAR, 0L);
}
if( ((CMainFrame*)GetParent())->m_wndStatusBar.IsWindowVisible() )
{
    GetParent()->SendMessage(WM_COMMAND, ID_VIEW_STATUS_BAR, 0L);
}
バーを表示するには、SendMessageのlParamに0Lの代わりに1Lを指定します。

JKBenjamin@aol.com、mfc-lにて(1995年5月16日)

ダイアログ内にツールバー/ステータスバーを作成する方法は?

このFAQのセクション6.3.5を参照してください。

IEで提供された新しいコントロールをMFCがサポートしていないわけは?

Office 97アプリケーションのようなツールバーがMFCにないわけは?

Visual C++のIDEのようなコマンド バー メニューをMFCがサポートしていないわけは?

Microsoft Internet Explorerで提供された新しいユーザー インターフェイスのスタイルは、まだベータ バージョンのCOMCTL32.DLLによって実装されています。ベータ版のインポート ライブラリやDLL、ドキュメントなどは、Microsoft ActiveX SDK(Internet Explorer 3.01対応)に含まれています。Microsoft ActiveX SDKのドキュメントは、MSDNライブラリのSDK Documentation binにあります。SDKそのものは http://msdn.microsoft.com/default.asp からダウンロードできます。コントロールは未だにベータなので、MFCではコントロールをサポートしていません。COMCTL32.DLLを含むActiveX SDKの最終的なバージョンが入手できるようになったら、新しいユーザー インターフェイスのスタイルをサポートするMFCのバージョンも間もなく登場するでしょう。

IDEおよびOfficeアプリケーションは、COMCTL32.DLLで実装されているコントロールを使用しません。従って、システムの実装ライブラリのバージョンには依存しません。

mikeblas@microsoft.com
メモ:
Internet ExplorerのコントロールはRebarと呼ばれています(MSJに、Rebarに関するとてもよい記事がありました)。Rebarは、Internet Explorer 3と4スタイルのツールバーを実装する必要がある場合にだけ役立ちます。Visual C++ 5.xやMicrosoft Office 97のツールバーはCommandバーと呼ばれ、Rebarとは異なります。Commandバーはrebarコントロールではありません。その違いは? Rebarコントロールは「スライド」させることができます。

Rebarコントロールはカスタマイズできません(私の知るところでは)。

Commandバーには、クールなフローティング メニューがあります。(Rebarコントロールも最終的にはこうなるでしょう) Commandバーの方が軽く動作します。

Commandバーには透過色の背景がありません。

CommandバーはOffice97/Developer Studioの内部で実装されています。開発者がこれを流用することはできません。Rebarは、コモン コントロールDLLの一部として、利用できるようになります。

両方とも、「クール」か「フラット」な外観で、これらの上をマウス ポインタが移動すると立体的に表示されます。

Rebarコントロールは、垂直にドッキングできないと思います。

Redbarを試すには、ActiveX SDKに付いてくるCOMMCTRL.HとCOMCTL32.LIBファイルが必要です。

また、詳細については http://www.microsoft.com/win32dev/ui/ から入手できるREBARサンプルと、記事「Rebar: It Isn't Just for Concrete Anymore」を参照してください。

参考
Stingray Softwareでは、Office97/Developer Studioスタイルのコマンド バーをObjective Toolkitで実装しました。http://www.stingsoft.com/ で、デモをチェックしてください。今現在、StingrayのObjective Toolkitでは現在のところツールバーしか動作しませんが、将来のリリースではメニューも完全に動作させることを約束します!

メニュー

MDIアプリケーション内のメニュー バーへのポインタを取得する方法は?

質問:MDIアプリケーションを書いているのですが、実際のメニュー バーへのポインタを取得できなくて困っています。通常の方法はMDIでは通用しないようです。


CMenu *menu;menu = GetMenu()->GetSubMenu(0);

メニューを更新したいのですが、どうすればメニュー バーへのポインタを取得できるのでしょうか?

答え:


AfxGetApp()->m_pMainWnd->GetMenu()->GetSubMenu(n);
mlinar@pollux.usc.edu(Mitch Mlinar)(1995年6月8日)

マウスの右クリックにポップアップ メニューを実装する方法は?


///////////////////////////////////////////////////////////////////
// WM_RBUTTONDOWN ハンドラ。

//
// このメッセージをトラップして、Button Properties ポップアップ メニューを表示します。

// メインのフレームがポップアップ メニュー メッセージを受け取ります。これによって、
// ステータス バーが更新され、ヘルプ テキストが表示されます。

///////////////////////////////////////////////////////////////////

void CAppButton::OnRButtonDown(UINT flags, CPoint point)
{
    CMenu menu;
    CMenu *submenu;
    // メニューをロード
    menu.LoadMenu(IDR_LAUNCH);
    // ポップアップ メニューを取得
    submenu = menu.GetSubMenu(0);
    // スクリーン座標を変換
    ClientToScreen(&point);
    // メニューをポストする
    submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
            point.x,    point.y,
            AfxGetApp()->m_pMainWnd,NULL);
}
johnm@unipalm.co.uk, programmer.win32(1995年7月12日)
代わりにRBUTTONUPを使うと、もっとよいでしょう。ですがダイアログ コントロール上で右クリックしても、RBUTTONUPとRBUTTONDOWNのメッセージは発生しません。

この状況も同様に扱うことが必要な場合は、プログラムは、Windows 3.xまたはWindows NTではWM_PARENTNOTIFYメッセージをキャッチし、Windows 95ではWM_CONTEXTMENUをキャッチしなければなりません。以下にコードを示します。


// ダイアログの場合もある
BEGIN_MESSAGE_MAP(CMyPropertyPage, CPropertyPage)
    // {{AFX_MSG_MAP(CMyPropertyPage)
    ON_WM_RBUTTONUP()
    ON_WM_PARENTNOTIFY()
    ON_MESSAGE(WM_CONTEXTMENU, OnContextMenu)
    // }}AFX_MSG_MAP
END_MESSAGE_MAP()
void CMyPropertyPage::OnRButtonUp(UINT nFlags, CPoint point)=20
{
    PopupMenu (&point);
}
void CMyPropertyPage::OnParentNotify(UINT message, LPARAM lParam)
{
    if (message !=3D WM_RBUTTONDOWN)
        CPropertyPage::OnParentNotify(message, lParam);
    else
    {
        CPoint pt(LOWORD(lParam),HIWORD(lParam));
        PopupMenu (&pt);
    }
}
LONG CMyPropertyPage::OnContextMenu (UINT wParam, LONG lParam)
{
    CPoint pt(LOWORD(lParam),HIWORD(lParam));
    ScreenToClient (&pt);
    PopupMenu (&pt);
    return 0;
}
// *****************************************************************
void CMyPropertyPage::PopupMenu(CPoint* pt)
{
    ASSERT(m_idContextMenu !=3D 0);
    ASSERT(nSubMenu >=3D 0);
    ClientToScreen (pt);
    CMenu FloatingMenu;
    VERIFY(FloatingMenu.LoadMenu(ID_POPUP_MENU));
    CMenu* pPopupMenu =3D FloatingMenu.GetSubMenu (0);
    ASSERT(pPopupMenu !=3D NULL);
    pPopupMenu->TrackPopupMenu (TPM_LEFTALIGN | TPM_RIGHTBUTTON,=20
                                pt->x, pt->y, this);
}
PaulACost@msn.com、電子メールにて(1995年10月15日)
メインフレームのメニューを動的に変更する方法は?
CMenu newMenu;
newMenu.LoadMenu (IDR_MENU1);
AfxGetMainWnd()->SetMenu( &newMenu );
AfxGetMainWnd()->DrawMenuBar();
newMenu.Detach ();
Arun Rao、MSMFC(1995年6月27日)

ウィンドウの生成/破棄にメニューを「アタッチ」する方法は?

編集者のメモ(Scot Wingo)
オリジナルの質問はダイアログに関するものでしたが、このコードはメニューを変更する必要のある、あらゆる種類のウィンドウに適用できます。

次のコードは、その方法1つを示します。

ダイアログ クラスのうちの1つで、変数CMenu pNewMenuを宣言します。

次に示すように、ダイアログ クラスの中でWM_INITDIALOGとWM_CLOSEメッセージを処理してください。


BOOL CMydlg::OnInitDialog()

{
    CDialog::OnInitDialog();
    // IDR_MYFRAME をロード
    pNewMenu = new CMenu;
    pNewMenu->LoadMenu(IDR_MYFRAME);
    // メイン フレームのメニューをメイン フレームにセット
    ((CMainFrame *)AfxGetMainWnd())->SetMenu(pNewMenu);
    return TRUE;
}
そして

void CMydlg::OnClose()
{
    // これまでの Hmenu ハンドルをオブジェクトからデタッチする。

    pNewMenu->Detach();
    pNewMenu->LoadMenu(IDR_MAINFRAME);
    // メイン フレームのメニューを復元。

    ((CMainFrame *)AfxGetMainWnd())->SetMenu(pNewMenu);
    CDialog::OnClose();
}
ダイアログを閉じる別の方法(ダイアログのボタンをクリックすることで閉じるなど)がある場合は、上記のOnCloseハンドラのコードをボタン クリック ハンドラに入れる必要があります。

Sanjeev Kumar、MSMFC(1995年6月23日)

Windowsコモン コントロール(別名Windows 95コントロール)

Windows NTやWin32sでこれらのコントロールは使用できますか?

Windows NT 3.50では、このコモン コントロールはサポートしていません。今後もサポートはされないでしょう。コモン コントロールを使うならWindows NT version 3.51を使わなければなりません。

blaszczak@BIX.com、mfc-l(1995年7月6日)
Win32sのバージョン1.30は、コモン コントロールをサポートしています。

すごくクールなコントロールのデモがどこかにありませんか?

MFCサンプルのFIREをチェックしてみてください。FIREを見れば、ほとんどのコントロールの動作がわかるでしょう。ファイル ロケーションはMSVC20\samples\mfc\fireです。バージョン4.0では、もう1つ別のものもあります。CMNCTRLSと言います。

Scot Wingo、scot@stingsoft.com(1995年7月27日)

CListCtlのためにNM_DBLCLKを処理する方法は?


BEGIN_MESSAGE_MAP(CListView, CView)
    ON_NOTIFY( NM_DBLCLK,ID_LISTCTRL,OnDblClick )
END_MESSAGE_MAP()
void CListView::OnDblClick(NMHDR* /*k*/, LRESULT* /*j*/)
{
    int nItem, nFlags;
    char szTest[80];
    nFlags = LVNI_SELECTED;
    nItem = m_ListCtrl->GetNextItem(-1, nFlags );
    if ( nItem != -1 )
    {
        sprintf( szTest, "Selected Item %d", nItem);
        AfxMessageBox(szTest);
    }
}
spolyak@interaccess.com, mfc-l(1995年7月21日)

CTreeCtrlは複数のセレクションをサポートしていますか?

していません。ごめんなさい!(この答えは見飽きた!)
Scot Wingo, scot@stingsoft.com
Microsoft Systems Journalの1994年7月号をご覧になれるなら、TreeViewについて広く考察しているので参照してください。ドラッグ&ドロップを含むサンプル プログラムも含まれています。
[MSDN 編集部のメモ:これは、Microsoft Systems Journal 1994年第9号のLibraryの、Books and Periodicalにあります。]
Windows 95の開発をしているのなら、シェアウェア版の実装は必要ありません。MFCにそのためのクラスがあります。

steven@primenet.com

CTreeCtrlでツリー表示のノードを開いたとき、視覚的なフィードバックがありません。どうすればよいのでしょうか?

TVS_SHOWSELALWAYSスタイルでコントロールを作成します。

kfreeman@viewlogic.com

ツリー コントロールに、複数選択やToolTips、編集可能なノード、および複数カラムを実装する方法は?

残念ながら、ツリー コントロールはバイナリ形式のDLLで実装されているので、さほど拡張性は高くなく、それらが提供する機能しか使えません。これらの機能を「ハック」することに挑戦する記事も見かけましたが、確実には動作しません。

Stingray Softwareは、Objective Toolkitでこれらすべての機能とプラスαが実装されている、代替のドロップイン型ツリー コントロールを提供しています。 http://www.stingsoft.com/ にデモがあります。

Scot Wingo、scot@stingsoft.com

CsplitterWndに関するFAQ

このFAQには、CSplitterWnd関連の質問をたくさん加えたいと思っています。あなたの抱えるCSplitterに関する難題をmfc_faq@stingsoft.comまで電子メールでお送りください。

参考:
George Shepherdと私の共著、「MFC Internals」では章を1つ丸ごとこの問題に割いています。CSplitterWndには、たくさんの興味深い内部機能、欠点、奇妙な動作などがあって、個人的にはお気に入りのMFCクラスなのです。

Dr. Dobbs Journalにも、スプリッタを水平から垂直に取り換える方法を説明した記事を書きました。 http://www.ddj.com にコードがあると思いますので、興味のある方はご覧ください。

Scot Wingo、scot@stingsoft.com

ビュー、ドキュメント、およびフレーム クラスに関する質問

Views
ドキュメント

ビュー

ビューのサイズを設定する方法は?

通常は、MoveWindow()を呼び出すことでウインドウのサイズを変えられます。Microsoft Foundation Class(MFC)Libraryを使って開発されたアプリケーションでは、ビュー ウィンドウはビューを囲んでいるフレーム ウィンドウの子ウィンドウになります。ビュー ウィンドウのサイズを変更するためには、GetParentFrame()を呼び出してビューのフレーム ウィンドウへのポインタを取得し、その後、MoveWindow()を呼び出して親のサイズを変更します。親フレーム ウィンドウがサイズを変更すると、親フレームに合わせてビュー ウィンドウのサイズが自動的に変更されます。

Visual C++ Knowledge Base記事Q109039(1994年6月4日)

CFormViewのサイズを設定する方法は?

Visual C++ Knowledge Base記事Q98598の「Using CFormView in SDI and MDI Applications」に長い答えがあるので参照してください。基本的には、CFormViewから派生クラスでOnInitialUpdate()をオーバーライドしなければなりません。同記事では、CFormViewから派生させることについて、その他の詳細も述べられています。

Visual C++ Knowledge Base記事Q98598(1995年6月7日)
ビューのClikethisView宣言内で以下のようにします。


virtual void OnInitialUpdate();
ClikethisViewのコード内で以下のようにします。


void ClikethisView::OnInitialUpdate()
{
    // ウィンドウのサイズをメイン ダイアログと同じにする。

    CFormView::OnInitialUpdate();
    GetParentFrame()->RecalcLayout();
    ResizeParentToFit( /*FALSE*/ );
}
andyr@gate.net, programmer.misc(1995年8月11日)

ドキュメント テンプレートのある新しいビューの使用法は?

AppWizardで作成されたアプリケーションならば、2つの選択肢があります。現在のビューの派生元を変更するか、新しいビューを作成して、それをオリジナルのビューと一緒にMDIアプリケーションの中で使用します。

新しいビューを作成するにはClassWizardを使用し、CViewから派生された新しいクラスを作成します。クラスを作成したら、新しいビューを使う手順と、AppWizardによって提供されたビューを変更する手順は同じです。

  1. ビュー クラスのヘッダ ファイルを修正して、CViewに対する参照をすべて、派生させた新しいビュー クラスの名前に変えます。この例では、クラスはCScrollViewから派生させています。通常、このステップは以下に示すように、ビュー クラスの派生元のクラス変更を伴います。

    
    class CMyView : public CScrollView
    
  2. ビュー クラスの実装ファイルを修正して、CViewに対する参照をすべて、派生させた新しいビュー クラスの名前に変えます。これには、以下に示すようなIMPLEMENT_DYNCREATE行の変更が必要です。

    
    IMPLEMENT_DYNCREATE(CMyView, CScrollView)
    
    BEGIN_MESSAGE_MAPを以下に示すように変更します。

    
    BEGIN_MESSAGE_MAP(CMyView, CScrollView)
    
    そして、CViewへの他の参照をすべてCScrollViewに変更します。

  3. App Wizardで作成されたビューを修正している場合は、それ以上の修正は必要ありません。新しいビューを作成している場合は、CWinApp::InitInstance()関数内でAddDocTemplate()呼び出しを探し出します。AddDocTemplate()の3番目のパラメータは、RUNTIME_CLASS(CSomeView)です。現在のビューを新しいビュー クラスで置き換えるには、CSomeViewをCMyViewに変更します。MDIアプリケーションでは、RUNTIME_CLASS(CSomeView)の代わりにRUNTIME_CLASS(CMyView)を指定した2つめのAddDocTemplate()を追加すれば、複数のビュー型が使用できます。

詳細については、Knowledge Base記事Q99562「Switching Views in a Single Document Interface Program.」をご覧ください。

Visual C++ Knowledge Base記事Q99562(1995年6月7日)

ビューの背景色を変更する方法は?

CView、CFrameWnd、CWndオブジェクトなどの背景色を変更するためには、WM_ERASEBKGNDメッセージを処理します。以下のコードを参考にしてください。


BOOL CSampleView::OnEraseBkgnd(CDC* pDC)
{
    // ブラシを使いたい背景色に設定する。

    CBrush backBrush(RGB(255, 128, 128));
    // 古いブラシを保存する。

    CBrush* pOldBrush = pDC->SelectObject(&backBrush);
    CRect rect;
    pDC->GetClipBox(&rect);     // 対象領域を消去。

    pDC->PatBlt(rect.left, rect.top, rect.Width(),
    rect.Height(), PATCOPY);
    pDC->SelectObject(pOldBrush);
    return TRUE;
}
私は以下のようにして、この問題を解決しました。


HBRUSH dlgtest::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    switch (nCtlColor)
    {
        case CTLCOLOR_BTN:
        case CTLCOLOR_STATIC:
        {
            pDC->SetBkMode(TRANSPARENT);
        }
        case CTLCOLOR_DLG:
        {
            CBrush*     back_brush;
            COLORREF    color;
            color = (COLORREF) GetSysColor(COLOR_BTNFACE);
            back_brush = new CBrush(color);
            return (HBRUSH) (back_brush->m_hObject);
        }
    }
    return(CFormView::OnCtlColor(pDC, pWnd, nCtlColor));
}
Tim、tfiner@vrli.com、電子メールにて(1995年9月10日)

カレント ビューを取得する方法は?

最善策は、ビューをパラメータとして渡すことです。これが適切でない場合でも、それが現在アクティブなドキュメント、現在アクティブなビューだということがわかっているなら、ビューは取得できます。詳細については、Visual C++ Knowledge Base記事Q108587の「Get Current CDocument or CView from Anywhere.」を参照してください。

要約すると


    ((CFrameWnd*) AfxGetApp()->m_pMainWnd))->GetActiveDocument()
それに

    ((CFrameWnd*)(AfxGetApp()->m_pMainWnd))->GetActiveView()
を使ってドキュメントとビューを取得します。この2つをstatic関数としてCMyDocおよびCMyView内でラップし、正しいRUNTIME_CLASSかどうかチェックするとよいでしょう。

ただし、ビューが現在アクティブでない場合や、OLEをインプレースで実行可能な場合、これは正常に動作しません。

null@diku.dk、programmer.misc(1995年6月8日)

1つのドキュメントに対して複数のビューを作成する方法は?

CDocTemplate:CreateNewFrame()関数を使えば、MFCを使って書かれたMDIアプリケーションで、ドキュメントに追加のビューを作成できます。この関数を呼び出すためには、CDocumentオブジェクト(この関数が新しくビューを作成する対象となるドキュメント)へのポインタと、複製するためのプロパティを持つフレーム ウィンドウへのポインタを指定します。普通、この関数の2番目のパラメータはNULLです。

アプリケーションがCreateNewFrame()を呼び出すと、この関数は新しいフレーム ウィンドウと、その中に新しいビューを生成します。フレーム ウィンドウの種類とビューの種類は、CreateNewFrame()呼び出しで指定されたドキュメントに関連付けられているドキュメント テンプレート(CDocTemplate)によって異なります。

Visual C++に付属するMFCのサンプル アプリケーションCHKBOOKも、ドキュメントに追加のフレームとビューを作成する方法を示します。CHKBOOK.CPP、CChkBookApp::OpenDocumentfile()関数をチェックしましょう。

もう1つのCreateNeFrame()の使用例は、MULTVIEWサンプル アプリケーションです。

CreateNewFrame()は、フレームとビューの両方を生成します。ビューだけではありません。何らかの理由から、CreateNewFrame()が自分の要求に合わないようでも、CreateNewFrame()のソース コードを見れば、フレームとビューを生成するために必要な手続きのよい参考になるでしょう。

Visual C++ Knowledge Base記事Q100993にいくらかの変更を加えました(1995年6月25日)

MDIアプリケーション内のすべてのビューを取得する方法は?

文書化されていない関数をいくつか使用しなければなりません。

  • CDocument::GetFirstViewPosition(); // DOCCORE.CPP
  • CDocument::GetNextView(); // DOCCORE.CPP
  • CMultiDocTemplate::GetFirstDocPosition(); // DOCMULTI.CPP
  • CMultiDocTemplate::GetNextDoc(); // DOCMULTI.CPP
さらに、CWinAppのm_templateListメンバもいじる必要があります。

blaszczak@Bix.com、mfc-l(1995年7月11日)
メモ
この問題はMFCバージョン4.0で変更されています。現在は、すべてのビューとドキュメントのアクセスできる、CDocManagerという新しいクラスがあります。詳細については、MFC Internalsで調べてみてください。

Scot Wingo、scot@stingsoft.com

CScrollViewを「マウスでスクロールできる」ようにする方法は?

CISにあるMSMFCライブラリから、AUTOSV.LZHをダウンロードしてください。このコードは、マウスの動作を扱う2次メッセージ ループの実装方法を示します。コードをカスタマイズするためのフックも用意されています。フリーウェアです。

Patrick Philippot、CISの電子メール(1995年8月3日)

ドキュメント

ドキュメント/ビュー アーキテクチャを使用しなければなりませんか?

MFCはドキュメント/ビューの使用を強制していません。HELLO、MDI、HELLOAPPサンプルをチェックしてみてください。ドキュメント/ビューはまったく使用されていません。MFCの機能の大部分は、ドキュメント/ビューを使わないアプリケーションでも使用できます。ただし、ドキュメント/ビューを使わなかった場合には、印刷プレビューや多くのOLE機能などが利用できません。

Scot Wingo、scot@stingsoft.com(1995年6月7日)

カレント ドキュメントを取得する方法は?

詳細については「カレント ビューを取得する方法は?」を参照してください。.

ドキュメントが破棄されるタイミングは?

SDIアプリケーションでは、アプリケーションの終了時にドキュメントが削除されます。MDIアプリケーションでは、ドキュメントの最後のビューが閉じられた時にドキュメントが削除されます。したがって、ドキュメントにSDI/MDIの互換性を保たせるためには、デストラクタではなく、仮想関数DeleteContents()でドキュメントのデータを削除しなければなりません。

Richard Hazenberg、drmcode@euronet.nl、programmer.misc(1995年6月24日)

複数ドキュメントを作成する方法は?

ドキュメントの種類を増やして、そのサポートを加えるためには、CWinApp派生オブジェクトでCMultiDocTemplateオブジェクトの作成、登録ができます。このテクニックは、MULTDOCSサンプル アプリケーションで説明されています。MFCアプリケーションで扱うドキュメントの種類を追加するために必要な一般的な手順を以下に示します。

  1. AppWizardを使用して、新しいドキュメント クラスと新しいビュー クラスを作成します。

  2. Resource Editorを使用して、新しいドキュメント クラスをサポートするための新しいリソース文字列を追加します。ドキュメント テンプレートの文字列リソースの形式についての詳細は、トピック「How to Interpret a Document Template String.」を参照してください。

  3. Resource Editorを使用して、アプリケーションにアイコンとメニュー リソースを追加します。各リソースのIDは、手順2で作成したドキュメント テンプレート文字列に使用するリソースIDと同じでなければなりません。このIDは、追加するドキュメントの種類に関連付けられているリソースを識別するために、CMultiDocTemplateクラスによって使用されます。

  4. アプリケーションのInitInstance()関数の中でMultiDocTemplateオブジェクトをもう1つ作成し、CWinApp::AddDocTemplate()関数で登録します。以下の例を参照してください。


CMultiDocTemplate* pDocTemplate2 = new CMultiDocTemplate(
IDR_DOC2TYPE, RUNTIME_CLASS(CDoc2),
RUNTIME_CLASS(CMDIChildWnd), RUNTIME_CLASS(CView2));
AddDocTemplate(pDocTemplate2);
そして最後に、カスタム シリアライゼーションと描画コードを新しいドキュメントとビュー クラスに加えます。

MS FAQ(1995年6月25日)

開いているドキュメントのリストを取得する方法は?

以下に示すコードでは、CDocTemplateオブジェクトを使用して作成したすべてのCDocumetに対するポインタのリストを取得する方法を示します。

下記のコードでは、CMyAppはCWinAppから派生しています。変数m_templateListは、CWinAppのメンバであるCPtrListオブジェクトであり、すべてのドキュメント テンプレート(CDocTemplate)へのポインタのリストが格納されています。CDocTemplate関数のGetFirstDocPosition()とGetNextDoc()は、ドキュメントのリストを巡回して各ドキュメント テンプレートを探し出すために使います。


void CMyApp::GetDocumentList(CObList * pDocList)
{
    ASSERT(pDocList->IsEmpty());
    POSITION pos = m_templateList.GetHeadPosition();
    while (pos)
    {
        CDocTemplate* pTemplate =
            (CDocTemplate*)m_templateList.GetNext(pos);
        POSITION pos2 = pTemplate->GetFirstDocPosition();
        while (pos2)
        {
            CDocument * pDocument;
            if ((pDocument=pTemplate->GetNextDoc(pos2)) != NULL)
                pDocList->AddHead(pDocument);
        }
    }
}
リファレンス マニュアルやオンライン ヘルプに文書として記載されていないCDocTemplateクラスのpublicメンバ関数が2つあります。ですが、これらはCDocTemplateクラスで定義されたpublicメンバ関数であり、開いているドキュメントのリストを巡回するために使える簡単な機能を提供します。これらの関数には、以下に示す機能があります。

関数 virtual POSITION GetFirstDocPosition() const;
注釈 テンプレートに関連付けられた、開いているドキュメントのリストから最初のドキュメントの位置を取得するために呼び出す。
戻り値 POSITIONの値。これはGetNextDocメンバ関数を使った繰り返し処理で使用できる。
関数 virtual CDocument* GetNextDoc(POSITION& rPosition) const;
rPosition GetNextDocまたはGetFirstDocPositionメンバ関数への呼び出しによって返されたPOSITIONの値への参照。この値はNULLにしてはいけない。
注釈 この関数を呼び出して、すべてのドキュメント テンプレートの開いているドキュメントを巡回して調べる。関数はrPositionで識別するドキュメントを返した後、リスト中の次のドキュメントのPOSITION値をrPositionにセットする。リスト中の最後のドキュメントを取り出したときには、rPositionにはNULLにセットされる。
戻り値 rPositionで識別されるビューへのポインタ。

Visual C++ Knowledge Base記事Q106455(1995年6月25日)
これが有効なのはMFCのバージョン3.2以前だけです。MFCバージョン4.0の場合は、以下のようになります。


void CMyApp::DoSomethingToAllDocs()
{
    CObList  pDocList;
    POSITION pos = GetFirstDocTemplatePosition();
    while(pos)
    {
        CDocTemplate* pTemplate = GetNextDocTemplate(pos);
        POSITION pos2 = pTemplate->GetFirstDocPosition();
        while(pos2)
        {
            CDocument* pDocument;
            if(pDocument = pTemplate->GetNextDoc(pos2))
                pDocList.AddHead(pDocument);
        }
    }
    if(!pDocList.IsEmpty()){
        pos = pDocList.GetHeadPosition();
    while(pos)
    {
        // ドキュメントごとに CDocument 関数を呼び出す。

        ( (CDocument*)pDocList.GetNext(pos) )
            ->UpdateAllViews(NULL);
    }
}
mcontest@universal.com、電子メールにて(1995年9月22日)

起動時にアプリケーションが新しいドキュメントを作らないようにする方法は?

アプリケーションのInitInstance内にあるProcessShellCommandを呼び出す直前に以下の呼び出しを加えます。


cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing
lechner-cos1@kaman.com、電子メールにて(1996年1月6日)

OLEクラスに関する質問

Storage/Compoundファイルの構造化
OLEコントロール(以前はOCXと呼ばれていました)

構造化ストレージ/複合ファイル

アップグレードしたら、古いバージョンのMFCで生成したファイルが読み込めなくなりました。どうすればよいでしょうか?

簡単な方法がたくさんあると思いますが、私が以前行ったのは次の方法です。

VERSIONABLE_SCHEMAとGetObjectSchemaを使ってバージョンを特定します。古いバージョンの場合は、OLEバージョン1.0形式で格納されています。この形式を読み込むには、まずWORDとCString(型と名前の情報)を読み込みます。次は、OLEデータ自体です(これが難しい)。次のようにします。

  1. StgCreateDocfileを使用して、一時的な複合ファイルを作成します。

  2. OleConvertOLESTREAMToIStorageを使用して、データをOLEバージョン2.0形式に変換して複合ファイルにコピーします。

  3. IStorageを使用してOleLoadを呼び出し、IUnknownのポインタを取得します。次にQueryInterfaceを呼び出してCOleClientItem::m_lpObjectのメンバをセットします。そして、m_nDrawAspectに対してDVASPECT_CONTENTをセットします。

  4. IUnknownポインタとIStorageポインタを解放します。

  5. OleConvertOLESTREAMToIStorageのコードにはOLESTREAMの実装が必要です。Visual C++ 1.0のMFCのコードからコピーします。

これは確実に動作します。スキーマ変数に関わる若干の問題があります。また、OLE 1/MFC形式ファイルの書き出し実装していません。リクエストがあればコードを公開します。

Ron Jones、ronjones@xnet.com、programmer.toolsにて(1995年5月9日)

OLEコントロール(以前はOCXと呼ばれていました)

OLEコントロールとは何ですか?

OLEコントロールは、16ビット対応のVBXコントロールの後継者となる32ビット対応のコントロールです。単純なDLLに格納されいて、関数インターフェイスを提供する従来のVBXコントロールとは異なり、OLEコントロールは、OLEオートメーションに依存しています。そのおかげで、VBXよりも柔軟性が高く、使いやすいインターフェイスになるでしょう。

OLEコントロールはOLEを使用しますが、オブジェクト指向ではありません。OLEコントロールには、アクセスしなければならない単純なプロパティのセットがあり、継承、ポリモーフィズムなどのオブジェクト指向技術は使えません。個人的な意見ですが、これは、オブジェクト指向技術を使用しているMFCプログラマをかなり苛立たせると思います。

Scot Wingo、scot@stingsoft.com(1995年6月25日)

OLEコントロールを記述する方法は?

Microsoft は VC++ 2.x でOLE CDK(OLE Control Developer's Kit)をリリースしました。このキットとツールを使えば、OLEコントロールが書けます。Visual C++ 4.0では、もうこれらは別々にはなっていないので、現在はControl Wizardを実行するだけになっています。詳細については、Visual C++のドキュメントを参照してください。

Scot Wingo、scot@stingsoft.com(1995年6月25日)

OLEコントロール コンテナをサポートするMFCのバージョンは?

MFC 4.0がOLEコントロール コンテナをサポートしています。MFC 4.0以前のバージョンでは、自分で作らない限りOLEコントロール コンテナをサポートしません(いやですね!)

インプレース編集時にアプリケーション固有のコントロール バーを消す方法は?

ツールバーのCreate()にCBRS_HIDE_INPLACEを加えます。

例:


m_wndFormBar.Create(this,
    WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_HIDE_INPLACE,IDW_FORMBAR)
Paul Rony、102615.601@compuserve.com

WOSAクラスに関する質問

CRecordSet
WinSock

CRecordSet

CRecordSetクラスを加えると、リンカ エラーが山ほど発生してしまいます。

プロジェクト作成時に、AppWizardに対してデータベース サポート機能を使うように指定しないと、AppWizardはデータベース用のヘッダをインクルードしてくれません。stdafx.hに「#include」を加えれば、CRecordSetはエラーにならなくなります。それから、リンカ オプションの設定をするウィンドウを開き、「odbc」ライブラリを加えます。これで、山のようなリンカ エラーは避けられます。

Wolfgang Bessler、bessler@highland.swb.de、comp.lang.c++(1995年6月5日) CRecordSet::Find()が必要です。なにをすればよいのでしょうか? SQLでWHERE句を使用します。

mikeblas@interserv.com、mfc-l(1995年10月10日)

WinSock

CSocketがブロックするので困っています。どうなっているのでしょう?

私は、Connect()を呼び出した後にポップ アップするダイアログを作成して、この問題を回避しました。以下に例を示します。


CMySocket MySocket;
MySocket.Create();
MySocket.Connect("mysmtphost",25);
DummyDlg DummyDialog;
MySocket.m_pDialog=&DummyDialog;
DummyDialog.DoModal();
これで、MySocketがDummyDialogにWM_CLOSEメッセージを送るまで、スレッドを停止しておけます。次のようにできればよいのですが、

MySocket.m_hEvent=CreateEvent(...);
WaitForSingleObject(MySocket.m_hEvent,INFINITE);
こちらはMySocketの処理をすべて停止させてしまいます。

Cynthia Jennings(idlewild@is.net)、programmer.win32(1995年6月19日)

DLLとビルドに関する質問

拡張DLLとユーザーDLLに関する質問がある場合は、MFC3.1 Technical Noteの11と33を必ず読んでください。そのドキュメントのVolume 2にも、さらに詳細な情報があります。最後に、Books Onlineで、DLLHUSK(拡張DLL)やDLLTRACE(ユーザーDLL)という名前のサンプルを検索してみてください。

DLLにCWinAppオブジェクトは必要ですか?

Microsoft Foundation Class Libraryは2種類のDLLをサポートします。_USRDLLと_AFXDLLです。_USRDLLモデルでは、DLLが使用するMicrosoft Foundation Class LibraryのWindowsクラスの初期化とクリーンアップを実行するCWinAppオブジェクトが1つなければなりません。この要求事項は、MFC Tech Note 11で説明されています。DLLTRACEサンプルには、CWinAppオブジェクトを含む _USRDLLがあります。

_AFXDLLは、CWinAppオブジェクトを必要としません。_AFXDLLは、Microsoft Foundation Class Libraryのクラスをアプリケーションと共有するので、初期化とクリーンアップをするCWinAppは必要ないのです。その代わりに、_AFXDLLには特別なバージョンのLibMain()とDLLの初期化関数が必要です。

Visual C++ Knowledge Base記事Q109031(1995年6月4日)

MFC DLLではWEPをどのように定義するべきでしょうか?

Microsoft Foundation Class Library version 2.0でビルドしたダイナミックリンク ライブラリ(DLL)では、USRDLLモデルはCのランタイム ライブラリで提供されているWEP()(Windows exit procedure)関数を使用します。コードがCライブラリのWEP()関数を使用するので、DLL内のスタティックとグローバルなオブジェクトのためのデストラクタが呼び出され、DLLアプリケーション オブジェクトに対してはCWinApp::ExitInstance()関数が呼び出されます。

Visual C++ Knowledge Base記事Q98374とTechnical Notes 11と33を参照してください。

Visual C++ Knowledge Base記事Q98374(1995年6月7日)

「拡張DLL」をビルドする方法は?

  1. 32-bitの拡張DLLをビルドするときには、コンパイラのコマンド ラインで_AFXEXTを定義します。AFXVER_.Hの中を調べれば、これによって_AFXDLLも強制的に定義されることがわかります。したがって、「AFXEXT」DLLはAFDLLになります。

  2. AFXDLLが定義されるとき、AfxGetResourceHandleは.EXE、拡張DLL、MFC DLLによって共有されるMFCのグローバルなデータに格納されている値を返します。返されたハンドルは、リソースを探すときに最初に検索されるモジュールを示します。

    (検索の順序について知りたいなら、AfxFindResourceHandle()のソース コードを参照してください)

  3. 厳密に言うと、私たちがリソースをロードするために必要としているものは、インスタンスのハンドルというよりもモジュールのハンドルなのです(インスタンスは、モジュール、つまりコードとリソースを共有しますが、持っているデータが違います)。DLLのモジュール ハンドルは、.EXEのハンドルと異なります。

  4. ::GetModuleHandleが使ってDLLのハンドルを取得したら、それをAfxSetResourceHandleに渡します。これで、リソースを探すときに、あなたのDLLが最初に探されます。ただし、検索対象モジュールから.EXEモジュールは除外されるので注意してください。AfxGetResourceHandleから返されたハンドルをどこかに保存しておいてからAfxSetResourceHandleを呼び出し、DLLリソースのロードが終わったら、元に戻すとよいでしょう。

Charlie Kester、Microsoft Developer Support、MSMFC(1995年7月19日)

リソース専用DLLでリソースを管理しながら、ClassWizardの恩恵は得るにはどうすればよいでしょう?

以下の文書は、CompuServeのMSMFCライブラリから、RESDLL.ZIPという名前のファイルとして入手できます(Visual C++ version 2.0が対象です)。

「How To Manage An MFC Project Storing Ints Resource Into A Resource-Only DLL」
プロジェクトのリソースをリソース専用のDLLに格納していれば、ソフトウェアのローカライゼーションはたいへん楽になります。また、プロジェクトのリソースをDLLに格納しておくとよい場合はほかにも数多くあります。

ですが、このプロジェクトがMFCプロジェクトなら、これを行うと重大な問題が起きます。リソースが別のプロジェクトとして管理されるので、ClassWizardの機能の恩恵が選られなくなります。

ただし、プロジェクトを標準のプロジェクト用に開発しながら、リソース専用DLLのモデルにすばやく切り替えられるようにするワザがあります。次のようにします。

方法その1

  1. AppWizardを使って通常のようにプロジェクトを作成します(TESTという名前だとしましょう)。

  2. プロジェクトを閉じ、新しいDLLプロジェクトを同じディレクトリに作成します(RESDLLという名前だとしましょう)。[Create]ボタンをクリックすると、Visual C++はファイルを追加するためのダイアログを開きます。この機会を利用して、新しいプロジェクトに前のプロジェクト(TEST.RC)のリソース ファイルを加えます。

  3. テスト プロジェクトのリソースをリソース専用DLLとしてコンパイルできるようにするには、リンカに対して/NOENTRYオプションを指定しなければなりません。残念ながらVisual C++の[Settings]ダイアログ ボックスでは、これが簡単にできません。

    メイン メニューから[Project]>[Settings]を選びます。

    [Link]タブをクリックします。

    [Category]コンボ ボックスから[General]を選びます。

    [Object/Library Modules]フィールドで、.LIBファイルに対するすべての参照を削除して(役に立ちませんから)、/NOENTRYを加えます。このオプションは、[Common Options]表示領域に現れるはずです。

    [OK]をクリックしてコンパイルします。これで、プロジェクトのリソースだけを含んでいるDLLができます。

  4. TEST.MAKプロジェクトを開いてはいけません。その代わりに、TEST.MAKをプロジェクト ディレクトリの中でTEST_RES.MAKにコピーします。

  5. TEST_RES.MAKを開き、TEST.RCをプロジェクト ファイルから削除します。

  6. [Project]>[Settings]を選んで[General]タブをクリックし、「USE_RESDLL」を[Preprocessor Definitions]のリストに加えます。

  7. TEST.Hを開き、次のようにCTestAppのクラス宜言を変更します。

    
    public:
    CTestApp();
    #ifdef USE_RESDLL
    public:
    virtual int ExitInstance();
    private:
    HINSTANCE m_hInstDLL;
    #endif // USE_RESDLL
    
  8. TEST.CPPを開き、CTestApp::InitInstanceを次のように変更します。また、新しく宣言されたExitInstanceメンバ関数を加えます。
    
    BOOL CTestApp::InitInstance()
    {
    // 標準の初期化。
    
    // これらの機能を使っておらず、実行ファイルの最終的なサイズを小さくしたければ、
    // 以下から、必要のない初期化ルーチンを削除してください。
    
    #ifdef USE_RESDLL
    if ((m_hInstDLL = LoadLibrary("resdll.dll")) == NULL)
    {
    return FALSE; // ローカライズされたリソースのロード失敗。
    
    }
    else
    {
    AfxSetResourceHandle(m_hInstDLL);
    // DLL からリソースを取得。
    
    }
    #endif
    // ....
    #ifdef USE_RESDLL
    int CTestApp::ExitInstance()
    {
    FreeLibrary(m_hInstDLL);
    return CWinApp::ExitInstance();
    }
    #endif
    
  9. コンパイルします。TEST_RES.EXEは何の問題もなく動いて、DLLからリソースをロードするはずです。

  10. プロジェクトを閉じて、TEST.MAKを開きます。コンパイルします。TEST.EXEも、問題なく動くはずです。ただし、今度はリソースは.EXEファイルからロードされます。これは、このプロジェクトではUSE_RESDLLを定義しなかったからです。

警告
あるモデルから別のモデルへ切り替えるときには、すべて再ビルドするか、TEST.CPPを修正しなければなりません。さもないと、問題が起きてしまいます。

これで、リソースを変更したり、TEST.MAKで作業をするときにClassWizardを使ったりできます。リソース専用DLL版のプロジェクトをビルドするためには、TEST_RES.MAKに切り替えるだけです。ただし、リソースに変更を加えた場合には、RESDLL(RESDLL.MAK)を再コンパイルしてから、切り替えます。

簡単です!

方法その2

  1. 上記手順1、2、3を実施します。

  2. 新しいプロジェクトを作成する代わりに、TEST.RCをRESDLL.RCにコピーします。プロジェクトからTEST.RCを削除し、RESDLL.RCを追加します。別の名前を付けたほうが安全です。

  3. RESDLL.RCをダブルクリックして、.CLWファイルを再ビルドさせます。

  4. 「Exclude File from Build」コマンドをRESDLL.RCに対して指定します([Project]>[Settings]で開いた[General]ページにある「Exclude File From Build」)。

  5. TEST.CLWとTEST.RCを削除します。

これで、TESTとRESDLLの両方を、ClassWizardを介して使えます。RESDLL.RCの中で定義されたリソースは、TEST.EXEには加えられません。この方法のほうが簡単ですが、両方の場合(DLLのリソースを使う場合と、.EXEファイルにリンクされたリソースを使う場合)でのプログラムのテストができません。私は開発をしているときは、後者の方法を使います。GPFが発生しても、独立しているDLLについて悩む必要がないからです(Windows NT上では問題のうちに入りませんけど)。

リソースを変更したときは、プログラムをテストする前に必ずDLLを再コンパイルすることを忘れないでください。当然ながら[Tools]メニューに、NMAKEを呼び出すバッチ ファイルをインストールしておくほうが、プロジェクトからプロジェクトへ切り替えるよりも速いでしょう。しかし、その場合「New Target」機能はなんの助けにもなりません。

プロジェクトに複数の独立したターゲットを入れられるBorland C++と同じ機能がVisual C++にあると、きっとすばらしいでしょうね。そうすれば、開発者はDLLと.EXEの作成を同じプロジェクトから管理できるのですから。

Patrick Philippot、CISの電子メールにて(1995年8月3日)

エクスポート/インポートと拡張DLLで問題が起きています。役に立つ情報のありかを知らないですか?

これは非常に複雑な問題です。この問題について、Visual C++ Developer's Journalの「MFC Internals」コラムにGeorge Shepardと私の書いた記事があります。 http://www.vcdj.com でご覧になれます。また、私達の本『MFC Internals』の第9章で、MFCとDLL(拡張と標準)について詳しく解説しています。

Scot Wingo、scot@stingsoft.com

MFCに関するヒント、秘訣、注意点

C言語で作ったWindowsアプリケーションをMFCに変換する最善の方法は?

MicrosoftとSymantecは共同でC言語で作ったWindowsアプリケーションをMFCへ移行するのに役立つツールを開発しました。このツールはMFC Migration Kitという名前で、Visual C++ 2.x CD-ROMのMFCKITディレクトリにあります。このキットはMicrosoft Software Libraryと、Symantecからも入手できます。

MFCアプリケーションの動作が遅いわけは?

MFCアプリケーションは何の問題もなく、速く動くはずです。デバッグ版をビルドしていないこと、そしてトレース オプション(MFCTRACE.EXE)を無効にしていることを確認してください。それでもアプリケーションが遅いままの場合は、簡単なプロファイリングを実行して、冗長な呼び出しを行っていないか調べましょう。

Scot Wingo、scot@stingsoft.com(1995年5月31日)

最大化された状態で起動するアプリケーションを作成する方法は?

アプリケーションを新しく作るのなら、AppWizardのステップ4で作成できます。ステップ4で[Advanced]を選び、[Main Frame]タブを選びます。[Maximized]オプションをチェックします。

MDIアプリケーションの場合は、CWinApp::InitInstance()関数でCWinApp::m_nCmdShowに対してSW_SHOWMAXIMIZEDを指定してからpMainFrame->ShowWindow(m_nCmdShow)を呼び出します。AppWizardによって生成されたアプリケーションでは、コードは次のようになります。


// メイン MDI Frame ウィンドウを作成。

CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
    return FALSE;

m_nCmdShow = SW_SHOWMAXIMIZED;  // この行を追加!

pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
m_pMainWnd = pMainFrame;
SDIアプリケーションでは、CWinApp::InitInstance()関数でCWinApp::m_nCmdShowに対してSW_SHOWMAXIMIZEDを指定してからOnFileNew()を呼び出します。たとえば、AppWizardによって生成されたアプリケーションでは、コードは次のようになります。


m_nCmdShow = SW_SHOWMAXIMIZED;
// 新しい(空の)ドキュメントを作成。

OnFileNew();
Visual C++ Knowledge Base記事Q109039(1995年6月4日)

アプリケーション中でTRACEマクロを有効にする方法は?

Visual C++ 1.0を使用している場合は、Microsoft Visual C++プログラム グループに含まれているTRACERアプリケーションを実行します(「MFC TRACE Options」という名前のアイコンです)。[Enable Tracing]を選び、次に[OK]を選びます。

Microsoft C/C++ 7.0を使用している場合は、AFX.INIファイルをMicrosoft Foundation Class Libraryのソース ディレクトリ(標準ではC:\C700MFCSRC)からWindowsディレクトリ(標準ではC:\WINDOWS)へコピーする必要があります。このファイルには、次のようなセクションがあるはずです。


[Diagnostics]
   TraceEnabled = 1
   TraceFlags = 0
TraceEnabledが1にセットされていれば、トレーシングは有効になります。

このAFX.INIファイルは、C/C++ 7.0とVisual C++ 1.0の両方で同じです。Visual C++ 2.xでは、デバッガで実行するとTRACE出力は自動的に[Debug]ウィンドウに出力されます。

Visual C++ Knowledge Base記事Q109039(1995年6月7日)

アプリケーションでバックグラウンド処理を実行する方法は?

アプリケーションの多くは、ユーザーがアプリケーションを操作していないような空き時間に「バックグラウンドで」長い処理を実行しています。Microsoft Windowsオペレーティング システム向けに開発されたアプリケーションでは、長い処理を多数の小さな断片に分割して、アプリケーションのバックグラウンド処理を実行できるようになっています。各断片処理の実行後、アプリケーションはPeekMessage()ループを使用してWindowsに実行の制御を渡します。

Microsoft Foundation Class Libraryを使って開発したアプリケーションは、ライブラリ コードのメイン メッセージ ループでPeekMessage()ループを使用するか、別のPeekMessage()ループをアプリケーションに埋め込むことによって、バックグランド処理を実行できます。

詳細についてはVisual C++ Knowledge Base Q99999を参照してください。

Visual C++ Knowledge Base記事Q99999(1995年6月7日)

別スレッドにメッセージを送る方法は?

別スレッドにメッセージを送るには、SendNotifyMessage()を使用してみてください。

null@diku.dk、programmer.miscにて(1995年6月18日)

Microsoftは自社製品にMFCを使用していますか?どの製品で使用していますか?

MFCを使って書かれたMicrosoftアプリケーションはたくさんあります。ときにはそれが一目ではわからないこともあります(少し例を挙げると、Bookshelf、Bob、WordArt OLEサーバー、Visual C++(当然ですね)、Windows 95のペイント、Windows 95のワードパッド、Windows 95のFAXソフトウェアの一部分、Windows 95のゲームなど...)。

今後は、MFCを使用したアプリケーションがもっと増えるでしょう。これらすべてを追跡する手段は知らないので、私が知らなかったり、憶えていなかったりするものもたくさんあるでしょう。WordやExcelがMFCを使うようになるとは思いません。なぜなら、WordやExcelには非常に多くの古いコードが残っていて、MFCに書き直したところでユーザーが何の恩恵も受けないと思われるからです。私が言っているのは、Microsoftは新しいコードでは必ずMFCを使用するということです。「旧」コードの中にも、将来のバージョンでMFCを利用するものが出てくるでしょう。

Dean McCrory、MSMFC(1995年6月8日)

MFCアプリケーションを1つのインスタンスに制限する方法は?

Microsoft C++のサンプルONETIMEを参照してください。

突き詰めれば、


const char* MyMainWndClassName = "MyMainWndXQW"
BOOL CMyApp::InitApplication()
{
    // 基本クラスを呼び出す。標準では何もしない。

    CWinApp::InitApplication();
    WNDCLASS wndcls;
    // NULL を標準として設定。

    memset(&wndcls, 0, sizeof(WNDCLASS));
    // 標準のウィンドウ クラスのクラス情報を取得。

    ::GetClassInfo(AfxGetInstanceHandle(),"AfxFrameOrView",&wndcls);
    // 新しいクラスを独自のクラス名で置きかえる。

    wndcls.lpszClassName = MyMainWndClassName;
    // 新しいクラスを登録し、リザルト コードを返す。

    return ::RegisterClass(&wndcls);
}
それに:

BOOL CMyApp::FirstInstance()
{
    CWnd *PrevCWnd, *ChildCWnd;
    // 同じクラス名を持つ別のウィンドウが存在するか調べる。

    PrevCWnd = CWnd::FindWindow(MyMainWndClassName, NULL);
    if (PrevCWnd != NULL)
    {
        // あったら、ポップアップを持っているか調べる。

        ChildCWnd=PrevCWnd->GetLastActivePopup();
        // メイン ウィンドウを最前面に出す。

        PrevCWnd->BringWindowToTop();
        // 最小化されているのならメイン ウィンドウを元のサイズに戻す。

        if (PrevCWnd->IsIconic())
            PrevCWnd->ShowWindow(SW_RESTORE);
        // ポップアップがあるならそれらも前に持ってくる!
        if (PrevCWnd != ChildCWnd)
            ChildCWnd->BringWindowToTop();
        // FALSE を返す。これが初めてのインスタンスではなく、
        // すでに開いているのをアクティブにした。

        return FALSE;
    }
    else
        // 初めてのインスタンスなので、平常どおり処理をする。

        return TRUE;
}
CMyApp::InitInstance()
{
    if (!FirstInstance())
        return FALSE;
    // ...
}
null@diku.dk、programmer.tools(1995年6月19日)
また、Win32 SDK Knowledge Base記事Q124134(「Allowing Only One Application Instance on Win32s」)と、Jeffrey Richterによる『Advanced Windows NT』の第7章、「Prohibiting Multiple Instances of an Application from Running: The MultInst Sample Application」(MSDN Library CDにも収録されています)も参照してください。

null@diku.dk、電子メールにて(1995年8月8日)
改訂-以下はmfc-lにポストされたものです。

私は各InitApplication()に、セマフォを作らせています。GetLastError()がERROR_ALREADY_EXISTSを返したら、別のアプリケーションがすでに実行中であることがわかるので、そこで処理をやめて抜け出します。


Yourapp::InitInstance()
{
    hMutexOneInstance =
    CreateMutex(NULL,TRUE,_T("PreventSecondInstance"));
    if(GetLastError() == ERROR_ALREADY_EXISTS)
        bFound = TRUE;
    if(hMutexOneInstance)
        ReleaseMutex(hMutexOneInstance);
    return (bFound == TRUE) ? FALSE : TRUE;
}
mcontest@universal.com
Jeffrey Richterによる『Advanced Windows NT』には、これについてよく書かれたセクションがあります。要点は、プロセス間の共用データ セグメントを使うという方法です。

  1. メインのファイルに、以下を加えます。

    
    #pragma data_seg(".SharedData")
    LONG nUsageCount = -1;
    #pragma data_seg()
    
  2. アプリケーションのInitInstance()で、以下を呼びだします。

    
    InterlockedIncrement ( &nUsageCount );
    
    この関数は、変数のインクリメント値を返します。値がゼロ以外なら、最初のアプリケーションでないことがわかります。

  3. アプリケーションのExitInstance()で、以下を呼びだします。

    
    InterlockedDecrement( &nUsageCount );
    
  4. .DEFファイルに以下の行を入れます(ここで指定するセグメント名は、アプリケーションのメイン ファイルにあるものと同じでなければなりません)。


SEGMENTS
    .SharedData shared
abalakri@us.oracle.com
組み込みの同期化手法を使うほうがよいでしょう。Win32 Knowledge Base記事Q124134「Allowing Only One Application Instance on Win32s」を参照してください。同期化のためにメモリマップされたファイルを使用する方法のサンプルがあります。サンプルでは以前のインスタンスを開始する方法は示されていませんが、初めて実行しているアプリケーションでないことがわかるのなら、解決は簡単です。CreateFileMappingが失敗したら、ウィンドウ クラス名から以前のインスタンスを突き止めます。見つからないときは、少しスリープしてからやり直します(CreateFileMappingから)。これにより、ほかのインスタンスがウィンドウを作成しているところに遭遇するか、CreateFileMappingが成功します。CreateObjectの代わりにCreateFileMappingを使用するとよいのは、CreateFileMappingがWin32s上でも動作するからです。

nuj@kruger.dk
メモ
このサンプルがMFC FAQアーカイブの中にあります。onetime4.zipです。サンプルはjohn@jing.com(John Xu)から寄稿されました。

Win32プラットホーム上のレジストリをMFCアプリで使用する方法は?

CWinAppクラスでSetRegistryKey("MyCompany")を呼び出して、文字列(一般には、あなたの会社名)を渡すだけです。この文字列は、レジストリのどこにデータを格納するかを定義します:HKEY_CURRENT_USER\Software\MyCompany\\\ これを呼び出したあとは、通常のWriteProfilexxx()ルーチンを使えば、データは.INIファイルの代わりにレジストリへ格納されます。確実で、簡単に動作します!
Brett Robichaud、brett_robichaud@tallysys.com programmer.win32(1995年6月23日)

プログラムの中でMFCアプリケーションを終了させる方法は?

MFCは、秩序立ててアプリケーションを終了させるpublic関数を提供していません。この問題に対処する1つの手段は、次のような関数をアプリケーション中に作成することです。


void ExitApp()
{
    // メイン ウィンドウのウィンドウ メニューをダブルクリックするのと同じ。

    ASSERT(AfxGetApp()->m_pMainWnd != NULL);
    AfxGetApp()->m_pMainWnd->SendMessage(WM_CLOSE);
}
ご覧の通り、これはグローバル関数として実装されており、アプリケーションのどこからでも呼び出せます。この関数は、単にアプリケーションのメインフレーム ウィンドウにWM_CLOSEメッセージを送っているだけです。そして、これによってアプリケーションが秩序正しくシャットダウンされます。

MFCバージョン2.5、またはそれ以降を使用している場合は、MFCの新しいグローバル関数(「AfxGetMainWnd」)を利用してコードを単純化できます。


void ExitMFCApp()
{
    // メイン ウィンドウのウィンドウ メニューをダブルクリックするのと同じ。

    ASSERT(AfxGetMainWnd() != NULL);
    AfxGetMainWnd()->SendMessage(WM_CLOSE);
}
メモ
ドキュメントのデータを変更したら、必ずCDocument::SetModifiedFlag()を呼びだしてください。そうすれば、シャットダウンの前に、フレームワークは確実にユーザーに対してデータを保存するように促します。シャットダウン処理をより綿密に制御したければ、CDocument::SaveModified()をオーバライドすることができます。

Visual C++ Knowledge Base記事Q117320(1995年6月25日)

IMPLEMENT_DYNAMIC、IMPLEMENT_DYNCREATE、IMPLEMENT_SERIALの違いは?

IMPLEMENT_DYNAMICは実行時の型情報を提供し、IsKindOfやGetRuntimeClassのようなマクロをサポートします。

IMPLEMENT_DYNCREATEは、MFCが実行時に型を作成できる機能を追加します。このマクロは、ファイルにシリアライズされる、あらゆる具体的なデータ型で必要になります。

IMPLEMENT_SERIALもクラスにバージョン番号を提供し、ファイルから型を読むための>>演算子を使用できる機能を追加します。

たとえば、派生クラスDogがIMPLEMENT_DYNCREATEを使用し、基本クラスAnimalがIMPLEMENT_SERIALを使用しているなら、Dogはどちらへのポインタでも書き込みができますが、読み込みはAnimalへのポインタでしかできません。

MFC 3.xは、IMPLEMENT_SERIALと一緒に使える定数VERSIONABLE_SCHEMAを提供して、同時に複数のバージョンのスキーマをサポートします。ただし、MFCの実装は壊れていて、実行時に失敗します。

jimb@turningpoint.com、電子メールにて(1995年7月11日)
メモ
バージョン対応スキーマが依然として壊れているというのは間違いです。これはMFC 4.0で修正済みです。

MSDN編集部より
かつてバージョン対応スキーマは、仕様どおりには機能しませんでした。しかし、この問題はMFC version 4.0で修正済みです。

抽象基本クラスをIMPLEMENT_SERIALであると宣言する方法は?

次のような特殊な形式のIMPLEMENT_SERIALが必要です。通常のDECLARE_SERIALを使用しますが、IMPLEMENT_SERIALの代わりに以下に示すIMPLEMENT_SERIAL_ABCを使用します。


#define IMPLEMENT_SERIAL_ABC(class_name, base_class_name, wSchema)
_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, NULL)
CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)
{
    pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name));
        return ar;
}
匿名

afx.inlとかafxwin1.inlなどは、何のファイルですか?

これらのファイルは\msvcxx\mfc\includeディレクトリにあり、インライン関数を含んでいます。これらの関数はデバッグ用でない(_DEBUGが定義されていない)MFCのビルドの場合だけ「インライン」になります。関数の前には、_AFX_INLINEという特別な命令が置かれ、これがデバッグ用でないビルドでは"inline"に変換され、デバッグ用ビルドのときは " に変換されます。

MFCがこれを行うのは、デバッグ モードで関数の中までデバッグできる一方で、リリース ビルドのときにはインラインの利点が得られるからです。リリース ビルド モードではライブラリがとても小さくなる理由の1つです。

Scot Wingo、scot@stingsoft.com(1995年7月20日)

MFCはMacintoshで使用できますか?

できます!Microsoftは、Visual C++ cross-platform editionというものを提供しています。この製品で68000系のMac用ソフトをクロスコンパイルすることができます。また、PowerPCバージョンも、まもなく出るでしょう。詳細については、直接Microsoftへお問い合わせください。

Scot Wingo、scot@stingsoft.com(1995年7月27日)

MFCはOS/2で使用できますか?

できません。

MFCを学ぶ方法は?学習の開始方法は?

この質問は、何度もみかけました。まず始めに、このFAQのセクション2.4.2をチェックしてください-書物はいつでも役に立ちます[MSDN編集部より この情報については、WebにあるこのFAQのオリジナル バージョンを参照してください]。もう1つ気を付けなければならない点は、基礎になるWindows APIを知り、理解していなければ、MFCを本格的に使うことはできないという点です。したがって、Windows APIの概要から始めるとよい人もいるでしょう(Petzoldによる本は、定番と言えます)。少なくともWindowsが何をして、何ができるか感覚としてつかみましょう。

ほかにヒントとして、あまりウィザードに依存しないほうがよいということが挙げられます-ウィザードは確かによいのですが、初心者を保護しすぎる場合があるのです。AppWizardやClassWizardの実行結果を調べてみたり、mainfrm.cppをデバッガで追ってみたりしましょう。ウィザードの魔法で生成されたコードが何をしているのかを理解するまでは、コードが一体何をしているのかわからないでしょう。

『Writing Windows Applications with MFC』という本は、Petzoldの本とMFCの入門書の特徴を兼ね備えたよい本だと思います。『Inside Visual C++』の内容は、Windowsをあまり知らない人にとっては、進行が速すぎるかもしれません。

Scot Wingo、scot@stingsoft.com(1995年7月27日)
お金を払うしかありません。

しかし残念ながらMFCは、支払いを終えるようにはできていないんです(はははは!!!)

raymond@btw.com、mfc-l(1995年7月23日)
Visual C++の『User's Guide』から始めて、「Scribble Tutorial」まで進んでみてください。Visual C++とMFCの紹介と、これらを組み合わせて使う方法が解説されています。次はMSDNのCD-ROMです。数多くの記事があり、基礎的な概念の理解を助けてくれます。MFCとArchitectureを検索してみてください。MFC Encyclopediaの記事も読むとよいでしょう(どちらかという言えば「ハウツー」ものです)。そして、MFC Technical Noteには、必ず慣れ親しむ必要があります。また、これはヒントですが、学習を始めたばかりなら32ビット関係を追うとよいでしょう。さらに、世の中には役に立つきわめて優れた雑誌も数多くあります。

billb@microsoft.com、mfc-l(1995年7月23日)
重要なポイント(私の考えで)は、次の通りです。

あなたがWindowsのプログラマなら、グラフィックスについて心配する必要はありません。CDCのメンバ関数は、Windowsの関数をほとんど直接カプセル化したものだからです。またCWndは、ほとんどのウィンドウ操作関数をカプセル化します。

ドキュメント/ビュー アーキテクチャを学習しましょう:ドキュメント テンプレート、アプリケーション間の対話、ドキュメントとビュー(AfxGetApp、GetDocument、UpdateAllViewsなど)などです。

シリアライゼーションも学びましょう(Scribbleは、入門としてよいでしょう)。ダイアログ ボックスのためのDDXとDDVについては、よい技術解説記事があります。WM_COMMANDハンドラとCOMMAND_UIハンドラは、メニュー項目を有効化/無効化できるようにするほか、メニュー項目に対してチェック マークやラジオ ボタン マークを適用できるようにします。また、ステータス バーを使うための方法も1つ提供します。

これらをすべて学ぶ間にも、ClassWizardを使用する方法と、それがどのようにEditorやResource Editorに統合されるかもわかります。ことによると、MFC FAQの一部として、学習すべきトピックのリスト(重要性の高いものから順番に)を作成することができるかも?[もちろんです。これもよいスタートじゃないですか?-Scot]

grimes@parallax.co.uk、mfc-l(1995年7月24日)

自分のMFCアプリと一緒に配布しなければならないDLLは?

Visual C++のCDにあるREDISTRB.WRIのファイルをもう一度チェックします。このファイルは、異なるアプリケーションの種類ごとに、どのファイルが必要か説明しています。また、WindowsファイルとMFCファイルについても、何をするべきか説明しています。

blaszczak@BIX.com、programmer.tools(1995年7月9日)
dumpbin/imports myapp.exe dll_one.dll dll_two.dll | grep-i dllを実行して、見つけたDLLを調べてみましょう。ただし、LoadLibrary()によってロードされるDLLは見つかりません。

null@diku.dk、programmer.tools(1995年7月10日)

WM_SETTEXTメッセージをインターセプトする方法は?

MFCは、WM_SETTEXTをオーバライドする一般的な手段を提供していないので、メッセージ マップでOnMessage(WM_SETTEXT, OnSetText)を使用し、それから、自分のメソッドを定義します。


....LRESULT CMyClass::OnSetText(wParam, lParam);
    // ...
jfriend@collabra.com、programmer.tools(1995年8月17日)

DECLARE_DYNCREATEのせいでインスタンスが作成できません!

質問:ClassWizardはクラスを生成してくれましたが、DECLARE_DYNCREATE(...)を使用してしまい、コンストラクタをprotectedとして宣言しています。クラスのインスタンスを作成しようとすると、コンパイラ エラーが出てしまうのです。


error C2248: 'CChkTbl::~CChkTbl'が'CChkTbl'クラスで宣言されたプロテクトされたメンバにアクセスできません
答:ClassWizardがこうするは、通常はフレームワークがインスタンス化を処理しているためです。つまり、これがCViewの派生クラスなら、通常は標準のOnFileNew()の間、またはCxxxDocTemplate->OpenDocumentFile()か類似した何かを呼び出すときに、CDocumentTemplateがビューをインスタンス化をします。フレームワークがこれをするのは、あなたが自分でうっかりインスタンス化しようとしたときにエラー メッセージを返せるようにするためです。CDocTemplateフレームワークの外で、実際にインスタンス化を行う必要がある場合は、コンストラクタをpublicに変えるだけで実現できます。

chucks@skypoint.com、programmer.tools(1995年8月12日)
答:DECLARE_DYNCREATEマクロが「protected:」命令を置いて、それをそのままにするからというのが答えです。DECLARE_DYNCREATEの後にあるものも「protected」でなければなりません。さもなければ、必要に応じて「public:」または「private:」と宣言しなければなりません。

duane@anasazi.com、電子メールにて(1995年8月15日)

いつも見かける_T()とは?

_Tは、Unicodeに対応するために、文字列リテラルを拡張するマクロです。

Mike Oliver、MSMFC(1995年8月1日)

CMemoryStateの使用法は?

MFC 2.0以降では、組み込みの診断機能が提供されています。したがって、CMemoryStateを明示的にアプリケーションに含める必要はありません。MFC 2.0のデバッグ ライブラリは、自動的にメモリ リーク検出を実行します。検出コードはAFXMEM.CPPにあります。このコードは、アプリケーションが動的にオブジェクトを割り当て、プログラムが終了する前にオブジェクトの削除を失敗するのを検出します。

実のところ、CMemoryStateは正しく機能しないことがあります。詳細については、Visual C++ Knowledge Base記事Q99022「Foundation Class Debug Library Detects Memory Leaks」を参照してください。

#define new DEBUG_NEWは、すべての.CPPソース ファイルで定義されなければなりません。また、以下のコードをアプリケーションのInitInstanceに挿入します。


#ifdef _DEBUG
    afxMemDF |= checkAlwaysMemDF;
#endif
詳細については、Visual C++ Knowledge Base記事Q117326「Foundation Classes Common Asserts, Causes and Solutions」を参照してください。

Muniraju(NetQuest)、MSMFC(1995年8月1日)

自分で登録したメッセージの処理方法は?

編集部より(Scot Wingo)
このFAQでは、Deanが、WM_CHKTBLTOGGLEを処理しようとしているある人にその処理方法を教えています。教えを受けている人物は、以前、間違った方法で処理をしていました-教育のため、それも収録しておくことにしました。

ON_MESSAGEを使用する:


    ON_MESSAGE(WM_CHKTBLTOGGLE, OnChkTblToggle)
クラス定義では:

    afx_msg LRESULT OnChkTblToggle(WPARAM wParam, LPARAM lParam);
メッセージ マップでは:

#define ON_WM_CHKTBLTOGGLE()
{
WM_CHKTBLTOGGLE, 0, AfxSig_vwp, (AFX_PMSG)(AFX_PMSGW)(BOOL
(AFX_MSG_CALL CWnd::*)(BYTE, BYTE))OnChkTblToggle
},
コードでは:

LRESULT CMyView::OnChkTblToggle(WPARAM wParam, LPARAM lParam)
{
    // TODO: 自分のコードをここに書く。

}
MFC対しては、関数を次のように言言しました。


    void CMYView::OnChkTblToggle(UINT, CPoint)
これが、AfxSig_vwpというシグネチャが意味するところであり、あなたが望んでいるものとは、明らかに違います。

ON_MESSAGEとON_REGISTERED_MESSAGEは、メッセージ ハンドラを拡張して独自のメッセージ ハンドラを作れるようにすることを目的としています。特定のAfxSig_*の値やメッセージ マップ構造には依存しないでください-これらは警告なしに変わることがあります。

Dean McCrory、mfc-l(1995年8月19日)

MFCのアイドル時間処理をカスタマイズする方法は?

MFCのアイドル処理は頻繁すぎると思います。そして残念ながら、これは変更できません。と言うのも、これを変更すると、思わぬところでアプリケーションを壊す可能性があるからです。しかし・・・私たちはアプリケーションが「アイドルに入る」時間をカスタマイズする方法をコードに組み込みました。その秘密ですか?CWinThread:IsIdleMessageですよ! 以下は、マウスとタイマー メッセージのアイドル処理を無効にした例です。


BOOL CMyApp::IsIdleMessage(MSG* pMsg)
{
    if (!CWinApp::IsIdleMessage(pMsg))
    return FALSE;
    if ((pMsg->message >= WM_MOUSEFIRST &&
        pMsg->message <= WM_MOUSELAST) ||
        (pMsg->message >= WM_NCMOUSEMOVE &&
        pMsg->message <= WM_NCMBUTTONDBLCLK))
            return FALSE;
    if (pMsg->message == WM_TIMER)
        return FALSE;
    // 上記以外の場合はアイドル処理を始める...
    return TRUE;
}
さて、ステータス バー上にの時計があって、それがペイン用のアップデート ハンドラへの呼び出すことをWM_TIMERに期待していたとしたらどうなるでしょう?ご推察のとおり、上記のコードのせいで機能しません。しかし、簡単に解決する方法があります。アイドル処理を強制的に開始する秘密の方法があるのです。WM_KICKIDLE(afxpriv.hで定義されます)を使う方法です。

タイマー ハンドラ(普通は派生されたCStatusBarクラスにあるハンドラ)に、次のような処理をするコードを追加します。


if (TimeIsDifferentEnough())
    PostMessage(WM_KICKIDLE);
CWinThread:IsIdleMessageを見るとわかるように、不要なOnIdle呼び出しを回避するために、マウスポインタが前にあった場所と同じ場所に移動しているかどうかなど、相当な処理をしています(なぜWindows NTがそんなことをするのか、わかりませんが、そうするのです。そして、そのたびにキャレットが点滅します)。私たちは上位互換性を保ちながら、できるだけのことをしているのです。

deanm@microsoft.com、電子メールにて

リサイズできない(スタティックで固定された)スプリッタ バーの作成方法は?

Visual C++ Knowledge Base記事Q105150「Preventing Static Splitter Bars from Tracking」を参照してください。これを行う方法が説明してあります。基本的には、CSplitterWndからのクラスを派生させて、左ボタンとマウス移動メッセージをオーバライドします。CSplitterWndのではなく、CWndの処理を呼び出すようにします。最後に、ID_WINDOW_SPLITコマンドを生成する可能性のあるメニュー項目を削除します。

bills@anest4.anest.ufl.edu、mfc-l(1995年9月28日)

Windows 95の流行のアプリケーションにある[What's this]メニューを自分のアプリケーションに加える方法は?

取っ掛かりのための手順をいくつか示します。

  1. 以下のメニューをリソース スクリプトに入れます。

    
    IDR_WHAT_IS_THIS_MENU    MENU    DISCARDABLE
    BEGIN
    BEGIN
    POPUP "a"
    BEGIN
    MENUITEM "What's this?", ID_WHAT_IS_THIS
    END
    END
    END
    
  2. ダイアログに右クリック ハンドラ(OnRButtonDown)を追加して、IDR_WHAT_IS_THIS_MENUメニューを指定します。次のようにして、最後のクリックの場所を何らかの変数に格納する必要があります。

    
    CPoint        m_cLastRClickPoint;
    
    そして、ここにクライアントの最後の右クリックを格納します。

  3. 以下のコードをダイアログ クラス(または、すべてのダイアログの親クラス)に入れます。

    
    BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
        // {{AFX_MSG_MAP(CMyDialog)
        // なにかの処理
        //}}
        ON_COMMAND(ID_WHAT_IS_THIS, OnWhatIsThis)
    END_MESSAGE_MAP()
    void CMyDialog::OnWhatIsThis()
    {
        CWnd* pControl = ChildWindowFromPoint (m_cLastRClickPoint);
        // いずれかのコントロールをクリックしたのでなければ、ダイアログのヘルプを開く。
    
        if (pControl == NULL || pControl->m_hWnd == m_hWnd)
            WinHelp (HID_BASE_RESOURCE + m_nIDHelp,
            HELP_CONTEXTPOPUP);
        else
            WinHelp (HID_BASE_CONTROL + pControl->GetDlgCtrlID(),
                HELP_CONTEXTPOPUP);
    }
    
    -そして、最後に以下の行をmakehelp.batファイルに加えます。

    
    echo. >>hlp\wr.hm
    echo // Controls (IDC_*) >>hlp\wr.hm
    makehm IDC_,HIDC_,0x50000 resource.h >>hlp\wr.hm
    
    これが、すべてをヘルプ システムに結び付けます。

Poul A. Costinsky、PoulACost@msn.com

Choose Fileダイアログの代わりにChoose Directoryダイアログを表示する方法は?


/* Windows 95 対応でないと動作しない */
if (afxData.bWin4)
{
    LPMALLOC pMalloc;
    /* Shell の標準のアロケータを取得 */
    if (::SHGetMalloc(&pMalloc) == NOERROR)
    {
        BROWSEINFO bi;
        char pszBuffer[MAX_PATH];
        LPITEMIDLIST pidl;
        // BROWSEINFO 構造体を埋める - 必要なところをすべて埋める。

        bi.hwndOwner = GetSafeHwnd();
        bi.pidlRoot = NULL;
        bi.pszDisplayName = pszBuffer;
        bi.lpszTitle = _T("Select a Starting Directory");
        bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
        bi.lpfn = NULL;
        bi.lParam = 0;
        // 次の呼び出しがダイアログ ボックスを表示する。

        if ((pidl = ::SHBrowseForFolder(&bi)) != NULL)
        {
            if (::SHGetPathFromIDList(pidl, pszBuffer))
            {
            // この時点で、pszBuffer に選択されたパスが入っている */.
                DoingSomethingUseful(pszBuffer);
            }
            // SHBrowseForFolder によって割り当てられた PIDL を解放
            pMalloc->Free(pidl);
        }
        // Shell のアロケータを解放
        pMalloc->Release();
    }
}
メモ
このコードはWindows 95上でのみ有効です-これはシェルの一部です。

bradw@netnet.net、mfc-l(1995年9月9日)

MFC 4.0とSTLを使っていて問題が生じています。何がいけないのでしょう?

STLヘッダをインクルードする前に、new.h(と、似たような理由からiostream.hも)をインクルードすることが秘訣です。例を示します。


#include <new.h>
#include <iostream.h>
namespace std {
#include <map.h>
}
dave_bickford@compuware.com、mfc-l
あるウィンドウを、常にほかのウィンドウより前面に置く方法は?

SetWindowPos(&wndTopMost,NULL,NULL,NULL,NULL,SWP_NOMOVE|SWP_NOSIZE)
(DBWIN と同じ方法)
lee@usa.nai.net、mfc-l(1995年1月19日)

あるウィンドウを別のウィンドウの前に移動させる方法は?

次のどちらかを呼び出します。


SetWindowPos(&wndTop,NULL,NULL,NULL,NULL,SWP_NOMOVE|SWP_NOSIZE)
または

BringWindowToFront();
lee@usa.nai.net、mfc-l(1995年1月19日)

MFCはなくなりませんか?

いいえ。オペレーティング システムが成長し、変わっていくにつれて、またユーザーの要求が大きくなり成熟するにつれて、MFCも成長し、変化し続けていきます。1997年の夏には、MFCチーム メンバの数人が、厳しい機密保持契約のもとにプレゼンテーションを行うことになっています。このプレゼンテーションでは、製品の次期バージョンの計画の一部も明らかにされるでしょう。製品の開発の方向性は、思いがけない緊急のニーズを満たすために変わることもありますが、製品は決してなくなったりはしません。

mikeblas@microsoft.com

ATLはMFCを置き換えますか?

いいえ。ATLとMFCは、2つのまったく異なる性質の問題を解決するために設計されているのです。

mikeblas@microsoft.com

Developer Studioのようなドッキング ウィンドウを実装する方法は?

MFCでは、これは簡単にはできません。問題は、ドックバー/コントロールバーのアーキテクチャが、ウィンドウではなく基本的なツールバーのために作られていることにあります。私たちはこの問題を、Objective Toolkit製品で解決しました。弊社のWebサイト http://www.stingsoft.com/ には、デモンストレーションとホワイト ペーパーがあります。チェックしてください!
Scot Wingo、scot@stingsoft.com

MFCの次のバージョンは[何か特別な機能]をサポートするでしょうか?

MFCチームは、時間とリソースの許すかぎりMFCの機能セットを増やしています。

何らかのAPIやシステム機能を直接使用するよりも、MFCを利用したほうが明らかに有利なものを、C++プログラマに提供できるとMFCチームが感じたときに、MFCに機能が追加されます。MFCチームは、Microsoftが発表する新しい戦略テクノロジのほかに、オペレーティング システムの機能も考慮しています。

残念なことですが、業界の中にはMFCがサポートするものやしないものを、無責任に予測し始めた人もいます。MFCの開発チームに属していない人が、MFCが特定の機能をサポートするかどうかを知っている、主張したとしたら、それは間違いです。知らないのですから。MFCチームのメンバが、特定の機能に対するサポートを計画していると言うなら、その時点では、たぶん正解でしょう。というのも、ソフトウェアの開発スケジュールは、あらゆる種類の思いがけないことで変更されたり、影響を受けたりするからです。チーム メンバの最善の努力にもかかわらず、特定の機能が製品の最新バージョンを含まれたり含まれなかったりするのです。

mikeblas@microsoft.com

ウィザードに関する質問

自分のアプリケーションに合わせて選んだAppWizardオプションを変更する方法は?

残念ですが、AppWizardではすでに作成、変更されたアプリケーションを、もう一度変更してはくれません。そのため、AppWizardを使って新しいスケルトン アプリケーションを作成する必要があります。そして、自分のコードをそこに結合するか、スケルトンとオリジナルを比較して、何が違うかを調べます。よいニュースもあります。OLEのサポート機能やMDIを追加したり削除したりするなら、ちょっといじるだけで済みます。ほとんどの場合は、CWinAppからの派生クラス、フレーム ウィンドウなどを変えるだけです。

Scot Wingo、scot@stingsoft.com(1995年5月31日)

Visual C++に関する質問

Windows 95に関する質問
コンフィギュレーションに関する質問
言語機能に関する質問
信ずるところに関する質問
Visual C++の高度な使い方のヒントと秘訣
Visual C++に関する様々な質問
Visual C++/MFC 4.0固有のヒント、問題、その他 ここに掲載されている質問などの内容をできるだけ一般化しようと思っているのですが、この種の質問はリリースにきわめて大きく依存しているため、対象となるもののバージョンできる限り含めました。

Windows 95に関する質問

Visual C++ 2.0を実行しているのですが、ダイアログがWindows 95のルック&フィールになりません。何が悪いのでしょうか?

Visual C++バージョン2.0では[Project/Options]ダイアログへ行き、[Linker]を選んでリンカのコマンドラインが/subsystem:windows,4.0を含むようにします。これは、Visual C++バージョン2.1では既定値になっています。

Chris Marriott、chris@chrism.demon.co.uk、programmer.misc(1995年5月25日)

Windows 95でコンパイルすると、DOSモードになったりそこから抜けたりします。

Windows 95とWindowsディレクトリにあるすべてのdosprmpt.pifファイルを削除するかリネームします。これでだめなら、すべての.pifファイルも同様にします。

Scot Wingo、scot@stingsoft.com(1995年5月25日)
\Win95\systemディレクトリにConagentのための.pifがあります。フル スクリーンにせずWindowスクリーン モードにするなら、この.pifファイルの設定を変えます。私の場合は、画面が一瞬点滅するのが1ヶ月も続いていたいのですが、この設定を変えたら魔法のように解決しました・・・。

RockyMoore@aol.com、電子メールにて(1995年7月16日)

Visual C++ 1.5か2.0をWindows 95で使用できますか?

どちらのバージョンも実行できます。Visual C++ version 1.5xは16ビット対応アプリケーションのみ作成できます。これらのアプリケーションはWindows 95上で実行できますが、Visual C++ version 2.0で作成できる真の32ビット対応アプリケーションのほうが望ましいでしょう。これらはWindows NTにもあてはまります。

Scot Wingo、scot@stingsoft.com(1995年6月5日)

コンフィギュレーションに関する質問

Visual C++に必要なメモリ容量は?

Visual C++ version 1.5.xは486クラスのマシンで最低4MBのRAMがあれば動作します。8MBあれば、もっと快適です。Visual C++ version 2.xの最低必要量は16MBです。Microsoftはパッケージで20MBを推奨しています。

メモ
16MB未満のメモリしかない場合は、どのオペレーティング システムでもVisual C++ 1.5はパフォーマンスが大きく(3倍に)低下します。Windows/DOS Developer's Journalの1995年5月号に、この件(とほかのパフォーマンス強化)に関する私の記事が載っています。
jimb@turningpoint.com、email電子メールにて(1995年7月11日)

Visual C++ 2.1の入手方法は?店頭で見つけられないのですが。

マイナー バージョンのアップグレード リリースはVisual C++サブスクリプションの一部です。店頭販売されているのはメジャー リリースだけですので、マイナー バージョンアップを入手するにはサブスクリプションを購入する必要があります。サブスクリプションはMicrosoftやProgrammer's Paradise、Programmer's Shopから入手できます。

Windows 95上で実行すべきでしょうか、NT上ですべきでしょうか?

人生は妥協の連続です。さて、ここでのトレードオフは、Windows 95上で実行するなら多くのメモリは必要ないけれど、このオペレーティング システムがWindows NTほど堅牢でないために、NTの場合に比べてクラッシュの回数は多くなる可能性があります。逆にWindows NT(完全な32ビットOS)上で実行するなら、クラッシュの回数は少ないでしょうが、メモリが必要になります。個人的にはWindows NTのほうが好きですね。Windows NTなら、メモリ問題をすべての捕らえて上手く解決し、しかもシステムを不安定にすることもないからです。

Scot Wingo、scot@stingsoft.com(1995年6月18日)

include/lib/exeディレクトリの設定方法は?

Visual C++ 2.0以前のバージョンでは、環境変数INCLUDE/LIBDIR/EXEDIRを使用していました。Visual C++バージョン2.0以降は、Visual C++ の[Tools]メニューで設定を使用しています。[Tools]メニューを選び[Options]を選んでください。ダイアログ ボックスが開くので[Directories]タブを選びます。ここで、IncludeディレクトリやLibraryディレクトリ、Executableディレクトリなどを設定できます。

Lee, Jin Woo、lee@sam1.info.samsung.co.kr、programmer.win32より(1995年6月10日)
プログラムがどれもコンパイルできません!たすけて! 質問:システムにVisual C++ 1.51をインストールしたばかりです。インストール自体はエラーもなく、スムーズに進みました。それなのに、プログラムがどれもコンパイルできないんです!最も単純で最も短いプログラムをコンパイルしようとしても、「Can't open the compilation response file」のエラー メッセージが出てしまいます。

答:この種のエラーの一般的な原因として、適切なディレクトリを指していないTMPまたはTEMP環境変数が存在していることが挙げられます。たとえば、AUTOEXEC.BATに、


set TMP=C:\TMP
と書いてあるのに、C:\TMPディレクトリがない場合などです。

chris@chrism.demon.co.uk、programmer.misc(1995年6月18日)

別のパスにあるソースを参照するようにプロジェクト設定できますか?

まずローカル ディレクトリを探索し、そこになければネットワーク ドライブを検索するようにしたいのですが。

Joe Kinsella、jkinsella@procd.com

MFC全体をブラウザできるファイルを作成する方法は?

MFCクラスとソース コードをブラウズできたら、いいですよね。最善の方法は、Microsoftから提供されている.BATファイル使うことでしょう。これはMicrosoft Software Libraryにあります。

BLDBSC15.BAT-Visual C++ 1.5用
BLDBSC20.BAT-Visual C++ 2.0用
BLDBSC21.BAT-Visual C++ 2.1用

基本的には、すべてのファイルについて、/Zsをオプションを指定してコンパイラを実行します。/Zsオプションは.objファイルを生成せずに.sbrファイルの生成することを意味します。次に、生成された.sbrファイルを対象にbscmake.exeを実行して.bscファイルを作成します。.sbrsと.objsの両方を作成したとしたら、時間がもっとかかります。

Scot Wingo、scot@stingsoft.com(1995年6月25日)
Visual C++ 2.1のCD-ROMの上にはビルド済みのブラウザ ファイルがあります。ただしこれはインストールされません。これは[File]>[Open]コマンドで、いつでもIDEへロードできます。

jimb@turningpoint.com、電子メールにて(1995年7月11日)
32ビット バージョンを使用しているなら、\msvc20\mfc\srcディレクトリのREADME.TXTファイルに詳細情報があります。README.TXTには、そのディレクトリへ移動してDOSプロンプトから以下を実行するよう書いてあります。


nmake DEBUG=1 BROWSEONLY=1
billb@microsoft.com, mfc-l(1995年7月17日)

言語機能に関する質問

Visual C++はテンプレートと例外処理をサポートしていますか?

32ビット対応Visual C++ 2.0は、テンプレートと例外処理が導入されています。Visual C++ 2.0の16ビット対応バージョンでは、これらのC++機能はサポートされていません。

大事なことですが、16ビット版でのTHROWとCATCHの実装は破綻しています。16ビット版では実行時にスタックがクリーンアップされないのです。

jimb@turningpoint.com、電子メールにて(1995年7月11日)

Visual C++はStandard Template Libraryをサポートしていますか?

はい!詳しくは「MFC 4.0とSTLを使用していて、問題が出ています。何がいけないのでしょう?」を参照してください。このサポートは、バージョン4.0で追加されました。

Visual C++はRTTIをサポートしていますか?

はい、バージョン4.0でサポートしています。

Scot Wingo、scot@stingsoft.com(1995年6月7日)

信ずるところに関する質問

OWLとMFC、Borland CとMicrosoft Visual C++。これらはどちらのほうがよいのですか?

この質問は、いつもUsenet上にあがっています。答は、まさにあなたの何をしたいのかに左右されます。もしあなたの要求しているものが、テンプレートやRTTIと言った最新のC++機能なら、通常これらの機能を最初に取り入れているのがBorland C++ですから、こちらということになります。しかし、注意してください。BorlandがOWL 2から3へ(1から2のときも?)バージョン アップしたとき、アプリケーション開発者は彼らのアプリケーションを、すべて書き直さなければならなかったのです。MFCバージョン1.0で書かれたアプリケーションは、現在の、より新しいバージョンのMFCでも動作しています!最善の方法は、各ベンダから機能を入手して、自分の状況に一番適している製品を選ぶことでしょう。

Scot Wingo、scot@stingsoft.com、comp.lang.c++(1995年6月8日)

Visual C++とVisual Basic、どちらを使えばいいのでしょう?

(よく似た、おもしろい投稿を見たのですが・・・。)さて、原理は同じなのです(インターフェイスの作成、イベントに反応するコードの追加、役に立つことをするコードの追加など)。ですがVisual Basicでのプログラミングが子供用自転車に乗るようなものだとすれば、C++でのプログラミングは、F1レーシング カーを運転するようなものなのです-事故に出会う確率からみても。

冗談は抜きにしても- Visual Basicはインタプリタ型であり、Visual C++はコンパイル言語です。ですから、Visual C++のプログラムはVisual Basicのものよりも大変高速になるはずなのです(上手く書いてあれば)。一般的に、Visual Basicはユーザー インターフェースを「試作する」のに最適と言われていますが、コードの再利用や、もっと大きな問題の解決となるとVisual Basicは不利になります。一方、Visual CはVisual Basicほど簡単には扱えませんが、いったん憶えてしまえば、Visual Basicで書くよりもずっと複雑なプログラムを書くことができます。ようするに、どちらを使うべきかは、書いているアプリケーションによって異なるということなのです。

Scot Wingo、scot@stingsoft.com(1995年6月18日)

MFC哲学(Microsoftひいきより!)

MFCが提供しているクラスについて、一言・・・。これらのクラスは「汎用」のクラスとして設計されているのであって、「万能」のクラスではありません。どのような用途でも理想的なパフォーマンス特性を発揮できる単独の実装はありません。提供された実装を評価し、それが当面の作業にとって適当であるかどうかを判断するのはプログラマであるあなた次第なのです。もし使えないものとわかったときにも、いくつかの対策が選べます。提供された実装を修正またはサブクラス化するか、別のソースからもっと適切なクラスを見つけるか、あるいは、自分でゼロから実装を書き起こすかです。

elsbree@msn.com、mfc-l(1995年7月15日)

MFCコレクションとSTLコレクション、どちらを使えばよいのでしょう?

わかったら投稿してくださいね。

Visual C++の高度な使い方のヒントと秘訣

Visual C++の「Easter Eggs」を見る方法は?

参考:Easter Eggsとは隠し画面のことで、通常はイニシャルや開発チームの名前などが表示されます。

回転するプラス記号とVisual C++のチーム メンバを見るには、Visual C++ 2.0または2.1で次のようにします。

  1. [About]ボックスを開きます:[Help]をクリックして、次に[About]をクリックします。

  2. CTRL+TABを押しながら、ボックスの中央をダブル クリックします。

メモ
これを実行するためには、Visual C++のCD-ROMをドライブに入れておく必要があると思います。

上記の手順がうまくいかない場合は、こちらを試してみてください。

  1. MSVCCRD.DLLをCD-ROMから\msvc\binへコピーします。

  2. CTRLを押して、ダイアログ中の絵をダブルクリックします。

メモ
2回目のクリックと同時に、CTRLキーを放します。

Jeff O'Halloran、ohallorj@pwc-tpc.ca
Visual C++ 4.0のAboutボックスを見るためには、CD-ROMをドライブに入れて、CTROLキーを押したままの状態でAboutボックスの中でダブル クリックをします。[OK]ボタンが消えるはずです。3分から5分待つと、きれいで素晴らしいフライト シミュレータが見えるはずです!msdevcrd.dllを開くと画像も見られます。このDLLは\msdev\binにあります。

Scot Wingo、scot@stingsoft.com

Visual C++のコマンドライン オプションは?

MSVC /V- DOSウィンドウでコンパイラを実行します(バージョン1.5のみ?)
mark@techop.demon.co.uk
MSVC /NOLOGO- AboutボックスなしでVisual C++を実行します。1~2秒節約できます。

MSVC /bppassc:yes-[Breakpoints]ダイアログのクールなブレークポイント カウンタが使えるようになります([Debug]メニューで)。

jimb@turningpoint.com、電子メールにて(1995年7月11日)
MSVC -p- Visual C++をプロセスにアタッチします。

ClassWizardで生成されたコードの色を変える方法!

ClassWizardによって変更されたテキストの色は、標準では白地に黒文字になっています。Visual C++は、ClassWizardによって変更されたコードを判別できるので、これらの変更を別の色を使って強調表示することができます。

ClassWizardが変更したテキストを強調表示すれば、新しく変更した部分がはっきりするので、簡単に見つけられるようになります。これを行うには[Tools]メニューから[Options]を選び、次に[Options]ダイアログ ボックスの中の[Format]タブを選びます。[Colors Listbox]で[Wizard Code]を選んで、テキストと背景用の色を定義します。

メモ
Visual C++ 2.x(と4.x)にしか適用できませんが、すごく便利ですよ!
Visual C++ Knowledge Base記事Q125779(1995年6月25日)

Visual C++エディタで使える10のすぐれ技!

CTRL+ALT+Tを押すと、タブ キャラクタが表示されます。

SHIFT+ESCを押すと、アクティブなドック可能ウィンドウが閉じます。

CTRL+F3を押すと、現在の単語が次に出てくる場所が検索されます。

CTRL+Mを押すと、対応する括弧が検索されます。

CTRL+>またはCTRL+<を押すと、次または前の対応する#ifdef、#else、#endifが検索されます。

CTRL+SHIFT+Rを押すと、マクロの記録が開始/停止します。

CTRL+SHIFT+Pを押すと記録されたマクロが再生されます。

#includesで右クリックをして、ヘッダ ファイルが開けます。

TAB/SHIFT+TABを押すと、選択範囲の行を字下げしたり、字下げを解除したりできます。

ALTキーを押したままドラッグすると、複数のカラムを選べます。

Jeff Henshaw、MFC PDCのスライドから借用(1995年6月25日)
メモ
Visual C++ 1.5xでは、これらがすべて動作するわけではありません。ほとんどは2.xと4.xで動作します。

Visual C++のコンパイル速度を上げる方法は?

コンパイル済みのヘッダを使用する、ブラウザ([Options]>[Project]>[Compiler]>[Listing Files]>[Browser Information])を切る、ディスク キャッシュのサイズを増やす/減らす、環境変数INCLUDEとLIBのディレクトリ順序を変更します。まあ、そんなところです。

ebarb@iadfw.net、programmer.tools(1995年8月19日)

すべてのMFCキーワードを違う色にする方法!

ファイルUSERTYPE.DATをFAQ Archiveからダウンロードします(ftp://landru.unx.com/pub/mfc_faq/archive)。このアーカイブにはFAQを実例で説明するMFCのサンプルが入っています。試してみてください。気に入りますよ!
メモ
バージョン4.0向けにアップデートされています。

Scot Wingo、scot@stingsoft.com(1995年8月31日)

.mdpファイルなしで4.xプロジェクトを開始する簡単な方法

巨大プロジェクトのために、バージョン管理システムにmdpファイルを記録したり、格納したりすることはないかもしれません。メークファイルを見つけて、すぐに実行したいだけですよね。問題は、Visual C++が.makファイルを簡単には認識しないということです。メークファイルから直接Visual C++ 4.0を起動して、.mdpファイルも作成する簡単な方法を次に示します。

  1. レジストリ エディタを開始します(Windows 95のRegistry.exe、Windows NT 3.51のRegedt32.exe)。

  2. HKEY_CLASSES_ROOTウィンドウをアクティブにします。

  3. 新しいキーを加えます。
    .mak

  4. 新しい文字列値を加えます。
    mdpfile

  5. レジストリを閉じます。

これだけです。これで、どんなVisual C++ 4.0のメークファイルでも、ファイル マネージャやエクスプローラんで、Visual C++のIDEを起動できます。そのメークファイル用にプロジェクト ワークスペース(.mdp)が作成されます。これで、今後ビジュアル環境に入って「ファイルを開く」コマンドを実行して、ファイル名を入力して、「メークファイルとして開く」必要もないのです。まさに時間の節約なのです。

G_LEDONNE@msn.com

Visual C++に関する様々な質問

Visual C++が作成するこれらのファイルは何なのですか?

一般的な拡張子の説明と、Visual C++がそれらを何のために使用しているのかを表にしてみました。

拡張子 用途 メモ
.APS AppStudioファイル
.BSC ブラウザ ファイル 複数の.SBRを1つの.BSCに結合。
.CLW ClassWizardファイル
.ILK インクリメンタル リンカ ファイル
.NCB ClassView情報を含む 4.0で追加。問題が出たら削除。
.PCH コンパイル済みヘッダ ファイル
.PDB デバッグ情報 /Z7が影響する。
.RES RC「オブジェクト」ファイル .EXEにリンクされる。
.SBR 1ファイル用ブラウザ情報 .BSCに結合される。
.VCP Visual C++情報ファイル

MFCXX.DLLの使用からスタティックにリンクしたライブラリの使用への変更方法は?

ビルド設定ウィンドウで、設定を少し変える必要があります。

[C/C++]タブで[General Category]を選びます。プリプロセッサ定義のリストから「_AFXDLL」を削除します。[Code Generation]タブで[Multithreaded]または[Debug Multithreaded]を選びますが、これはあなたのビルドにとって適切な方を選びます。

[Resources]タブで「_AFXDLL」がリストの中にないことを確認します。これは[Preprocessor Definitions]エディット フィールドにあります。

blaszczak@BIX.com

Microsoftにバグ レポートする方法は?

バグだと思われるものを見つけたときには、次のようにします。

  1. あわてる。

  2. 近所であばれると脅迫する。

  3. 問題を整理しようとする。

  4. 整理したものを別マシン上の別プロジェクトへ持って行き、再試行してみる。

  5. それでも問題が起きるなら、コードをステップ実行してみる。で、考える。

  6. もっと色々考える。

それでもなお、これはバグだと確信したなら、以下のうち最低1つを実行しましょう。

  • PSSに電話する。

  • 公認のコンサルタントなど、サポートの専門家に話す。

  • MFC-LかMSFMCグループに手紙を書く。またはMSNのMFC掲示板に書き込む。

ですがPSSへ電話を掛けることは、支援を受けるための公式の、保証された方法であることを認識しておいてください。

私の仕事はソフトウェアを書くことです。仕事が得意と言えるよう、努力しています(ホッケーの試合へ行くのも得意ですが)。しかし、製品サポートの仕事までフルタイムで、完全な責任の元に行うのは、本当に無理なのです。直接私にバグ レポートを送っていただいても、私はばつの悪い思いで、「すいませんが、力になれません」と返事をするしかできないのです。

ご理解に感謝します。

mikeblas@iqnterserv.com

Visual C++/MFC 4.0固有のヒント、問題、その他

ウィンドウがMFC 4.0で作成されないわけは?

おそらく問題は、あなたがMFC 3.xのうちの1つを使っていて、::Create()に渡したクラス名について、古いバージョンのウィンドウ クラスを使っている点でしょう。たとえば、たとえば白い背景のウィンドウを作成する場合、ごく一般的な方法として「AfxViewOrFrame」を使います。MFC 4.0では、すべてのウィンドウ クラス名が変わっており、必要になったとき動的に変更が行われるのです!

解決法としては、AfxRegisterWndClass()を使用して、結果をCreate()に渡します。正しい結果を得るには、必ず正しいフラグを指定してください。また、4.0向けに改訂されたMFC Techical Note 1も参照しましょう。

Scot Wingo、scot@stingsoft.com(1996年1月20日)

Visual C++ 2.xのメークファイルを4.xに変換する方法は?

  1. [File]>[Open]を選びます。

  2. *.makファイルを選びます。

  3. [Open as: Makefile]を選びます。

  4. [Open]を選びます。すると、それをVisual C++ワークスペースに変化するようVisual C++が勧めてきます-これで、ご希望通り。

各国語対応版の4.0バージョンのメンバにコントロールを結び付けるのにClassWizardで問題が生じています-どうしたらよいでしょう?

問題があるかどうかをテストして確かめるために、次のようにします。

  1. Visual C++バージョン4.0で、AppWizardを使ってダイアログベースのアプリケーションを作成します。

  2. 次に、ダイアログ リソース エディタを使って上で生成したダイアログ ボックス上にコントロールなどを追加します。

  3. 追加したコントロールにメンバ変数を関連付けるために、ClassWizardを起動します。コントロールIDは、メンバ変数プロパティのページの、コマンドIDウィンドウの中にはリストされません。(そこにあるのはIDCANCELとIDOKだけです!)
コントロールIDはメッセージマップ プロパティのページにありますが、メンバ変数プロパティのページではないのです!どうしてですかね?

修正:

修正をするには、ダイアログ テンプレートの言語プロパティを変更します(リソースウィンドウの中で、ダイアログ ボックスの名前を強調表示し、マウスのボタンを右クリックします)。私の場合、言語プロパティをEnglish (US)から English (Australian)に変更しなければなりませんでした-こうしないと、コントロールのメンバ変数は表示されません。このプロパティは、使用しているWindows 95が使っている言語と同じでなければならないでしょう。いずれにせよ、私と、同じ問題を抱えたドイツのユーザーの場合は、この変更で解決できました。

doug@psy.uwa.edu.au、電子メールにて(1995年12月13日)

私が使うと、Visual C++ 4.0が必ずクラッシュするんです!助けて!

私は、自分ではこれらの問題は経験していませんが、この問題の解決策(おそらく、いくつかはすでに知っていると思いますが)をいくつか示します。

サービス アップデートを http://msdn.microsoft.com/visualc/ からダウンロードしたかどうか、確認してください。

サブプロジェクトを使っているなら、恐らくそれが犯人です-サブプロジェクトは非常に不安定なのです。

200以上のファイルを使っている場合は、プロジェクトをDLLに分割してみてください。

.ncbファイルを削除してみてください-ここにはClassViewの情報が格納されています。

プロジェクトをバージョン2.xから変換していて問題が生じているのなら、新たに4.0のプロジェクトでスタートして、そこへファイルを入れてください。

コンポーネント(クラスではなく)をComponent Galleryに加える方法は?

Component Galleryに使用されているAPIを、Microsoftはまだ公開していません。

Visual C++ 5.0のヘルプ システムはどうなってしまったのでしょう?

広く知られていることですが(少くとも私が話す相手には)、Visual C++ 5.0のヘルプ システムは、ものすごく遅いのです!Microsoftはすばらしくて速いRTFベースのWinHelpビューワ システムを、HTMLベースのInternet Explorerビューワ システムと置き換えてしまったのです。どうしてこれは遅いのでしょう?現在のヘルプはまるでAlta Vistaを使って検索しているような感じなのですが、これはヘルプ ファイルがHTMLで書かれているからです。インデックス システムがあり、それを使えば速度を上げられるという話もありますが、今のところ本当に遅いのです。

私は次のようにしています。

  1. すべてのヘルプ ファイルを削除します。

  2. Visual C++ 4.2をインストールし、IDEとヘルプ ファイルのオプションだけを選びます。c:\vc42_helpなど、固有ディレクトリへインストールします。

  3. Visual C++ 4.2をヘルプ用に、Visual C++ 5.0をコンパイル用に使用します。

簡単なように見えて、実際そのとおりです。ですが、あなたが私のようなヘビー ユーザーだとしたら、5.0のヘルプは本当に遅いのです(これがよいものなのか、悪いものなのか、私にはわかりません)。

Scot Wingo, scot@stingsoft.com
MSDN編集部より
確かに、InfoViewerバージョン5.0の初期リリースは(MSDN Libraryと各種ドキュメント セット用のInternet Explorerベースのブラウザ)は、非常に遅いものでした。1997年4月のリリース以降のMSDN Libraryには、「パフォーマンス強化」バージョンが含まれています。また、1997年の初夏までには、Visual Studio 97とVisual C++ 5.0にも含まれることになっています。

Visual C++ 5.0でVisual C++ 4.2のドキュメントを使おうと考えているなら、バージョン4.2と5.0にかなりの違いがあることに注意してください。バージョン4.2のヘルプはバージョン5.0のヘルプほど正確にはなりません。MSDNは、Visual C++ 5.0でVisual C++ 5.0のドキュメントを使用するよう提案しています。