Windowsプログラミング Edit

ダイアログボックス Edit

ダイアログの閉じ方と対応するイベントハンドラ Edit

操作イベントハンドラ
EnterOnOK()
EscOnCancel()
Alt+F4

タブオーダの保存形式 Edit

リソースファイル(*.rc)内でコントロールが定義される順番が、そのままタブオーダとして解釈される。

他のコントロールと連携が必要な場合の例 Edit

異なるコントロールを連携させて使用する場合がある。連携させるには、タブオーダを連続させる必要がある。

コントロールへのドラッグ&ドロップの優先順位 Edit

複数のコントロールが重なっている場合、タブオーダの小さいコントロールが優先。
グループボックスコントロールを使用する際などは注意。

クラスウィザードに表示されていないメッセージ Edit

クラスウィザードに目的のメッセージが表示されていない場合、表示内容がフィルタリングされている可能性がある。 「詳細設定オプション」で「メッセージフィルタ」を変更することで、目的に合ったメッセージを表示できる。

例)ダイアログクラスで初期状態ではフィルタリングされているメッセージ。

コントロールのフォーカス移動 Edit

CWnd::SetFocus() ではなく、CDialog::GotoDlgCtrl() を用いる。
CWnd::SetFocus()を用いてもフォーカスは移動するが、画面描画上は移動していないように見えてしまうことがある。

  1
  2
  3
    // ボタンのコントロール変数が m_BtCtrl の場合
    m_BtCtrl.SetFocus();    // ←×
    GotoDlgCtrl(&m_BtCtrl); // ←○

ダイアログ表示直後に指定コントロールへフォーカス設定するには、OnInitDialog() で GotoDlgCtrl() を呼び出した後、OnInitDialog() の戻り値を FALSE にする必要がある。

リソースエディタ上でコントロールのフォントを指定した際の文字化け Edit

コード上でフォントの設定をすると直ることがある。

ツールバーを使用するには Edit

通常、ツールバーはSDIかMDIのアプリケーションにしか用意されないが、以下の手順でダイアログにも使用できる。
※ダイアログに配置するコントロールの位置は、ツールバーの範囲を考慮する必要がある。また、ドッキング可能にすることはできない。

<ツールバーの作成>

  1. ツールバーのリソースを用意する。
  2. ダイアログクラスのメンバ変数として以下を定義しておく。
      1
    
        CToolBar    m_wndToolBar;
  3. CXxxDlg::OnInitDialog()でツールバーを作成する。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
    
        CRect    rcClient;
        CSize    sizeToolBar;
     
        // ツールバー作成
        if(!m_wndToolBar.Create(this)){
            return FALSE;
        }
        // ツールバーをリソースからロード
        if(!m_wndToolBar.LoadToolBar(IDR_TOOLBAR)){
            return FALSE;
        }
     
        // クライアント領域を取得
        GetClientRect(rcClient);
        // ツールバーのサイズを取得
        sizeToolBar = m_wndToolBar.CalcFixedLayout(FALSE, TRUE);
     
        // 作成時は大きさが0なので設定する必要がある
        m_wndToolBar.MoveWindow(0, 0, rcClient.Width(), sizeToolBar.cy);
    <ツールバーのボタン押下時のイベントハンドラ定義>

    クラスウィザードは対応していないため、自分で定義する必要がある。(以下、ボタンのIDを ID_XXXとする)

  4. ダイアログクラスのヘッダのメッセージマップ部分に関数のプロトタイプを作る。
      1
      2
      3
      4
      5
      6
      7
      8
    
        // 生成されたメッセージ マップ関数
        //{{AFX_MSG(CMStepEditorDlg)
        virtual BOOL OnInitDialog();
        afx_msg void OnPaint();
        afx_msg HCURSOR OnQueryDragIcon();
        //}}AFX_MSG
        afx_msg void OnXxx();        // @TODO:ここに追加
        DECLARE_MESSAGE_MAP()
  5. ダイアログクラスのソースに関数 OnXxx() を定義する。
  6. ダイアログクラスのソースのメッセージマップ部分に定義した関数を登録する。
      1
      2
      3
      4
      5
      6
      7
    
        BEGIN_MESSAGE_MAP(CMStepEditorDlg, CDialog)
            //{{AFX_MSG_MAP(CMStepEditorDlg)
            ON_WM_PAINT()
            ON_WM_QUERYDRAGICON()
            //}}AFX_MSG_MAP
            ON_COMMAND(ID_XXX, OnXxx)    // @TODO:ここに追加
        END_MESSAGE_MAP()

イベントハンドラ活用 Edit

F1押下時のヘルプの無効化 Edit

アプリケーションクラスのメッセージ処理を修正することで、無効化できる。

  1
  2
  3
  4
  5
  6
  7
BEGIN_MESSAGE_MAP(CXxxApp, CWinApp)
    //{{AFX_MSG_MAP(CXxxApp)
        // メモ - ClassWizard はこの位置にマッピング用のマクロを追加または削除します。
        //        この位置に生成されるコードを編集しないでください。
    //}}AFX_MSG
//    ON_COMMAND(ID_HELP, CWinApp::OnHelp)        // ※ここをコメントアウト
END_MESSAGE_MAP()

ダイアログベースアプリケーションの終了コード Edit

アプリケーションのCWinAppを継承したクラスのExitInstance()をオーバーライドし、戻り値を設定すればよい。

WM_INITDIALOGの戻り値 Edit

通常、ウィンドウプロシージャやダイアログプロシージャの戻り値は、メッセージを処理したか否かをBOOL型で返すが、WM_INITDIALOGだけは戻り値に特別な意味がある。
TRUE を返すと、タブオーダーが一番若いコントロールに自動的にフォーカスが当てられる。
FALSE を返すと、何も行われない。(自分でフォーカスを当てるコードを書く必要がある。)

ダイアログ生成時に明示的にあるコントロールにフォーカスを当てたい場合や、ダイアログを非表示で生成しておき後で表示するような場合は、FALSEを返す必要がある。(後者のケースは、ダイアログ生成時にフォーカスが奪われてしまうのを防ぐため)

CDialogBar派生クラスでOnInitDialog()を実装する方法 Edit

CDialogBarクラスにはOnInitDialog()が用意されていないため、自分でON_MESSAGE()でWM_INITDIALOGをハンドルする必要がある。

参考:http://support.microsoft.com/kb/185672/ja

MFCによりダイアログが自動的に中央に移動する Edit

MFCで作られたダイアログは、自動的に中央に移動する機能が組み込まれている。
通常、OnInitDialog()でMoveWindow()等を呼び出し、明示的に表示位置を移動している場合は中央に移動することはないが、ある条件を満たすと中央に移動させられてしまう。
例として、(0, 0)の位置に移動しようとしたときに現象が発生する。

以下の条件のいずれかを満たしている場合は中央移動は行われない(一部抜粋)


詳細は関連するMFCの関数を参照。

コントロール Edit

エディットボックス内の改行の注意点 Edit

コンボボックスのドロップダウンリストのサイズ変更 Edit

ダイアログエディタ上で、コンボボックスの右端の▼マークをクリックすると、ドロップダウンリストのサイズ変更状態になる。

リストビューコントロールのヘッダのソート記号 Edit

リストビューのヘッダに対して以下の定数を設定する。

HDF_SORTUP昇順(▲)
HDF_SORTDOWN降順(▼)

※この機能はXP以降の機能なので、VC6.0(1998年)時点では未対応。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
    // CListCtrl    m_LcTest    リストビューのコントロール変数
    // long         lSubItem    ヘッダの列インデックス
 
    HDITEM          hdItem;     // ヘッダ設定情報
    CHeaderCtrl*    pHrCtrl;    // リストビューのヘッダ
 
    // 初期化
    ::ZeroMemory(&hdItem, sizeof(HDITEM));
    // メンバ変数 HDITEM::fmt を有効にする
    hdItem.mask = HDI_FORMAT;
 
    // ヘッダコントロールをリストから取得
    pHrCtrl = m_LcTest.GetHeaderCtrl();
     
    // ヘッダの指定列の現在の設定状況を取得
    pHrCtrl->GetItem(lSubItem, &hdItem);
 
    // 例)昇順ソートマークを設定
    hdItem.fmt &= ~HDF_SORTDOWN;    // 降順マークを消す
    hdItem.fmt |= HDF_SORTUP;       // 昇順マークを設定
 
    // ヘッダに設定
    pHrCtrl->SetItem(lSubItem, &hdItem);

ツリービューの全展開 Edit

ツリービューコントロールを全展開する関数

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
    // ツリー全展開
    //  CTreeCtrl *pTreeCtrl    展開対象のツリーコントロール
    //  HTREEITEM hParent       展開対象のノード (呼び出し時はNULL指定する)
    void ExpandAllTree(CTreeCtrl *pTreeCtrl, HTREEITEM hParent){
 
        HTREEITEM   hChild;
 
        // 初回呼び出し
        if(hParent == NULL){
            // ルートノード
            if((hParent = pTreeCtrl->GetRootItem()) == NULL){
                // エラー
                return;
            }
        }
 
        // 子ノードが存在するなら
        if(pTreeCtrl->ItemHasChildren(hParent) == TRUE){
 
            // 展開
            pTreeCtrl->Expand(hParent, TVE_EXPAND);
 
            // 子ノードを順番に処理する
            hChild = pTreeCtrl->GetChildItem(hParent);
            while(hChild != NULL){
 
                // 子ノードを展開する
                ExpandAllTree(pTreeCtrl, hChild);
 
                // 次の子ノード
                hChild = pTreeCtrl->GetNextSiblingItem(hChild);
            }
        }
    }

スピンコントロールの値変更イベント Edit

スピンコントロールの値が変更された場合のイベントハンドラは、バディされたエディットコントロールのEN_CHANGEイベントで行う。
しかし、スピンコントロールとエディットコントロールを自動でバディする設定の場合は、OnInitDialog()よりも前に自動的にバディされたタイミングでEN_CHANGEイベントが発生してしまう。すると、まだ用意されていないコントロールにアクセスされてしまい、実行時エラーになってしまうことがある。
対処策としては、自分でエディットコントロールにバディするコードを書くか、フラグを用意してOnInitDialog()コール前ならEN_CHANGEイベントハンドラで何も処理しない設計にしておく。

リッチエディットコントロール使用前の準備 Edit

リッチエディットコントロール(CRichEditCtrl)を使用する場合は、通常のエディットコントロール(CEdit)と違って予め準備をしておく必要がある。

  1
  2
  3
  4
    // m_ReCtrl : リッチエディットコントロールのコントロール変数
 
    // EN_CHANGEイベント(内容が変更されたときのイベント)を使用する場合のマスク設定
    m_ReCtrl.SetEventMask(m_ReCtrl.GetEventMask() | ENM_CHANGE);    // ENM_CHANGE : EN_CHANGEのイベントマスク

エディット/リッチエディットコントロールのワード区切り Edit

単語境界やワードラップ機能は、どのように文字列のワード区切りを判断しているかに依存する。
ワード区切りの方法を変更するには、以下のキーワードを調べる。(まだ試していない)

コントロールの「通知」の有無 Edit

コントロールのプロパティの「通知」の有無の違いによって、イベントハンドラがコールされる順番を検証。
(CWnd::Create()でコントロールを作成する場合は、「SS_NOTFY」フラグに該当する。)

ActiveXコントロールの登録状況の確認 Edit

ActiveXコントロールを使用したMFCアプリケーションを作成すると、デフォルトの動作ではActiveXコントロールが登録されていないと何の表示も行われずに終了してしまう。
それだと、なぜアプリケーションが起動しなかったのかが分からないため、この方法を用いてエラーメッセージを表示するとよい。

コントロールのサブクラス化 Edit

サブクラス化の方法 Edit

※サブクラス化とは、C++の「クラスの継承」のことではない。
既存のウィンドウプロシージャを、独自のウィンドウプロシージャに差し替えることを指す。

サブクラス化に用いた派生クラス内での初期処理 Edit

OnCreate()はコールされないため、初期処理を記述することはできない。
派生クラスでは、PreSubclassWindow()をオーバライドして、そこに初期処理を書く。
元のクラスが構築されて(OnCreate)、サブクラス化が行われるのであって、派生クラスが直接構築されるわけではない。

MFCを継承した自作クラスへのイベントハンドラの追加 Edit

MFCを継承している場合は、クラスウィザードに派生クラスを認識させておく必要がある。
(認識されているかどうかは、クラスウィザードの「クラス名」に表示されるかどうかで判断できる)
認識されていない場合は、以下の操作ができない。

プロジェクト内でクラスを作成した場合は自動的に認識されるが、外部から持ってきたコードを用いる場合は、 一度clwファイルを削除してクラスウィザードを作り直すときに、ファイルを含める必要がある。

※ちなみに、MFC継承クラスをさらに継承した場合は上記作業を行っても不可。仕様上は問題なくイベントハンドラを実装できるはずなので、手動で行うしかない?

ウィンドウ Edit

ウィンドウ操作関数一覧(一部) Edit

操作Win32APIMFC
SetGetSetGet
ウィンドウ情報-::GetWindowInfo-CWnd::GetWindowInfo
親ウィンドウ-::GetParent-CWnd::GetParent
関連ウィンドウ-::GetWindow-CWnd::GetWindow
スタイル::SetWindowLong::GetWindowLongCWnd::ModifyStyleCWnd::GetStyle
拡張スタイルCWnd::ModifyStyleExCWnd::GetExStyle
インスタンスハンドル--
ウィンドウプロシージャ--
(特記)使用可否::EnableWindow::IsWindowEnableCWnd::EnableWindowCWnd::IsWindowEnable
表示状態::ShowWindow::IsWindowVisibleCWnd::ShowWindowCWnd::IsWindowVisible
ウィンドウクラス登録::RegisterClassEx---
ウィンドウクラス登録解除::UnregisterClass---
コントロールID-::GetDlgCtrlIDCWnd::SetDlgCtrlIDCWnd::GetDlgCtrlID
ドラッグ&ドロップ許可::DragAcceptFiles-CWnd::DragAcceptFiles-
アクティブ::SetActiveWindow::GetActiveWindowCWnd::SetActiveWindowstatic CWnd::GetActiveWindow
位置スクリーン座標::MoveWindow::GetWindowRectCWnd::MoveWindowCWnd::GetWindowRect
クライアント座標-::GetClientRect-CWnd::GetClientRect
位置データ::SetWindowPlacement::GetWindowPlacementCWnd::SetWindowPlacementCWnd::GetWindowPlacement
Zレベル::SetForegroundWindow::GetForegroundWindowCWnd::SetForegroundWindowstatic
CWnd::GetForegroundWindow
::SetWindowPos-CWnd::SetWindowPos-
中央へ移動--CWnd::CenterWindow-
キャプション::SetWindowText::GetWindowTextCWnd::SetWindowTextCWnd::GetWindowText
透明度::SetLayeredWindowAttributes::GetLayeredWindowAttributesCWnd::
SetLayeredWindowAttributes
CWnd::
GetLayeredWindowAttributes
フォント--CWnd::SetFontCWnd::GetFont
操作閉じる::DestroyWindow-CWnd::DestroyWindow-
最大化::ShowWindow::IsZoomedCWnd::ShowWindowCWnd::IsZoomed
最小化::ShowWindow
::CloseWindow
::IsIconicCWnd::ShowWindow
CWnd::CloseWindow
CWnd::IsIconic
メッセージ送信(同期)::SendMessage-CWnd::SendMessage-
メッセージ送信(非同期)::PostMessage-CWnd::PostMessage-
再描画::UpdateWindow-CWnd::UpdateWindow-

ウィンドウの中央表示 Edit

タイトルバーの高さの取得 Edit

OSの設定(フォントやテーマ等)に依存する。

  1
    ::GetSystemMetrics(SM_CYCAPTION)    // タイトルバーの高さを取得

コンソールウィンドウの使用 Edit

::AllocConsole(), ::FreeConsole()を用いる。
コンソールへの入出力をするためには、標準入出力を割り当てるか専用の関数でハンドルを取得する。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
    ::AllocConsole(); // コンソール作成
 
    // ①標準入出力を割り当てる方法
    // printf()などの標準関数を使う場合
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
    freopen("CONIN$", "r", stdin);
    printf("test\n");
 
    // ②コンソールハンドルを取得する方法
    DWORD   dwRet;
    char    *str = "test";
    HANDLE  hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
 
    // コンソールへ書込み
    ::WriteConsole(hStdOut, str, strlen(str), &dwRet, NULL);
 
    // アプリケーション終了時?などに::FreeConsole()でコンソールを解放する

ウィンドウの描画更新の抑制 Edit

CWnd::LockWindowUpdate() と CWnd::UnlockWindowUpdate() で、処理を挟み込む。

  1
  2
  3
  4
  5
    LockWindowUpdate()
 
    // 描画更新を抑制した状態で行う処理を記述
 
    UnlockWindowUpdate()

大きなツリービューへのアイテムの追加など、時間がかかる処理は描画更新を抑制することで、画面のチラつきを防止できる。

小さいアイコン(16x16)の設定 Edit

大きいアイコン(32x32)と小さいアイコン(16x16)の両方を用意してあるのに、小さいアイコンがうまく適用されないことがある。
その場合は、OnInitDialog() 等のアイコン設定処理で、小さいアイコン設定の処理をコメントアウトするとよい。

  1
  2
    SetIcon(m_hIcon, TRUE);    // 大きいアイコンを設定
    //SetIcon(m_hIcon, FALSE); // 小さいアイコンを設定

※正式な修正方法ではないかも。

メニューの項目の取得 Edit

取得の手順。

  1. メニューを持つウィンドウを識別
  2. メニューバーを取得
  3. 必要があればサブメニューを取得していき、目的の項目を持つサブメニューまで辿る
  4. 目的のメニュー項目の情報を取得(/設定)する

メニューの項目は、0から始まるインデックスで位置を指定する
サブメニュー内は、それぞれで0から始まるインデックスを持つ。

 

メニュー項目の取得は、MENUITEMINFO構造体を介して行う。
MENUITEMINFO::cbSize に構造体のサイズを設定し、必要なメンバ変数に対応したフラグを MENUITEMINFO::fMask に設定する。

 

メニューバーの描画更新 Edit

メニュー項目に変更を行っても自動的に描画更新されないため、明示的に描画更新する必要がある。

指定ウィンドウの描画更新 Edit

ウィンドウの描画更新を促すには、WM_PAINTを発行する必要がある。ただし、ウィンドウに直接WM_PAINTを送るのではなく、下記の手順を踏む必要がある。

  1
  2
  3
    // ウィンドウ全体を再描画する
    pWnd->InvalidateRect(NULL);        // 更新リージョンにウィンドウ全体を追加する
    pWnd->UpdateWindow();            // 更新リージョンが空でなければ、WM_PAINTを発行する

ウィンドウの一部だけを再描画させる場合は、CWnd::InvalidateRect()の第一引数にNULLではなく、RECT*型の領域を渡す。

メッセージボックスの簡易的な最前面化 Edit

最前面ウィンドウを親に持つメッセージボックスを作成すればよい。
親ウィンドウは非表示にしておけば、メッセージボックスのみ表示される。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
    CWnd    *pDmyWnd;
    TCHAR    szClassName[256];
 
    // ダミーウィンドウ生成 (非表示で最前面)
    ::GetClassName(this->GetSafeHwnd(), szClassName, 256);
    pDmyWnd = new CWnd();
    pDmyWnd->CreateEx(WS_EX_TOPMOST, szClassName, NULL, 0, CRect(0, 0, 10, 10), this, 0);
 
    // ダミーウィンドウを親に持つメッセージボックス
    ::MessageBox(pDmyWnd->GetSafeHwnd(), "test", "test", MB_OK | MB_ICONINFORMATION);
 
    // ダミーウィンドウを破棄
    pDmyWnd->DestroyWindow();
    delete    pDmyWnd;

ウィンドウの再表示 Edit

ウィンドウの再表示を行う際、::ShowWindow()の第2引数に「SW_SHOWNORMAL」や「SW_RESTORE」を渡すだけでは、ウィンドウの状態(最小化、最大化)によって動作が異なってしまう。(ヘルプを読むと「SW_SHOWNORMAL」でよさそうだが。。)

以下のように場合分けをすることで解決できる。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
    int    nShowCmd;
 
    // 表示コマンドを決定
    if(::IsIconic(hWnd)){
        // 最小化時:元の状態に戻す
        nShowCmd = SW_RESTORE;
    } else if(::IsZoomed(hWnd)){
        // 最大化時:そのまま
        nShowCmd = SW_SHOWMAXIMIZED;
    } else {
        // それ以外は単純に再表示
        nShowCmd = SW_SHOW;
    }
 
    // ウィンドウを表示
    ::ShowWindow(hWnd, nShowCmd);

タイトルバーを常にアクティブ色にする Edit

再描画のたびにウィンドウメッセージでアクティブ通知を行う。

  1
  2
  3
  4
  5
  6
  7
void CXxxDlg::OnPaint() 
{
    :
    // キャプションを常にアクティブ色にする
    PostMessage(WM_NCACTIVATE, TRUE);
    :
}

メッセージ専用ウィンドウ Edit

ウィンドウメッセージの処理を行いたいがウィンドウの実体は必要無いというときは、メッセージ専用ウィンドウを利用することができる。
(ウィンドウ列挙等の対象からも外れる)

使用する場合は、予め下記の定数を設定しておく必要がある。

#define WINVER 0x0500   // 0x0500以上ならOK

ウィンドウメッセージ Edit

自作メッセージ Edit

自作のメッセージの定義は、以下の2通り。

メッセージフック処理 Edit

アクティブウィンドウに対してキーメッセージを送信 Edit

  1
  2
  3
    // VK_xx に送信したい仮想キーを書く
    keybd_event(VK_xx, 0, KEYEVENTF_EXTENDEDKEY, 0);                   // キー押下(WM_KEYDOWN)
    keybd_event(VK_xx, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); // キー解放(WM_KEYUP)

「ALT+F」などのメッセージも、キー押下と解放のタイミングと組み合わせで実現可能。「PrntScrn」や「NumLock」も可能。

Windowsアプリケーションが終了する仕組み Edit

  1. ユーザが終了処理を行う(×を押すなど)

    ↓ WM_CLOSE

  2. ::DestroyWindow()

    ↓ WM_DESTROY

  3. ::PostQuitMessage()

    ↓ WM_QUIT

  4. WinMain()やスレッド内で用いられている::GetMessage() が FALSE を返すことにより、メッセージループを抜け、処理が終了する。

メッセージによるコマンドの実行 Edit

ウィンドウにコマンドIDを付与した WM_COMMAND メッセージを送ることで実行できる。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
    // HWND     hWnd    ウィンドウハンドル
    // UINT     nID     コマンドID
 
    // メニューコマンドを送信する場合
    // ・第2引数のWPARAM
    //      上位ワード : 0x0000     0
    //      下位ワード : nID        メニュー項目ID
    ::PostMessage(hWnd, WM_COMMAND, (WPARAM)MAKELONG((WORD)nID, (WORD)0x0000), 0);
 
    // アクセラレータコマンドを送信する場合
    // ・第2引数のWPARAM
    //      上位ワード : 0x0001     1
    //      下位ワード : nID        アクセラレータキーID
    ::PostMessage(hWnd, WM_COMMAND, (WPARAM)MAKELONG((WORD)nID, (WORD)0x0001), 0);

アプリ間で使用する任意メッセージの受信 Edit

  1. グローバル変数として、メッセージIDを保持しておく
      1
      2
      3
    
    // ユニークなメッセージIDを取得
    // ("TEST" はメッセージID登録用の任意の文字列。メッセージを送受信するアプリ間で決めておく)
    static const UINT uiWmTest = ::RegisterWindowMessage("TEST");
  2. ウィンドウクラスのヘッダに以下のようにイベントハンドラを追加
      1
      2
      3
      4
      5
      6
    
        // 生成されたメッセージ マップ関数
        //{{AFX_MSG(CXxx)
    //}}AFX_MSG
        afx_msg LRESULT OnTest(WPARAM=0, LPARAM=0);        // イベントハンドラを追加
        DECLARE_MESSAGE_MAP()
  3. ウィンドウクラスのソースに以下のようにメッセージマップとイベントハンドラの実装を追加
      1
      2
      3
      4
      5
      6
    
    BEGIN_MESSAGE_MAP(CXxx, CXxxParent)
        //{{AFX_MSG_MAP(CXxx)
    //}}AFX_MSG_MAP
        ON_REGISTERED_MESSAGE(uiWmTest, OnTest)            // メッセージマップに追加
    END_MESSAGE_MAP()

      1
      2
      3
      4
      5
      6
    
    // イベントハンドラを実装
    LRESULT CXxx::OnTest(WPARAM wParam/*=0*/, LPARAM lParam/*=0*/){
        // 任意の処理
        MessageBox("TEST");
        return 0;
    }

ウィンドウサイズの制限(WM_GETMINMAXINFO) Edit

マウスドラッグやWin7のスナップ機能などによるウィンドウサイズの変更時に、ウィンドウサイズを制限する場合は、WM_GETMINMAXINFO をハンドルする。

コモンダイアログ Edit

ファイル選択(保存)ダイアログとカレントディレクトリ Edit

ファイル保存ダイアログの拡張子自動付与機能 Edit

※デフォルト拡張子は「.」を含まない。

ファイル選択(保存)ダイアログ使用時にアプリケーションが落ちる現象 Edit

ファイル選択ダイアログを使用していると、呼び出し元のアプリケーションごと強制終了してしまう現象が発生している。PCによって起きたり起きなかったりする。

文字列処理 Edit

マルチバイト文字とワイド文字のサンク機能 Edit

Wwindowsプログラミングをする上で、文字列を「マルチバイト」と「ワイド文字」のどちらで扱うかに気をつける必要がある。

Win32APIやMFCには、これらの文字セットの切り替えを容易にした仕組み(thunk)が用意されており、これに則ってプログラミングすることで、どちらの文字セットを使用するかをコンパイルスイッチで切り替えることができる。

CStringオブジェクトの変換 Edit

constな文字列型(LPCTSTR)への変換しか許されていない。
それ以外は、変換関数が用意されていないため。

文字列のコンバート (VBのStrConv()相当) Edit

::LCMapString()で、ひらがな/カタカナ、半角/全角、大文字/小文字の相互変換が可能。

ファイルパス長関係のマクロの意味 Edit

これらは、「バイト数」ではなく「文字数」を定義したもの。
マルチバイト文字を扱う場合は注意が必要。

マクロ名意味
_MAX_PATH260フルパス
_MAX_DRIVE3ドライブ
_MAX_DIR256ディレクトリ
_MAX_FNAME256ファイル名(拡張子除く)
_MAX_EXT256拡張子(「.」含む)

↑ <stdlib.h> に定義されている

各種文字列型の相互変換 Edit

変換元 src
char [](STL) stringCString
変換先
dst
char []strcpy(dst, src.c_str());strcpy(dst, src);
(STL) stringdst = src;
dst = string(src);
string dst(src);
dst = src.c_str();
dst = CString(src.c_str());
CString dst(src.c_str());
CStringdst = src;
dst = CString(src);
CString dst(src);
dst = src.c_str();
dst = CString(src.c_str());
CString dst(src.c_str());
BSTRdst = ::SysAllocString(src);

::SysFreeString(dst);
dst = ::SysAllocString(src.c_str());

::SysFreeString(dst);
dst = src.AllocSysString();

::SysFreeString(dst);
CComBSTRdst = src;
dst = CComBSTR(src);
CComBSTR dst(src);
dst = src.c_str();
dst = CComBSTR(src.c_str());
CComBSTR dst(src.c_str());
dst.Attach(src.AllocSysString());


変換元 src
BSTRCComBSTR
変換先
dst
char []::WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)src, -1, dst, sizeof(dst)-1, NULL, NULL);直接変換する方法はない
(STL) string直接変換する方法はない直接変換する方法はない
CStringdst = src;
dst = CString(src);
CString dst(src);
dst = (BSTR)src;
dst = CString((BSTR)src);
CString dst((BSTR)src);
BSTRdst = src.Copy();

::SysFreeString(dst);
CComBSTRdst = src;
dst = CComBSTR(src);
CComBSTR dst(src);

マルチバイト文字の判定 Edit

_ismbblead(),_ismbbtrail() を使用すると、「その文字がマルチバイト文字の第1バイト/第2バイトに相当するか」をチェック可能。
しかし、マルチバイト文字の第1バイトと第2バイトの取り得る数値の範囲は重複しているため、この関数では「そのバイトが第1バイトと第2バイトのどちらか」という判定には使えない。

例)「り」(0x82E8) の第2バイトである「0xE8」は、第1バイト/第2バイトどちらでも使用される値である。

その場合は、_ismbslead(), _ismbstrail() を使用し、文脈から判定させることで解決できる。
ただし、指定した文字列全体のチェックを行うため、パフォーマンスは低いことに注意。

ファイル処理 Edit

ファイル(ドライブ、ディレクトリ)の存在確認 Edit

ファイルのプロパティダイアログの表示 Edit

ファイルを右クリックして「プロパティ」を選択したときのダイアログのこと。::ShellExecuteEx() を使用する。

フォルダの属性変更 Edit

なぜかできない。CFile::SetStatus() だと例外が発生し、::SetFileAttributes() だと何も起こらず変更もされない。

デバイスコンテキスト/画像処理 Edit

用途別のデバイスコンテキスト Edit

MFCクラス用途備考
CPaintDC更新領域のみに対して描画する場合に使用する。・コンストラクタでBeginPaint()、デストラクタでEndPaint()が呼び出されているため、クリッピングリージョンが自動的に設定される。(WS_CLIPSIBLINGSスタイルで兄弟ウィンドウをクリップにしている場合など、自動的に適用される)
・OnPaint()の処理内でしか使用できない。OnDraw()の引数で渡されるpDcも元はこれ。(OnDraw()はOnPaint()から呼び出されている)
CClientDCウィンドウのクライアント領域全体に対して描画する場合に使用する。・コンストラクタでGetDC()、デストラクタでReleaseDC()が呼び出される。
・領域のクリッピング等は自前で行う必要がある。
CWindowDCウィンドウの非クライアント領域(キャプションやメニューなど)も含めた領域全体に対して描画する場合に使用する。・コンストラクタでGetWindowDC()、デストラクタでReleaseDC()が呼び出される。
・領域のクリッピング等は自前で行う必要がある。

ダブルバッファリング Edit

1つ1つの描画処理を行うたびに画面が更新されてしまうことによってチラつきが発生してしまう。そこで、描画処理は裏画面に対して行っておき、描画処理が終わった時に画面更新を1度だけ行うことでチラつきを防ぐ手法がダブルバッファリング。以下はダブルバッファリングの実装例。

  1. ウィンドウ(ダイアログ)クラスのメンバ変数として以下を定義しておく。
      1
      2
      3
      4
      5
      6
    
        // CStatic  m_PcCtrl        ピクチャコントロール
     
        CDC         m_memDC;            // メモリデバイスコンテキスト(裏画面)
        CBitmap     m_memBmp;           // メモリデバイスコンテキストの実体ビットマップ
        CBitmap     *m_pOldBmp;         // メモリデバイスコンテキストの旧実体
        CRect       m_PcRect;           // キャンバスの矩形領域
    
  2. アプリケーションの初期処理で、デバイスコンテキストと裏画面の設定を行う。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
    
        CDC     *pDC;   // デバイスコンテキスト
     
        // コントロールの矩形領域の設定
        m_PcCtrl.GetClientRect(&m_PcRect);
     
        // ピクチャコントロールのデバイスコンテキストを取得
        pDC = m_PcCtrl.GetDC();
     
        // メモリデバイスコンテキストの作成
        m_memDC.CreateCompatibleDC(pDC);
        // メモリデバイスコンテキストの実体を作成して関連付ける
        m_memBmp.CreateCompatibleBitmap(pDC, m_PcRect.Width(), m_PcRect.Height());
        m_pOldBmp = m_memDC.SelectObject(&m_memBmp);
     
        // デバイスコンテキスト解放
        m_PcCtrl.ReleaseDC(pDC);
  3. アプリケーションの終了処理で、デバイスコンテキストと裏画面の解放を行う。
      1
      2
      3
      4
      5
      6
    
        // メモリデバイスコンテキストのビットマップを元に戻す
        m_memDC.SelectObject(m_pOldBmp);
        // 実体ビットマップを削除
        m_memBmp.DeleteObject();
        // メモリデバイスコンテキストを削除
        m_memDC.DeleteDC();
  4. CWnd::OnPaint() 等の再描画処理で、裏画面からの転送処理を行う。
      1
      2
      3
      4
      5
      6
    
        CPaintDC dc(this); // device context for painting
        // TODO : ここにメッセージ ハンドラ コードを追加します。
        // 描画メッセージで CStatic::OnPaint() を呼び出さないでください。
     
        // ピクチャコントロールにメモリデバイスコンテキストの内容を転送
        dc.BitBlt(0, 0, m_PcRect.Width(), m_PcRect.Height(), &m_memDC, 0, 0, SRCCOPY);
  5. 実際の描画処理。
    描画は裏画面に対して行い、最後にウィンドウの再描画を促す。
      1
      2
      3
      4
      5
    
        // m_memDC に対して描画処理を行う
     
        // 再描画
        InvalidateRect(NULL, FALSE);    // 領域を無効化
        UpdateWindow();                 // 領域を無効化して呼ぶことで、WM_PAINTを送信する
    

ウィンドウキャプチャ(ハードコピー) Edit

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
    // CWnd     *pWnd   キャプチャ対象のウィンドウクラス
 
    CDC         capDC;          // ウィンドウキャプチャのDC
    CBitmap     capBmp;         // ウィンドウキャプチャの実体ビットマップ
    CBitmap     *pBmpOld;       // 旧Bitmap
    CRect       wndRect;        // ウィンドウの矩形
 
    CWindowDC   wndDC(pWnd);    // ウィンドウDC
 
    // ウィンドウの矩形を取得
    pWnd->GetWindowRect(&wndRect);
 
    // ウィンドウキャプチャのDCの作成
    capDC.CreateCompatibleDC(NULL);
    // 実体ビットマップの作成
    capBmp.CreateBitmap(wndRect.Width(), wndRect.Height(), wndDC.GetDeviceCaps(PLANES), wndDC.GetDeviceCaps(BITSPIXEL), NULL);
    // ウィンドウキャプチャのDCに実体を関連付ける
    pBmpOld = capDC.SelectObject(&capBmp);
    capDC.SetMapMode(MM_TEXT);  // マップモードは1ピクセル単位
 
    // キャプチャ
    capDC.BitBlt(0, 0, wndRect.Width(), wndRect.Height(), &wndDC, 0, 0, SRCCOPY);
 
    // capBmp にキャプチャされた画像が保持されている
    // @TODO
 
    // メモリデバイスコンテキストの実体を戻す
    capDC.SelectObject(pBmpOld);
    // メモリデバイスコンテキストを削除
    capDC.DeleteDC();
    // ビットマップを削除
    capBmp.DeleteObject();

ビットマップのコピー Edit

メモリ上のビットマップを複製する方法。

DCの内容をビットマップファイルとして保存 Edit

ビットマップファイルは次のような構造になっている。

  1. BITMAPFILEHEADER
  2. BITMAPINFOHEADER
  3. (カラーパレット)
  4. ピクセルデータ

上記の順番でバイナリデータをファイルに保存していくことで、ビットマップファイルを作成できる。
以下に、24bitフルカラーでのビットマップファイルの保存処理の例を示す。
※フルカラーの場合、カラーパレットは格納しない。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
    // LPCTSTR  szFilePath  保存ファイルパス
    // HBITMAP  hBmp        保存対象のBitmapハンドル
    // HDC      hDC         デバイスコンテキスト
 
    DWORD               dwFileAttr;         // ファイル属性
    HANDLE              hFile;              // Bitmapファイルハンドル
    DWORD               dwWritten;          // ファイル書込み時の結果の値
    DWORD               dwInfoHdrSize;      // BITMAPINFOHEADER(+カラーパレット)のサイズ
    DWORD               dwScanDataSize;     // ピクセルデータのサイズ
    BITMAPFILEHEADER    bmpFileHeader;      // BITMAPFILEHEADER
    BITMAPINFOHEADER    *pBmpInfoHdr;       // BITMAPINFOHEADER
    BYTE                *pHeaderBuffer;     // BITMAPINFOHEADER(+カラーパレット)の実体
    BYTE                *pScanDataBuffer;   // ピクセルデータの実体
    BITMAP              bmp;                // Bitmap実体
    BOOL                bRet;               // 戻り値
 
    // ハンドルからBitmapの実体を取得
    GetObject(hBmp, sizeof(BITMAP), &bmp);
 
    //////// BITMAPINFOHEADER ////////
 
    // BITMAPINFOHEADERのサイズを決定
    dwInfoHdrSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 0; // フルカラーの場合はカラーパレットを使用しない
    // BITMAPINFOHEADERの実体を作成
    pHeaderBuffer = new BYTE[dwInfoHdrSize];
    memset(pHeaderBuffer, 0, dwInfoHdrSize);
    pBmpInfoHdr = (BITMAPINFOHEADER*)pHeaderBuffer;
 
    // 情報を格納
    pBmpInfoHdr->biSize = sizeof(BITMAPINFOHEADER);     // BITMAPINFOHEADERのサイズ
    pBmpInfoHdr->biBitCount = 24;                       // 1ピクセルあたりのビット数(画像の色数)    フルカラーは24bit
    pBmpInfoHdr->biWidth  = bmp.bmWidth;                // Bitmap画像の幅 [pixel]
    pBmpInfoHdr->biHeight = bmp.bmHeight;               // Bitmap画像の高さ [pixel]
    pBmpInfoHdr->biPlanes = 1;                          // 1を格納する
    pBmpInfoHdr->biCompression = BI_RGB;                // 圧縮状態             BI_RGB:非圧縮
    pBmpInfoHdr->biSizeImage = 0;                       // 画像サイズ [Byte]    後で::GetDIBits()で取得する
    pBmpInfoHdr->biXPelsPerMeter = 0;                   // X方向の解像度
    pBmpInfoHdr->biYPelsPerMeter = 0;                   // Y方向の解像度
    pBmpInfoHdr->biClrUsed = 0;                         // 色の使用数           最大数使用の場合は0を格納
    pBmpInfoHdr->biClrImportant = 0;                    // 色の必要数           全て必要な場合は0を格納
 
    //////// ピクセルデータ ////////
 
    // pBmpInfoHdr->biSizeImageを取得する
    GetDIBits(hDC, hBmp, 0, bmp.bmHeight, NULL, (LPBITMAPINFO)pBmpInfoHdr, DIB_RGB_COLORS);
    // 取得した画像サイズでピクセルデータのバッファを用意する
    dwScanDataSize = pBmpInfoHdr->biSizeImage;
    pScanDataBuffer = new BYTE[dwScanDataSize];
    // ピクセルデータを取得
    GetDIBits(hDC, hBmp, 0, bmp.bmHeight, pScanDataBuffer, (LPBITMAPINFO)pBmpInfoHdr, DIB_RGB_COLORS);
 
    //////// BITMAPFILEHEADER ////////
 
    bmpFileHeader.bfType = 0x4d42;                                      // ファイル形式     文字列"BM"をリトルエンディアンで格納
    bmpFileHeader.bfReserved1 = 0;                                      // 予約メンバ       0を格納する
    bmpFileHeader.bfReserved2 = 0;                                      // 予約メンバ       0を格納する
    bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + dwInfoHdrSize; // ピクセルデータの位置 [Byte]
    bmpFileHeader.bfSize = dwScanDataSize + bmpFileHeader.bfOffBits;    // ファイルサイズ [Byte]
 
    //////// Bitmapファイル作成 ////////
 
    hFile = CreateFile(szFilePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile != INVALID_HANDLE_VALUE){
        WriteFile(hFile, &bmpFileHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);   // BITMAPFILEHEADER
        WriteFile(hFile, pHeaderBuffer, dwInfoHdrSize, &dwWritten, NULL);               // BITMAPINFOHEADER (+カラーパレット)
        WriteFile(hFile, pScanDataBuffer, dwScanDataSize, &dwWritten, NULL);            // ピクセルデータ
        CloseHandle(hFile);
    }
     
    // メモリ解放
    delete[] pScanDataBuffer;
    delete[] pHeaderBuffer;

描画時の兄弟ウィンドウのクリッピング Edit

WS_CLIPSIBLINGSスタイルを指定すると、OnPaint()やOnDraw()に用意される標準のデバイスコンテキストは兄弟ウィンドウがクリップ(兄弟ウィンドウの上に描画処理を行わないようにリージョンを除外すること)された状態となる。理由は、BeginPaint() によって作られたDCのため。(WS_CLIPCHILDRENスタイルの場合も同様に、子ウィンドウがクリップされた状態になると思われる。)
しかし、CClientDCでクライアント領域全体のデバイスコンテキストを取得してそれに対して描画を行うような場合は、常に領域全体が描画対象になっているため、必要に応じて自前でクリップ処理を作成するしかない。
自作コントロールの描画処理を行う場合などは注意が必要。

以下、兄弟ウィンドウをクリップする処理を自前で作成する場合の例。
(※コントロール描画用のデバイスコンテキストとして CClientDC型 のメンバ変数 m_pDC が用意されているものとする。)

フォント Edit

フォントを変更する際の注意点 Edit

フォントの設定にCFontクラスのオブジェクトを用いる場合、メンバ変数にしておく必要がある。
ローカル変数だと、スコープを抜けたときに変数が自動的に破棄され、デストラクタの働きによってフォントの内容が削除されてしまうため、正しくフォントが反映されない。

単位系 Edit

コンピュータの世界でよく用いられる長さに関する単位。

単位説明換算
pixel文字や画像を表示する際の最小要素。画素。
ディスプレイ上ではdotと同義。
-
TwipもとはVBでフォーム設計する際に用いられる長さの単位。
リッチエディットコントロール上ではこの単位がもちいられる。(要確認)
mmやinchよりも細かく指定できる。
1Twip = 1/20point = 1/1440inch
pointフォントの大きさを表す1point = 1/72inch
inch一般的に使われる長さ。コンピュータの世界ではmmより使われる頻度が高い?-
dpidot per inch。1inchあたりのdot数。
ディスプレイ上では1inchあたりのpixel数と同義。
PCの解像度は通常96dpi。
-

セル高さと文字高さ Edit

アクセント記号を含めた高さをセル高さ(Cell Height)、含めない高さを文字高さ(Char Height)と呼ぶ。

(MSのサイトから抜粋 http://support.microsoft.com/kb/32667/ja)

 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

                     O    O
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

                       /\ 
                      /  \ 
                     /    \ 
 _ _ _ _ _ _ _ _ _  /______\ _ _ _ ___   _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                   /        \     /   \|
                  /          \   |     |
 _ _ _ _ _ _ _ _ /_ _ _ _ _ _ \ _ \___/| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                                       |
                                       |
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\___/_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
                                                      |
                                                      |- External Leading
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _
                                                      |
                     O    O                           |- Internal Leading
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _
              |        /\ 
              |       /  \ 
              |      /    \ 
              |     /______\ _ _ _ ___   _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
 Char Height -|    /        \     /   \|
    (Em)      |   /          \   |     |
              |  /_ _ _ _ _ _ \ _ \___/| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
              |                        |
              |                        |
 _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _\___/_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

              |                                           |
              |      O    O                               |
              |                                           |
              |        /\                                 |
              |       /  \                                |
              |      /    \                               |- Ascent
 Cell Height -|     /______\       ___                    |
              |    /        \     /   \|                  |
              |   /          \   |     |                  |
              |  /_ _ _ _ _ _ \ _ \___/| _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _
              |                        |                  |
              |                        |                  |- Descent
 _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _\___/_ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _

LOGFONT構造体によるフォントサイズの指定 Edit

LOGFONT:lfHeightでフォントの高さ、LOGFONT:lfWidthでフォントの幅を指定する。

DLL Edit

リソースのみを含むDLL Edit

ソースコード(エントリポイント)を持たないDLLを作るには、プロジェクト設定の「リンク」タブの「一般」カテゴリで、「プロジェクトオプション」に "/NOENTRY" を追加する。

DLL内での実行ファイルのパス取得 Edit

WIN32APIの ::GetModuleFileName() を使用することで、実行ファイルのパスを取得できる。
DLL内部のコードから ::GetModuleFileName() を使用した場合、第1引数にDLLのインスタンスハンドルを渡せばDLLのパスが取得できるが、NULLを渡した場合は呼び出し元のEXEのパスの取得となる。

DLLからモードレスダイアログを表示 Edit

DLLからモードレスダイアログを表示する場合、タブストップやキー入力等の処理がデフォルトで行われない。→参考
モーダルダイアログのようにタブストップ等を処理させるには、メッセージをフックする必要がある。

  1. グローバルな変数に、対象となるダイアログクラスオブジェクトのアドレスを渡しておく。
      1
    
    CXxxDlg    *g_pDlg;
  2. ダイアログクラスのヘッダファイルで、フックハンドルとフックプロシージャを定義する。
      1
      2
      3
      4
      5
      6
      7
    
    public:
        // フックハンドル
        HHOOK    m_hHook;
        
    private:
        // メッセージ監視フックプロシージャ
        static long CALLBACK GetMsgProc(int, WPARAM, LPARAM);
  3. CXxxDlg::OnInitDialog() でフックプロシージャを登録する。
      1
      2
    
        // メッセージ監視フックプロシージャ
        m_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)CXxxDlg::GetMsgProc, NULL, ::GetCurrentThreadId());
  4. CXxxDlg::OnDestroy() でフックプロシージャを破棄する。
      1
      2
    
        // フックプロシージャの破棄
        ::UnhookWindowsHookEx(m_hHook);
  5. CXxxDlgクラスのソースで、フックプロシージャを実装する。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
    
    // メッセージ監視フックプロシージャ
    long CXxxDlg::GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) 
    {
        LPMSG    lpMsg;
     
        // メッセージを取得
        lpMsg = (LPMSG)lParam;
        
        // メッセージを処理する必要あり
        if(nCode >= 0 && wParam == PM_REMOVE){
     
            // キー関連メッセージ
            if((lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST)){
     
                // モードレスダイアログ用メッセージを処理させる
                if(g_pDlg->IsDialogMessage(lpMsg)){
     
                    // 処理済みなら WM_NULL とする
                    lpMsg->message = WM_NULL;
                    lpMsg->lParam  = 0;
                    lpMsg->wParam  = 0;
                }
            }
        }
        
        // 次のフックプロシージャへ
        return ::CallNextHookEx(g_pDlg->m_hHook, nCode, wParam, lParam);
    }

DLL内でMFCリソースの使用 Edit

DLL内でダイアログを作成して表示するコードを用意して使用する場合、exportする関数の先頭に以下のコードを記述する必要がある。(COMのDLLも同様)

  1
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

EXEからexportした関数が呼び出された状態では、リソースハンドル等はEXE用のものを使用する状態になっているため、DLL内では正しい処理が行われない。
上記のコードによって、モジュール状態がDLL用のものに切り替わるため、正しい処理が行える。また、スコープを抜けるときに元の状態に戻してくれるため、EXE側に処理が帰った後も問題がない。

DLLのデバッグ方法 Edit

DLLは親となるプロセスにロードされるため、呼び出し側のEXEが必要になる。

[注意点]

  1. 呼び出し側EXEも含めてデバッグ
    呼び出し側EXEのソースが手元にある場合に有効。
    1. 呼び出し側EXEとデバッグ対象DLLのプロジェクトを1つのワークスペースにまとめておく。
    2. 呼び出し側EXEのデバッグを開始する。
    3. デバッグ対象DLL内のコードにブレークポイントを貼っておき、その位置まで実行する。
  2. 呼び出し側EXEを指定してデバッグ
    呼び出し側EXEが単純に起動できる場合に有効。
    1. デバッグ対象DLLのプロジェクトを開き、プロジェクト設定の「デバッグセッションの実行可能ファイル」に呼び出し側EXEのパスを指定する。
    2. デバッグ対象DLL内のコードにブレークポイントを貼っておき、デバッグを開始する。
  3. 呼び出し側EXEのプロセスにアタッチしてデバッグ
    呼び出し側EXEの起動タイミングを制御できない場合(常駐など)に有効
    1. 呼び出し側EXEが起動された状態にする。
    2. VisualStudioを新規に起動し、「プロセスにアタッチ」で呼び出し側EXEのプロセスを指定してデバッグを開始する。
    3. VisualStudioにデバッグ対象DLLのソースファイルをドラッグ&ドロップし、ブレークポイントを貼り、その位置まで実行する。

LIB Edit

libファイルのリンク Edit

プロジェクトにlibファイルをリンクするには以下の3つの方法がある。

  1. プロジェクトにlibファイルを追加する。コマンドラインでのビルドであれば、オブジェクトファイルと一緒にlibファイルを指定する。
  2. プロジェクトの設定でリンク指定する。
  3. プリプロセッサで指定する。
      1
    
        #pragma comment(lib, "xxx.lib")

自作のスタティックライブラリ Edit

自作クラスなどの汎用的なコードは、スタティックライブラリ化しておくと再利用しやすい。

  1. スタティックライブラリ側のプロジェクト
    1. スタティックライブラリのプロジェクトを作り、ヘッダとソースを追加する。
    2. ヘッダファイルの置き場所はあらかじめ決めておく。複数のライブラリのプロジェクトがあっても、ヘッダは一ヵ所にまとめる。
    3. 出力ファイルであるLibファイルもあらかじめ決めた場所に出力する。Debug版のLibファイルも、名前を変えて同じ場所に出力させる。
  2. アプリケーション側のプロジェクト(Libファイルを使用する側)
    1. プロジェクトの設定で、リンクするLIBファイルを設定する。(Debug版はDebug版のLibファイルを使用する)
    2. ワークスペースに使用するライブラリのプロジェクトを含め、さらに依存関係を設定しておくと常に最新のライブラリファイルをリンクできる。
  3. VC++の設定
    1. ヘッダファイル、Libファイルを置いたパスをディレクトリ設定に追加する。

リソーススクリプト Edit

その他 Edit

実行対象のWindowsバージョン Edit

Win32API等のOSの機能を使用する場合、OSのバージョンによって利用できる機能と利用できない機能が存在するため、例えばWinXPから実装された機能を使用するアプリケーションは、Win2kでは動作が保証されないことになる。
つまり、アプリケーションを作成する場合、実行対象のWindowsの最小バージョンを決めておき、それより後のバージョンで実装された機能を使用しないようにする必要がある。

そこで、Windows用のヘッダファイルには以下のマクロが用意されており、定義された値によって機能が限定されるように作られている。アプリケーションを作る場合、使用する機能によってはこのマクロの値を適宜設定する必要がある。(stdafx.h内のWindows用のヘッダファイルのインクルードよりも前に定義する)

参考URL

コマンドライン引数 Edit

二重起動防止 Edit

CWinApp 派生クラスの InitInstance() で、ウィンドウ起動処理に以下を追加。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
    CString strExeName(m_pszExeName);
 
    // 大文字化
    strExeName.MakeUpper();
 
    // アプリケーションの二重起動防止
    CMutex    mutex(FALSE, strExeName);
    if(!mutex.Lock(0)) {
        // 既に起動されているときの処理
 
        // 起動しているアプリケーションのウィンドウをアクティブにして終了する場合の例
        HWND hWnd;
        if((hWnd = ::FindWindow(NULL, "対象ウィドウのタイトル")) != NULL){
            // ウィンドウが最大化/最小化している場合は元に戻す
            ::ShowWindow(hWnd, SW_RESTORE);
            // ウィンドウを最前面化する
            ::SetForegroundWindow(hWnd);
        }
 
        return FALSE;
    }
 
    // 正常時の起動処理
    // :
 
    // ミューテックスを解放
    mutex.Unlock();

※ <afxmt.h>をインクルードする必要がある。
※ ミューテックス作成時に渡す文字列は大文字小文字を区別することに注意。

処理中の砂時計カーソル Edit

カーソルの変更 Edit

外部プログラムの起動 Edit

Win32APIやMFCのメンバ関数の戻り値 Edit

クリップボードの読み書き Edit

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
    // HBITMAP hBmpは、ビットマップデータのハンドル
 
    // クリップボードを開く
    if(::OpenClipboard(hWnd)){
        // クリップボードをクリアして所有権を得る
        if(::EmptyClipboard()){
            // ビットマップをクリップボードに設定
            ::SetClipboardData(CF_BITMAP, hBmp);
        }
        // クリップボードを閉じる
        ::CloseClipboard();
    }
 
    // ここでhBmpの画像データを変更すると、クリップボードに設定された画像データも変更されることになる

アクセラレータ Edit

アプリケーションのショートカットキーを実現するアクセラレータの使用方法。

  1. リソースにアクセラレータテーブルを追加する。(例:IDR_ACCELERATOR)
  2. アクセラレータを関連付けるウィンドウ(ダイアログ)クラスの CWnd::PreTranslateMessage() をオーバライドし、以下のコードを追加する。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
    
        BOOL CxxxxDlg::PreTranslateMessage(MSG* pMsg) 
        {
            // TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
            HACCEL  hAccKey;      // アクセラレータテーブルハンドル
     
            // アクセラレータテーブルハンドルを取得
            hAccKey = ::LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR)); 
            if (hAccKey != NULL){
                // アクセラレータ処理
                if (::TranslateAccelerator(this->m_hWnd, hAccKey, pMsg)){
                    // アクセラレータとして処理された場合は、TRUEを返して終了
                    return TRUE;
                }
            }
     
            return CDialog::PreTranslateMessage(pMsg);
        }
  3. アクセラレータテーブルにショートカットキーを登録する。(例:「Ctrl+C」→ ID_ACCEL_CTRL_C)
  4. クラスウィザードで、アクセラレータをウィンドウ(ダイアログ)クラスに関連付け、COMMANDメッセージのイベントハンドラを作成する。

タスクトレイ常駐 Edit

タスクトレイに常駐するアプリケーションの基本的な作成方法。

  1. リソースに、タスクトレイに表示するアイコンと、ポップアップ用のメニューを追加する。
    1. アイコン
      アイコンを切り替える場合は、複数用意しておく。(例:IDR_MAINFRAME, IDR_SUBICON)
    2. ポップアップメニュー
      通常のメニューと同様に追加する。ポップアップ用メニューはサブメニューとして定義する必要がある。(例:IDR_POPUPMENU)
  2. ポップアップメニューの項目のイベントハンドラを実装しておく。
  3. ウィンドウ(ダイアログ)クラスのメンバ変数として以下を定義しておく。
      1
    
        NOTIFYICONDATA      m_notifyIcon;   // タスクトレイのアイコンデータ
    
  4. タスクトレイのアイコンがクリックされた場合のユーザ定義メッセージを定義する。
      1
    
        #define     WM_USER_ACTION      (WM_USER + 100)
  5. アプリケーションの初期処理で、メインウィンドウを非表示し、タスクトレイにアイコンを設定する。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
    
        // ウィンドウの表示を消す
        ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW);           // タスクバーにも表示しない
        SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_HIDEWINDOW);          // ウィンドウ非表示
     
        // タスクトレイのアイコンデータを設定
        memset(&m_notifyIcon, 0, sizeof(NOTIFYICONDATA));
        m_notifyIcon.cbSize = sizeof(NOTIFYICONDATA);               // お約束
        m_notifyIcon.uID = 0;                                       // タスクトレイに複数のアイコンを同時に格納する場合はそれぞれユニークな値を設定する
        m_notifyIcon.hWnd = m_hWnd;                                 // ウィンドウハンドル
        m_notifyIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;     // メンバの hIcon, uCallbackMessage, szTip を有効にする
        m_notifyIcon.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);  // アイコン
        m_notifyIcon.uCallbackMessage = WM_USER_ACTION;             // ユーザ定義メッセージ
        lstrcpy(m_notifyIcon.szTip, "MAINモード");                  // マウスカーソルを置いたときのチップ
     
        // アイコンをタスクトレイに格納する
        ::Shell_NotifyIcon(NIM_ADD, &m_notifyIcon);
  6. CWnd::WindowProc() 等のウィンドウプロシージャにユーザ定義メッセージのハンドラを追加する。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
    
        POINT   pntCur;         // マウスカーソルの位置
        CMenu   mainMenu;       // メインメニュー
        CMenu   *pPopupMenu;    // ポップアップメニュー
     
        // メッセージ処理
        switch(message){
     
        // タスクトレイアイコンのユーザ定義メッセージ
        case WM_USER_ACTION:
            // wParam : m_notifyIcon.uIDの値
            // lParam : マウスメッセージ
     
            // カーソルの現在位置を取得
            ::GetCursorPos(&pntCur);
     
            // 左クリック
            if(lParam == WM_LBUTTONDOWN){
                // メニューのロード
                mainMenu.LoadMenu(IDR_MENU_POPUP);
                // ポップアップメニューのロード
                pPopupMenu = mainMenu.GetSubMenu(0);
                if(pPopupMenu != NULL){
     
                    // メニュー項目のグレーアウト、チェックマークなどはここで行う
     
                    // タスクトレイのポップアップメニュー表示前は、最前面表示にしておく(やらないと挙動がおかしくなる)
                    SetForegroundWindow();
                    // ポップアップメニュー表示
                    pPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pntCur.x, pntCur.y, this);
                    // タスクトレイのポップアップメニュー表示後のお約束(やらないと挙動がおかしくなる)
                    PostMessage(WM_NULL);
                }
            // 右クリック
            } else if(lParam == WM_RBUTTONDOWN){
     
            //////// 以降、バルーンチップ関連 (※VC++6.0では非対応) ////////
            // バルーンが表示されたとき
            } else if(lParam == NIN_BALLOONSHOW){
     
            // バルーンがタイムアウトで消えたとき
            } else if(lParam == NIN_BALLOONTIMEOUT){
     
            // バルーンが通知領域のクリックにより消えたとき
            } else if(lParam == NIN_BALLOONUSERCLICK){
     
            // バルーンが×ボタンクリックにより消えたとき
            } else if(lParam == NIN_BALLOONHIDE){
     
            }
            break;
        }
  7. アプリケーションの終了処理で、タスクトレイからアイコンを削除する。
      1
      2
    
        // タスクトレイのアイコンを削除
        ::Shell_NotifyIcon(NIM_DELETE, &m_notifyIcon);
  8. タスクトレイのアイコンに対する処理の例。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
    
        //////// バルーンチップの例 (※VC++6.0では非対応) ////////
        m_notifyIcon.uFlags |= NIF_INFO;                // バルーンチップ用のメンバを有効にする
        m_notifyIcon.uTimeout = 3000;                   // 表示時間 3000ms
        m_notifyIcon.dwInfoFlags = 0L;                  // バルーンチップアイコンなし
        lstrcpy(m_notifyIcon.szInfoTitle, "タイトル");  // バルーンのタイトル
        wsprintf(m_notifyIcon.szInfo, "メッセージ");    // バルーンのメッセージ
                                                        // ※メッセージを空にするとバルーンが無効になる
        // バルーンチップ表示
        ::Shell_NotifyIcon(NIM_MODIFY, &m_notifyIcon);
     
        //////// タスクトレイアイコンの状態変更の例 ////////
        m_notifyIcon.hIcon = AfxGetApp()->LoadIcon(IDR_SUBICON);    // アイコンの変更
        lstrcpy(m_notifyIcon.szTip, "SUBモード");                   // チップの変更
        // 変更を反映
        ::Shell_NotifyIcon(NIM_MODIFY, &m_notifyIcon);

プロセス間共有メモリ Edit

通常、異なるアプリケーション間では別々のメモリ空間が使用されるため、メモリを共有することができない。(アプリAのメモリアドレス0x00001111は、アプリBのメモリアドレス0x00001111とは別の領域である)
そこで、OS側に仮想的なメモリ空間を作成してもらい、そのメモリ空間をそれぞれのアプリケーションが自プロセスのメモリ空間にマッピングして使用することで、あたかも同じメモリを操作しているように振る舞うことができる。
アプリAが共有メモリの内容を変更すると、それをマッピングしている全てのプロセスの共有メモリが同じように変更される。

変更が即時反映されるファイルを複数プロセスで同時にオープンしているイメージ。共有メモリは実体がない(正確にはOSが管理するメモリ空間に実体を持つらしい)ため、一意な名前をつけて識別する必要がある。

  1. 共有ファイル作成に使用する変数等の定義
      1
      2
      3
      4
      5
    
    #define    FMAP_KEY    "TEST_FMAP"                // 共有メモリの識別名
    #define    FMAP_SIZE    (sizeof(char) * 100)    // サイズ:100バイト
     
    HANDLE    hFileMap;        // 共有メモリのハンドル
    char    *pData;            // 共有メモリのマッピング先アドレス
    
  2. 共有ファイルの使用開始
      1
      2
      3
      4
      5
      6
      7
    
        // 共有メモリの実体を作成する
        // ※既に実体が作成されている場合はオープンとなる。
        //  明示的にオープンする場合は::OpenFileMapping()を使用する。
        hFileMap = ::CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, FMAP_SIZE, FMAP_KEY);
     
        // 共有メモリのビューをメモリ空間にマッピングする
        pData = (char*)::MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, FMAP_SIZE);
  3. 共有ファイルの使用終了
      1
      2
      3
      4
      5
      6
      7
    
        // 共有メモリのビューのマッピングを終了
        ::UnmapViewOfFile(pData);
        pData = NULL;
     
        // 共有メモリのハンドルを閉じる
        ::CloseHandle(hFileMap);
        hFileMap = NULL;
    

アプリケーションのアイコン設定 Edit

アプリケーションのバージョン情報の取得 Edit

対象の実行ファイルのパスから、バージョン情報を取得できる。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
    // char szPath[]   モジュールのパス
 
    // バージョン情報リソースのサイズを取得
    DWORD    dwZero = 0;
    DWORD    dwVerInfoSize;
    dwVerInfoSize = ::GetFileVersionInfoSize(szPath, &dwZero);
    if(dwVerInfoSize == 0) {
        return FALSE;
    }
 
    // バージョン情報リソースを取得
    BYTE    *pVerInfo;
    pVerInfo = new BYTE[dwVerInfoSize];
    if(pVerInfo == NULL){
        return FALSE;
    }
    ::GetFileVersionInfo(szPath, dwZero, dwVerInfoSize, pVerInfo);
 
    // 各バージョン情報を取得
    void    *pvVersion;
    UINT    VersionLen;
 
    // 以下の形式で取得するバージョン情報を指定する
    // 「\StringFileInfo\[①言語識別子&コードページ識別子(16進文字列)]\[②定義済みバージョン情報識別名]」
    // ①は、日本語の場合"041104b0"となる
 
    // FileVersionを取得
    CString    strFileVersion;
    ::VerQueryValue(pVerInfo, "\\StringFileInfo\\041104b0\\FileVersion", &pvVersion, &VersionLen);
    strFileVersion = (LPCTSTR)pvVersion;
 
    delete[] pVerInfo;
    return TRUE;

CreateProcess()を使用する場合のリダイレクトの方法 Edit

外部コマンドを起動して標準出力をリダイレクトする方法。(test.exe > stdou.txt)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
    // HANDLE    hFile    // リダイレクト先のハンドル(テキストファイルなど)
 
    BOOL                bRet;
    DWORD                dwEndCode;
    PROCESS_INFORMATION    pi;
    STARTUPINFO            si;
 
    // 起動情報
    ZeroMemory(&si, sizeof (si));
    si.cb = sizeof(si);
    si.dwFlags |= STARTF_USESTDHANDLES;    // hStdOutputの使用フラグ
    si.hStdOutput = (HANDLE)hTxtFile;    // リダイレクト先に指定する
 
    // プロセス起動 (第5引数をTRUEにする)
    bRet = ::CreateProcess(NULL, "test.exe", NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
 
    if(bRet){
        // プロセスの終了を待つ
        ::WaitForSingleObject(pi.hProcess, INFINITE);
 
        // プロセスの終了コードを取得
        ::GetExitCodeProcess(pi.hProcess, &dwEndCode);
    }

※標準出力のリダイレクトを追記させる場合は、予めファイルを追記モードで開き、シークを終端に移動しておく手順が必要。(test.exe >> stdou.txt)

同様の手順で、標準入力や標準エラー出力もリダイレクト可能。

ファイルのアイコン表示(エクスプローラと同じ表示) Edit

リストビュー、ツリービュー、タブなどのコントロールにアイコンを表示する場合は、イメージリストを使用した方法を使用する必要がある。
あらかじめ、CListCtrl::SetImageList()、ListView_SetImageList() などでイメージリストをコントロールに登録しておき、CListCtrl::InsertItem()、ListView_InsertItem() でアイテムを挿入する際にアイコンのインデックスを指定することで表示される。

※コントロールにシステムイメージリストを使用する場合は、ウィンドウスタイルに LVS_SHAREIMAGELISTS (リストビューコントロールの場合) を設定しておくこと。

同期処理/排他制御 Edit

セマフォミューテックスクリティカルセクション
同時に所有できる数上限を設定可能1つまで1つまで
使用可能な範囲プロセスをまたいで使用可能プロセスをまたいで使用可能同一プロセス内でのみ使用可能
Win32APIオブジェクトの作成CreateSemaphoreCreateMutexInitializeCriticalSection
所有権の取得WaitForSingleObject に代表される待機関数EnterCriticalSection
所有権の破棄ReleaseSemaphoreReleaseMutexLeaveCriticalSection

C/C++ネイティブ Edit

C/C++共通 Edit

定義済みプリプロセッサマクロ Edit

カテゴリマクロ名意味
ANSI C
(C90)
__STDC__標準規格のC言語であることを示す
K&Rとの区別に用いる
__FILE__ソースファイルパス
__LINE__ソースの行番号
__TIMESTAMP__コンパイル日時
__DATE__コンパイル日
__TIME__コンパイル時刻
C99__func__関数名
C++__cplusplusC++であることを示す
値が1とは限らない
文字セット
(どちらか一方)
_MBCSマルチバイト文字セットであることを示す
_UNICODEワイド文字(Unicode)セットであることを示す
ビルド構成
(どちらか一方)
_DEBUGDebug版
NDEBUGRelease版

const修飾子の効力 Edit

ある関数Aでconst修飾された引数を、さらに関数Bの非constな引数として渡すことができてしまう。もちろん関数B内で値の書き換えも可能。
コンパイラの警告レベルによっては、警告は出力される。

CALLBACK関数の定義 Edit

CALLBACK関数は、グローバル関数やstaticメンバ関数などの静的な関数として定義する必要がある。
その他、関数ポインタを使用する仕組みも同様。
クラスのメンバ関数はインスタンスが生成されるまでアドレス空間のどの位置に配置されるかが決まっていないため、ポインタにアドレスを格納できない。

ワイルドカード(*, ?)を用いた文字列検索 Edit

参考:http://katsura-kotonoha.sakura.ne.jp/prog/c/tip00003.shtml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
// ワイルドカードを使用した文字列検索
// ※BOOLはintで代用可
BOOL strmatch(const char *ptn, const char *str)
{
    BOOL    bmatch = FALSE;
 
    switch(*ptn){
 
    // パターン文字列の末尾
    case '\0':
        // 検索対象文字列も末尾なら真
        bmatch = (*str == '\0');
        break;
 
    // * : 0文字以上の任意の文字列
    case '*':
        // '*'が0文字とマッチしたと仮定して検索を継続
        if(!(bmatch = strmatch(ptn+1, str))){
            // '*'が1文字以上にマッチしていると仮定して検索を継続
            if(*str != '\0'){
                bmatch = strmatch(ptn, str+1);
            }
        }
        break;
 
    // ? : 任意の1文字
    case '?':
        // 次の文字から検索を継続
        if(*str != '\0'){
            bmatch = strmatch(ptn+1, str+1);
        }
        break;
 
    // その他の文字
    default:
        // 今回の文字が一致するなら
        if(*ptn == *str){
            // 次の文字から検索を継続
            if(*str != '\0'){
                bmatch = strmatch(ptn+1, str+1);
            }
        }
        break;
    }
 
    return bmatch;
}

関数シグニチャのマングルについて Edit

C++では、関数のオーバーロードを実現するために、関数シグネチャ(定義)から関数名、引数の型、修飾子などの情報を混合したシンボルを作成し、関数を一意に識別している。
このシンボル名を作成することをマングルと呼ぶ。

マングルの方法は、コンパイラによって異なる。
VC++を使用する場合はマイクロソフトの規約にしたがってマングルされる。先頭が?、末尾がZの形式(?Func@CClass@@QAEXH@Z)

逆に、シンボルを関数名に戻すことをデマングルと呼ぶ。
VC++の場合、Win32APIの::UnDecorateSymbolName()を使用することでデマングルが可能。

参考URL:
http://kone.vis.ne.jp/diary/diaryb13.html#080315
http://www.kegel.com/mangle.html
http://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B_Name_Mangling

構造化例外のキャッチ Edit

例えば、メモリアクセス違反(アクセスバイオレーション)などが構造化例外。
C++のtry-catch構文でキャッチするためにはコンパイルオプション「/EHa」が必要。(VC6の場合は設定画面に項目がないため、自分で追記する必要がある)
ただし、発生する例外はCException型ではないことに注意。以下のように全ての例外をキャッチすれば捕まるが、詳細情報は取れない。

  1
  2
  3
  4
  5
    try{
        // アクセスバイオレーション発生
    } catch(...) {
        printf("例外発生\n");
    }

Visual Studio を使用している場合は、MS独自仕様の __try-__except構文で構造化例外をキャッチできる。
これはC言語のソースでも可能で、コンパイルオプションも不要。

  1
  2
  3
  4
  5
  6
  7
  8
  9
#include <excpt.h>
 
    :
 
    __try{
        // アクセスバイオレーション発生
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        printf("例外発生\n");
    }

C++ Edit

static関数/変数 Edit

CPPファイルからCファイルに定義された関数を呼び出す Edit

関数のシグネチャがCとCPPで異なるため、単純なextern宣言では呼び出せない。(リンクエラーになる)

  1
  2
  3
  4
  5
  6
  7
  8
    // グローバル関数 or グローバル変数
    extern "C" ~~
 
    // 複数のグローバル関数
    extern "C"{
        ~~
        ~~
    }

メンバ初期化子(:) Edit

クラスのメンバ変数を初期化する際、コンストラクタで初期値を代入することもできるが、これは厳密な意味での「初期化」ではなく、値の「代入」である。
厳密に初期化を行う場合は、メンバ初期化子「:」を用いる。コンストラクタ初期化子とも呼ばれる。

実行時型情報(RTTI) Edit

typeid演算子によって、オブジェクトが何のクラスのインスタンスなのかを実行時に知ることができる。
使用するには、プロジェクトの設定で「ランタイムタイプ情報(RTTI)を有効にする」にチェックを付けることでコンパイルオプション「/GR」を指定しなければならない。

メンバアクセス演算子でprivateメンバにアクセス Edit

自身のクラス型のオブジェクトのprivateメンバは、たとえインスタンスが異なっていたとしても「.」「->」でアクセス可能。
例)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
class CSample
{
private:
    int num;
 
    void Test0()
    {
        printf("Test\n");
    }
 
public:
    void Test1(CSample &obj)
    {
        printf("%d\n", obj.num);    // OK
    }
    void Test2(CSample *pObj)
    {
        pObj->Test0();            // OK
    }
};

コーディングの慣例 Edit

MFC派生クラスのヘッダファイル Edit

VC6で生成されるMFC派生クラスのヘッダファイルの例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
// MFC派生クラス
class CSample : public CBase    
{
    DECLARE_DYNAMIC(CSample)    // ①
 
// コンストラクション
public:
    // ②
 
// アトリビュート
public:
    // ③
 
// オペレーション
public:
    // ④
 
// オーバーライド
    // ClassWizard は仮想関数のオーバーライドを生成します。
    //{{AFX_VIRTUAL(CAbc)
    // ⑤
    //}}AFX_VIRTUAL
 
// インプリメンテーション
public:
    // ⑥
 
protected:
    // ⑦
 
    //{{AFX_MSG(CAbc)
    // ⑧
    //}}AFX_MSG
 
private:
    // ⑨
};

説明

概要説明追加
CObjectクラスの
機能の使用宣言
全てのMFCクラスの基底クラスであるCObjectの機能を使用する場合は、hファイルに DECLARE_DYNAMIC マクロを追加する必要がある。
また、その場合はcppファイルにも対応する IMPLEMENT_DYNAMIC マクロを追加する必要がある。
VC6では、自動的に追加される訳ではないので注意。
任意
コンストラクションコンストラクタやCWnd::Create()のような、そのクラスのオブジェクトを使用する前に必ずコールする必要のあるメンバ関数を定義する。任意
アトリビュートそのクラスの属性(プロパティ)を制御する公開メンバ変数や、Set/Getメンバ関数を定義する。
Getメンバ関数はconstにすべきである。
任意
オペレーションそのクラスのオブジェクトに対して、何らかの操作を行わせるための公開メンバ関数を定義する。任意
オーバーライドMFC基底クラスの仮想関数のオーバーライドを定義する。ウィザード
インプリメンテーション
(公開メンバ)
そのクラスの実装のための公開メンバ(通常、クラスの使用者が知る必要のないメンバ)を定義する。ウィザード
インプリメンテーション
(被保護メンバ)
そのクラスの実装のための被保護メンバ(通常、クラスの使用者が知る必要のないメンバ)を定義する。ウィザード
Window
メッセージハンドラ
Windowメッセージハンドラ(メンバ関数)を定義する。ウィザード
インプリメンテーション
(非公開メンバ)
そのクラスの実装のための非公開メンバ(通常、クラスの使用者が知る必要のないメンバ)を定義する。ウィザード

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS