コレ です。
// シリアルポート一覧取得
// CStringArrayに"COM1"などのポート名称を格納する
// 戻り値はポート数(エラーの場合は0)
#include "atlbase.h"
INT_PTR GetSerialCommList( CStringArray &strListArray )
{
    CRegKey Reg;
    LONG iRet = Reg.Open( HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", KEY_READ );
    if( iRet != ERROR_SUCCESS ){
        return 0;
    }
    strListArray.RemoveAll();
    DWORD dwIndex = 0;
    TCHAR szName[256], szValue[256];
    DWORD nNameLen, nValueLen, dwType;
    for(;;){
        nNameLen = nValueLen = 256;
        iRet = RegEnumValue( Reg.m_hKey, dwIndex, szName, &nNameLen, 0, &dwType, (BYTE *)szValue, &nValueLen );   // なぜか CRegKey のメンバにRegEnumValue()がない!
        if( iRet != ERROR_SUCCESS ){
            break;
        }
        if( dwType == REG_SZ ){
            strListArray.Add( szValue );
        }
        dwIndex++;
    }
    Reg.Close();
    return strListArray.GetCount();
}
 
	Warning: calling DestroyWindow in CDialog::~CDialog --
	OnDestroy or PostNcDestroy in derived class will not be called.
 
これは,派生ダイアログクラスで OnDestroy あるいは PostNcDestroy が呼び出されなかったというワーニングです。
    CFindReplaceDialog  *m_pFindDialog;
    CString             m_strFindString;
 
2. メインダイアログの初期化で上記ポインタと文字列を初期化
    this->m_pFindDialog   = NULL;
    this->m_strFindString = "";
 
3. メインダイアログのメニューハンドラなどで検索ダイアログを作成
    if( this->m_pFindDialog ){
        return;
    }
    this->m_pFindDialog = new CFindReplaceDialog;
    if( this->m_pFindDialog ){
        BOOL bRet = this->m_pFindDialog->Create( TRUE, this->m_strFindString, NULL, (FR_DOWN|FR_HIDEMATCHCASE|FR_HIDEWHOLEWORD) );
        if( !bRet ){
            this->m_pFindDialog = NULL;
        }
    }
 
4. メインダイアログに検索ダイアログからのメッセージを受け取るための関数を追加(メンバ関数としてヘッダにも追加)
// 検索ダイアログからのメッセージを受信
LRESULT CMLOGVWDlg::OnFindCmd( WPARAM wParam, LPARAM lParam )
{
    CFindReplaceDialog *pDlg = CFindReplaceDialog::GetNotifier( lParam );
    if( pDlg == this->m_pFindDialog ){
        // 終了
        if( pDlg->IsTerminating() ){
            pDlg->DestroyWindow();
            this->m_pFindDialog = NULL;
        }
        // 次を検索
        else if( pDlg->FindNext() ){
            // 検索文字列の取得
            this->m_strFindString = pDlg->GetFindString();
            // 検索方向の取得
            BOOL bSearchDown = pDlg->SearchDown();
            // TODO: ここに独自の検索処理を入れる
        }
    }
    return 0;
}
 
5. 検索ダイアログからのメッセージを受け取るためメインダイアログにマクロを追加
const UINT wm_Find = RegisterWindowMessage( FINDMSGSTRING );    // ← ★追加
BEGIN_MESSAGE_MAP(CxxxxDlg, CDialog)
    …
    ON_REGISTERED_MESSAGE(wm_Find, OnFindCmd)                   // ← ★追加
END_MESSAGE_MAP()
 
※備考  
 
MFCのSDIやMDIアプリケーションではビューに描いたものを簡単に印刷できます。
方法は,ドキュメントとビュー の後半を参照してください。
 
定期的ではない処理を実行するときに,処理状況をダイアログ上のエディットコントロールに
表示しながら進めたい場合があると思います。
// 処理状況を表示する文字列とエディットコントロール(メンバ変数)
CString m_strLog;
CEdit   m_EditLog;
OnInitDialog( )
    // 開始メッセージ表示
    m_strLog = "処理開始...\r\n"
    this->m_EditLog.SetWindowText( m_strLog );
    // 500msec後に処理開始
    this->SetTimer( 1, 500, 0 );
OnTimer( )
    // 処理は一度だけ
    this->KillTimer( 1 );
    // 処理をここに入れる
    // 処理状況を表示
    m_strLog += "処理中です...\r\n";
    this->m_EditLog.SetWindowText( m_strLog );
    // 終わったらエディットコントロールをスクロールさせて最終行を見せる
    this->m_EditLog.LineScroll( this->m_EditLog.GetLineCount() );
 
 
◆ クラスウィザードが応答なしになってしまう(VC7)
 
Visual C++ .NET2003のVisual Studioでクラスを追加するときクラスウィザードが応答なしになってしまうことがありました。
 
デバッグの際,出力ウインドウに "MRU: open file (1)" とかいうメッセージがでることがあります。
何かのエラーかとびっくりしましたが,最近使ったファイルをオープンするとこのメッセージが出ることが分かりました。
Most Recently Used の略で,システムの機能が働いたことを通知するみたいです。もちろんエラーではありません。
 
リソースエディタでダイアログを新規作成し,それを子ダイアログとして親ウインドウからCreateしたとき,
ダイアログは表示されるけどその上に載っているコントロールを操作できないことがありました。
実はダイアログのプロパティで Disabled が True になっていただけでしたが,
気がつくまでにかなり時間がかかりました(子ダイアログなのでタイトルバーが表示されておらずグレイアウトされているのが分からないため)。
ダイアログリソースを追加する際に,プロパティページ型のダイアログを挿入するとデフォルトで Disable が True になっているので気をつけてください。
 
Microsoft Visual Studio .NET 2003 のリソースエディタで新規にダイアログを作成して,
さぁ OnInitDialog() をオーバーライドしようと思っても,どこをどうしたらいいんだか
さっぱり分かりません。しかたがないので基本クラスのソースをコピペして手動で
実装していましたが,やっぱりそういう機能がありました。
 
例えばマウスドラッグで領域を指定する場合などで,CRectの座標が,左(left)>右(right) または 上(top)>下(bottom) に
なってしまう場合も多々あると思います。そのまま幅や高さを計算しようとするとマイナス値になってしまいますが,
それぞれのメンバの大小を比較して入れ換える処理をベタで実装するのもスマートではありません。
 
◆ データ管理の味方 CArray と CList
 
SDIやMDIアプリケーションを作成するとき,ドキュメントファイルのフォーマットをどうしようか悩むと思います。
固定長で固定配置のデータなら別にどうってことないのですが,可変長のデータだとどうやってデータを管理しましょうか?
typedef struct hoge_st {
    int a, b, c, d;
} HOGE_ST;
typedef union fuga_un {
    int   a;
    short b;
    char  c;
} FUGA_UN;
 
この型のデータを動的に管理するには,malloc() や new でメモリを確保して,そのアドレスをリンクリストで
管理するのが普通です。削除するときもメモリリークしないように気をつけなければいけませんし,
シリアライズするときも一度にはできません。とーっても面倒くさいです。
#include 
typedef struct hoge_st {
    int a, b, c, d;
} HOGE_ST;
typedef union fuga_un {
    int   a;
    short b;
    char  c;
} FUGA_UN;
    // オリジナル構造体の配列を確保
    CArray HogeArray; // デフォルトでは配列の要素数は1ずつ拡張される
    HogeArray.SetSize( 5, 10 );         // 要素数と拡張単位数を設定することもできる
    // 要素にアクセス
    HOGE_ST *p;
    p = &HogeArray[0];
    p->a = 0xAAAAAAAA;
    // 途中で要素数の拡大可能
    HOGE_ST hoge = {1,2,3,4};
    HogeArray.Add( hoge );
    INT_PTR n = HogeArray.GetSize();
    p = &HogeArray[n-1];
    // 全要素を削除
    HogeArray.RemoveAll();
    // オリジナル共用体のリストを確保
    CList FugaList(16);   // リスト要素の拡張単位数を最初に設定(デフォルト10)
    // 要素を登録
    FUGA_UN fuga;
    fuga.a = 0xAAAAAAAA;
    fuga.b = (short)0xBBBB;
    fuga.c = (char)0xCC;
    FugaList.AddTail( fuga );
    INT_PTR m = FugaList.GetCount();
    // リストの検索
    POSITION pos;
    FUGA_UN f;
    pos = FugaList.GetHeadPosition();
    for( int i=0; i
なお,ダイアログアプリケーションでは #include <afxtempl.h> しないと使えないようです。
 
ウインドウのサイズではなくて,クライアント領域のサイズを設定したいときは
以下のようにしています。ダサいですか?(笑)
    // クライアント領域サイズ設定
    CRect WindowRect, ClientRect;
    this->GetWindowRect( WindowRect );
    this->GetClientRect( ClientRect );
    int dw = WindowRect.Width() - ClientRect.Width();
    int dh = WindowRect.Height() - ClientRect.Height();
    this->SetWindowPos( 0, 0, 0, CLIENT_WIDTH+dw, CLIENT_HEIGHT+dh, SWP_NOMOVE );
    this->CenterWindow();
 
 
◆ 動的に実装したコントロールのイベント処理方法(VC7)
 
動的に実装したコントロールのイベントを処理するには以下のようにする必要があります。
最初に,動的に実装するコントロール用に新しいクラスを作成します。 クラスビューの該当クラスで 右クリック → プロパティ で,イベントやメッセージに
    対する処理を実装します。
 動的にコントロールを実装するときに,新しく作成したクラスのオブジェクトを実装します。
  
 
IDE(統合開発環境)のようなドッキングバー(ドッキングウインドウ)を簡単に実装するには
以下のサイトのソースコードを利用させてもらうのがいいでしょう。
・sizecbar-v2.44 のソースとヘッダをプロジェクトに登録します。afxChNil が定義されていないという
  エラーがコンパイル時に発生するので,sizecbar.cpp に以下の行を追加します。
/////////////////////////////////////////////////////////////////////////////
// "afxChNil" undeclared identifier in VC++.NET 2003
static TCHAR afxChNil = '\0';
/////////////////////////////////////////////////////////////////////////////
・stdafx.hに以下の行を挿入します。
// ドッキングウインドウの追加
#include "..\sizecbar-v2.44\src\sizecbar.h"
#include "..\sizecbar-v2.44\src\scbarg.h"
#include "..\sizecbar-v2.44\src\scbarcf.h"
・MainFrm.hでメンバ変数を追加します。
    CSizingControlBarCF m_wndXXXXBar;
・MainFrm.cppで以下のように実装します。
    // ドッキングウインドウの追加
    if( !m_wndXXXXBar.Create( _T("Docking Bar"), this, IDR_CONTROLBAR ) ){
        return -1;      // 作成できませんでした。
    }
    m_wndXXXXBar.SetSCBStyle( m_wndXXXXBar.GetSCBStyle()|SCBS_SIZECHILD );
    m_wndXXXXBar.SetBarStyle( m_wndXXXXBar.GetBarStyle()|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC );
    m_wndXXXXBar.EnableDocking( CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT );
    // ドッキングを可能にする
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndXXXXBar, AFX_IDW_DOCKBAR_LEFT);
 
あとは,ドッキングウインドウを親として各種コントロールを実装すればよいです。
 
印刷プレビューウインドウのボタンが英語になるのは MFC の DLL に原因があります。
 
VC++.NET 2003 のリソースビューで Menu を新規追加したのはいいけど,
メニューのリソースIDを IDR_MENU1 からどうしても変更できない!
 
VC++.NET 2003でもリソースビューでダイアログの挿入をして,ダイアログエディタでボタンなどの
コントロールを載せていきますが,そのままでは,そのコントロールに変数を追加できません。
前もって新規ダイアログのクラスを作成しておかなければなりません。
 
SDI や MDI アプリケーションで,ステータスバーにメッセージを表示する方法。
static UINT indicators[] =
{
	ID_SEPARATOR,           // status line indicator
	ID_SEPARATOR,           // status line indicator
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
};
 
ステータスバーにメッセージを表示するには,
CMainFrame *pMainFrm = (CMainFrame *)AfxGetMainWnd();
CStatusBar *pStatusBar = (CStatusBar*)pMainFrm->GetMessageBar();
pStatusBar->SetPaneText( n, "message" );
 
ここで n は領域番号を示します。
 
クライアント領域に何かを描画して,それをスクロールさせたくなったら
ダイアログベースアプリケーションから卒業する時期です(笑)。
 
VC++4.0ではプロジェクトが存在するフォルダのパス名に半角スペースが入っていると
ブレークポイントを張っても止まらないようです。え?今時4.0なんて使っている
人はいない? う〜,今年こそパソコン買い換えて環境を最新にするぞ!!
 
リソースエディタで登録,編集できるデータはアイコンやメニューだけではありません。
WAVデータなど,その他全てのデータファイルを実行ファイル内に格納し,
リソースとして使用することができます。
#include 
#include "resource.h"
#pragma comment( lib, "winmm.lib")
void main( void )
{
    /* リソースからWAVデータを読み込む */
    HRSRC hRes = FindResource( NULL, (const char *)IDR_WAVE1, "WAVE" );
    HGLOBAL hSound = LoadResource( NULL, hRes );
    /* 読み込んだデータのアドレスを取得 */
    LPVOID pSound = LockResource( hSound );
    /* WAVデータを再生 */
    sndPlaySound( (const char *)pSound, SND_SYNC|SND_MEMORY );
    // ロードしたリソースの解放
    FreeResource( hRes );
}
  
リソースの解放は明示的に FreeResource( ) しなくてもプログラムの終了時にシステムが
自動で解放するようです。ヘルプにはWindows95では必ず FreeResource( ) しろと書いて
あるのに,デバッグでステップ実行してみると FreeResource( ) 自体は何もしていなかっ
たりするのですが,FreeResource( ) したからといって即座にメモリが解放されるかどうか
は95系かNT系かで実装が異なるみたいです。
【2007.02.02追記】
 
リソースビューを右クリックして「リソースの追加...」を選択する。 
「カスタム...」を押して,リソースの種類は「WAVE」を入力する。 
リソースビューに追加されたIDR_WAVE1などのシンボルをダブルクリックする。 
バイナリエディタで適当な値を追加する。 
ファイルメニューの「全てを保存」した後,該当リソースのプロパティでファイル名を登録したいものに変更する。 
ファイルを読み込みますか?ときかれるので「はい」を選択する。 
 
あと,リソースからサウンド再生する方法は次のようにすると簡単でした。VC++.NET 2003で確認しました。
#include "Mmsystem.h"
#pragma comment(lib,"winmm")
PlaySound( "IDR_WAVE1", AfxGetInstanceHandle(), SND_RESOURCE|SND_ASYNC );
 
sndPlaySound() は PlaySound() のサブセットで互換性のために残されている関数のようです。
 
ダイアログベースのアプリケーションにコマンドプロンプトからパラメータを指定して
実行できるようにしたとき,コンソールにメッセージを表示したいことがあります。
// 出力コンソールを開く
AllocConsole();
HANDLE hFile = CreateFile( "CONOUT$", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
// コンソール出力
char szMessage[] = "コンソール出力サンプル\n";
DWORD dwConsole;
WriteFile( hFile, szMessage, strlen( szMessage ), &dwConsole, NULL );
		
// コンソールを閉じる確認
char szReturn[] = "\n終了するにはRETURNキーを押してください。\n";
WriteFile( hFile, szReturn, strlen( szReturn ), &dwConsole, NULL );
CloseHandle( hFile );
		
// 入力コンソールを開く(RETURNキーを待つ)
hFile = CreateFile( "CONIN$", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
ReadFile( hFile, &dwConsole, 1, &dwConsole, NULL );
CloseHandle( hFile );
// コンソールを閉じる
FreeConsole();
 
 
メニュー項目などに「開く(O ) CTRL+O」などとあると,コントロールキーを押しながら
Oキーを押すとこの項目を実行できます。このCTRL+Oがアクセラレータキーと呼ばれるものです。
アクセラレータキーを実装すると使いやすいアプリケーションになるため,よく使う機能には
是非キーを割り当てたいものです。
BOOL C????Dlg::PreTranslateMessage(MSG* pMsg) 
{
    if( pMsg->message == WM_KEYDOWN ){
        // CTRL+O アクセラレータキーの代用
        if( pMsg->wParam =='O' && (GetKeyState( VK_CONTROL )&0x8000) ) On????Open( );
    }
    return CDialog::PreTranslateMessage(pMsg);
}
 
なおOn????Open( )はメニュー項目のハンドラです。
 
ネットワークプログラミングは難しそうに見えますが,クライアント(要求者)とサーバ(提供者)とで
データをやりとりしているに過ぎません。そのやりとりがプロトコルというお約束で決められているだけです。
クライアントが「海」と言ったらサーバが「山」といって色々なデータがやりとりされているようなものです。
// ※プロジェクトを作成する際にWindowsソケットを有効にしておく
// ソケット オブジェクトの構築
CSocket sockClient;
// SOCKET の生成
sockClient.Create( );
// コネクションの検索
sockClient.Connect( strAddr, nPort ); // アドレスとポート番号
// ファイル オブジェクトの構築
CSocketFile file( &sockClient );
// アーカイブの構築
CArchive arIn( &file, CArchive::load );
CArchive arOut( &file, CArchive::store );
// 受信データがあるか調査
arIn.IsBufferEmpty( );
arIn >> dwValue;  // 受信
arOut << dwValue; // 送信
 
CSocket の派生クラスを作成し,データを受信した時のイベントハンドラをオーバーライドすることに
よって受信データ待ちもスマートにプログラミング可能です。
// ※プロジェクトを作成する際にWindowsソケットを有効にしておく
#include "stdafx.h"
// CSocket派生クラス
class CSocketEx : public CSocket {
    CSocketEx( ){}  // コンストラクタ
    ~CSocketEx( ){} // デストラクタ
    // データ受信ハンドラ
    void OnReceive( int nErrorCode )
    {
        // Receive( )関数で受信
        CSocket::OnReceive(nErrorCode);
    }
};
// VC++4.0ではクラスウィザードでCSocketを基本クラスにした派生クラスを
// 新規作成できなかったので手動で派生しています。VC++6.0ではできた…。
 
イベントハンドラには OnReceive( ), OnSend( ), OnAccept( ), OnConnect( ), OnClose( ) というものが存在します。
詳細はそれぞれヘルプを見てください。「Windows ソケット」をキーワードにヘルプを参照するとソケット
プログラミングが良く分かります。
 
MFCには日付と時刻を管理する便利なCTimeクラスがあります。
以下のように簡単に時刻と時間の処理を実装することが可能です。
// 現在時刻で作成
CTime now = CTime::GetCurrentTime( );
// 指定した時刻で作成
CTime t = CTime( 2003, 10, 19, 11, 42, 0, -1 );
// 大小比較
if( now < t ){ ... }
// 差分の取得
CTimeSpan span = t - now;
// 時間の計算
now += span;
// UTC時刻からローカル時刻への変換
#include 
CTime utc = CTime( 2003, 10, 19, 2, 42, 0, -1 );
CTime local = utc;
local -= CTimeSpan( (time_t)_timezone );
  
詳細は CTime や CTimeSpan や「日付と時刻」や「時間管理」をキーワードにヘルプを参照してください。
 
これまでは主にダイアログベースのアプリを作成してきました。
テキストエディタやペイントツールの様に、データを参照するだけでなく
編集や保存を行いたい場合は SDI または MDI アプリケーションを作成する
必要があります。
// アトリビュート
public:
    char m_Data[10];    // ドキュメントデータ
 
新規ドキュメントを開いた時、このドキュメントデータの初期値を設定するように
 C????Doc.cpp 内の OnNewDocument( ) を変更します。
BOOL C????Doc::OnNewDocument( )
{
    if (!CDocument::OnNewDocument( )) return FALSE;
    for( int i=0; i<10; i++ ) m_Data[i] = 0;    // 新規ドキュメントデータ
    return TRUE;
}
 
またドキュメントを開いたり保存したりする場合に呼ばれる関数が Serialize( ) です。
現在のデータを一旦保存して、次に読み込んだとき作業を継続できるという意味で「シリアライズ」
という名称になっています。 C????Doc.cpp 内にあるこの関数にコードを追加します。
void C????Doc::Serialize(CArchive& ar)
{
    if (ar.IsStoring( ))    // 保存する時
    {
        for( int i=0; i<10; i++ ) ar << m_Data[i];
    }
    else                   // 読み込む時
    {
        for( int i=0; i<10; i++ ) ar >> m_Data[i];
    }
}
 
ar は CArchve クラスのオブジェクトですが、自動的に生成されていて、
ファイルの入出力を適切に行ってくれるものです。ファイル名の決定やオープン、
クローズは自動的に行われるので、上記のようなたった数行でドキュメントデータと
ファイルのやりとりが簡単に実装できます。
void CxxxxDoc::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        CString String;
        String.Format( "VERSION: 2.2\r\n" );
        ar.WriteString( String );
        String.Format( "PROJECT: %s\r\n", "hoge" );
        ar.WriteString( String );
    }
    else
    {
        CString VersionString, ProjectString;
        ar.ReadString( VersionString );
        ar.ReadString( ProjectString );
    }
}
 
ar.ReadString() でどんどん読み込んでいってテキスト終端に到達したとき,
戻り値は FALSE なり NULL を返してくれるのですが,
それと同時にMFCは必ず以下のような例外も発生してくれるようです…。
CArchive exception: endOfFile.
XXXX.exe の 0x???????? で初回の例外が発生しました : Microsoft C++ exception: CArchiveException @ 0x0012edbc。
 
例外は終端以降を読んだときにしてほしいなぁ。
初回の例外 
は実害がないようなので無視していいのかな?
void C????View::OnDraw(CDC* pDC)
{
    C????Doc* pDoc = GetDocument( );
    ASSERT_VALID(pDoc);
    for( int i=0; i<10; i++ ){
        CString Str;
        Str.Format( "m_Data[%d] = %d", i, pDoc->m_Data[i] );
        pDC->TextOut( 0, i*20, Str );
    }
}
 
ドキュメントへは GetDocument( ) 関数を使ってアクセスします。
void C????View::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
    C????Doc* pDoc = GetDocument( );
    // ドキュメントデータの変更
    for( int i=9; i>0; i-- ) pDoc->m_Data[i] = pDoc->m_Data[i-1];
    pDoc->m_Data[0] = pDoc->m_Data[1] + 1;
    // ドキュメントが更新されたことを通知
    pDoc->SetModifiedFlag( );
    // 更新されたドキュメントを表示するビューを再描画させる
    pDoc->UpdateAllViews( NULL );
    CView::OnLButtonDblClk(nFlags, point);
}
 
これでドキュメントとビューの一連のコーディングが終了しました。
何たってファイルを直接操作しなくて良いのですから簡単すぎますね!(笑)
【ドキュメント・ビュー追加TIPS】
 
ドキュメントにはデータ内容と,データの操作関数の両方を用意すると使い勝手が良くなります。
 ドキュメントの内容を作成するのはOnNewDocument( )で,内容を空にするのはDeleteContents( )をオーバーライドして実装します。
 ドキュメントは SetModifiedFlag( ) で変更フラグを更新し,IsModified( ) で変更があるかどうかを確認できます。
 ビュー側でデータの操作をする場合は GetDocument( )->操作関数( ) を実行します。
 ドキュメント側から全てのビューを再表示したいときは UpdateAllViews( NULL ) を実行します。
    このときビュー側では OnDraw() ではなく OnUpdate() が実行されるので注意してください。
 ドキュメント側からビューアドレスを取得したい場合は GetFirstViewPosition( ) と GetNextView( ) を使用します。
 メインフレームからビューとドキュメントにアクセスするには次のようにします。
    CView *pView = this->GetActiveView( );
    CDocument *pDoc = this->GetActiveDocument( );
    ※MDIの場合は最初に this->GetActiveFrame( ) で子フレームを取得してから。
 
 ドキュメントまたはビューからメインフレームにアクセスするには次のようにします。
    CFrameWnd *pMainFrm = (CFrameWnd *)AfxGetMainWnd( );
 
 ドキュメント名を変更するには SetTitle( ) を使います。
 ドキュメントが変更された場合,メインフレームのタイトルバーに変更フラグ(*)を以下のようにするとつけることができます。自動では面倒見てくれないようです。
// ドキュメント変更フラグ設定のオーバーライド
void CxxxxDoc::SetModifiedFlag(BOOL bModified)
{
    // デフォルト
    CDocument::SetModifiedFlag( bModified );
    // 変更状況に応じてメインフレームのタイトルの末尾に * を付ける
    CMainFrame *pMainFrm = (CMainFrame *)AfxGetMainWnd();
    if( pMainFrm == NULL ){
        return;
    }
    CString String;
    pMainFrm->GetWindowText( String );
    // 変更がある場合
    if( bModified == TRUE ){
        // 末尾が * でなければ * を追加
        if( String.Right(1) != "*" ){
            String += "*";
            pMainFrm->SetWindowText( String );
        }
    }
    // 変更がない場合
    else {
        // 末尾が * であれば削除
        if( String.Right(1) == "*" ){
            String = String.Left( String.GetLength()-1 );
            pMainFrm->SetWindowText( String );
        }
    }
}
 
 スクロールビューにスクロールバーが付かないときはビュークラスのソースで以下の項目を確認してください。
  
  OnInitialUpdate( ) で CScrollView::OnInitialUpdate( ) よりも前に SetScrollSizes( ) しているか?
   OnUpdate( ) をオーバーライドして SetScrollSizes( ) と ResizeParentToFit( ) を実行しているか?
    
 スクロールビューのマウスボタン操作やカーソル移動のハンドラに渡される引数 point は
スクロールされた分が入っていない(ビューウインドウの見かけ上の座標)ので,
    point += this->GetScrollPosition();
 
として加算しないと,描画座標と一致しません。
  
    // 印刷時のスケール設定
    if( pDC->IsPrinting() == TRUE ){
        pDC->SetMapMode( MM_ANISOTROPIC );
        pDC->SetWindowExt( 100, 100 );
        pDC->SetViewportExt( 400, 400 );
    }
 
【2007.05.08追記】
 
    if( pDC->IsPrinting() == TRUE ){
        pDC->FillSolidRect( x, y, 1, 1, nColor );
    }
    else {
        pDC->SetPixel( x, y, nColor );
    }
 
 
ウインドウの終了時に取得したメモリなどのリソースを解放しなくてはなりませんが、
具体的にどこで行えば良いのでしょうか?
OnClose( ) DestroyWindow( ) OnDestroy デストラクタ  
 
バイナリファイル操作には CFile クラスを使うのが便利ですが、ファイルの作成日時やサイズを
知りたいだけの場合もこのクラスの静的メンバ関数を使用するとファイルをオープンしないで
可能になります。
CFileStatus rStatus;
if( CFile::GetStatus( "filename", rStatus ) ){
    CTime Date = rStatus.m_mtime; // ファイル最終変更日時
    int nSize = rStatus.m_size;   // ファイルの論理サイズ
}
else // ファイルが存在しない
 
CFileStatus にはファイル属性などの情報もあるので詳しくはヘルプを見てください。
 
ダイアログを開いたとき、デフォルトではOKボタンにフォーカスがセットされています。
これを変更するにはリソースエディタのレイアウトでタブオーダーの設定をします。
一番若いスタティックでないコントロールにセットされるようです。
タブオーダーの設定の仕方は、ダブルクリックして1番をセットし、あとは順番にシングル
クリックしてセットしていきます。
NextDlgCtrl( ); // 次のコントロールへ
PrevDlgCtrl( ); // 前のコントロールへ
GotoDlgCtrl( GetDlgItem( IDC_???? ) ); // IDC_???? のコントロールにフォーカスをセット
GetDlgItem( IDC_???? )->SetFocus( );    // IDC_???? のコントロールにフォーカスをセットする別の方法
 
またダイアログ上であたかもTABキーが押されたかのようにすることでも次のコントロールへ移動できます。
// 処理前メッセージハンドラ
BOOL CVCalEdDlg::PreTranslateMessage(MSG* pMsg) 
{
    if( pMsg->message == WM_KEYDOWN ){
        // RETURNキーは次のコントロールを選択する(TABキーに変換)
        if( pMsg->wParam == VK_RETURN ) pMsg->wParam = VK_TAB;
    }
    return CDialog::PreTranslateMessage(pMsg);
}
 
 
メディアプレーヤを自作プログラムに組み込むことができるようです。
以前はOLEコントロールと呼んでいて今はActiveXコントロールとかいう技術を利用するのですが、
現在の筆者のレベルではよくわかりません。使ってるのがVC++4.0だし…。(^^; 
要するにメディアプレーヤがクラスとして定義され、ダイアログなどにオブジェクトとして実装できるということらしいです。
最初にプロジェクトを作成する時にOLEサポートの選択でOLEコントロールを有効にします。
 挿入->コンポーネント->OLEコントロールでWindows Media Playerを追加します。
 ダイアログなどにメンバ変数 CMediaPlayer2 m_Player; を追加します。#include "MediaPlayer2.h" するのを忘れずに。
 ダイアログのOnInitDialogで、
// スタティックコントロール内にメディアプレーヤを組み込む
CRect Rect;
this->GetDlgItem( IDC_STATIC_AREA )->GetWindowRect( Rect );
this->ScreenToClient( Rect );
Rect.DeflateRect( 2, 2, 3, 3 );
RECT rect = Rect;
m_Player.Create( "Media Player", WS_CHILD|WS_VISIBLE, rect, this, 1 );
 
  ロードするファイルの指定は m_Player.SetFileName( "c:\\windows\\デスクトップ\\movie.mpg" ); で行う。
  再生開始は m_Player.Play( ); で 停止は m_Player.Stop( ); らしい。
  
他にもオートスタートを制御する m_Player.SetAutoStart( FALSE ); とか、オートサイズ?を設定する
 m_Player.SetAutoSize( TRUE ); とか パネルを出すか出さないか設定する m_Player.SetShowControls( FALSE ); とか
色んなメンバ関数があるみたいですが、何せヘルプが無いのでよくわかりません。インターネットで調べてもあまり
使っている人はいないみたい…。暇ができたらもっと詳しく調べようと思います。[つづく]
 
デバグ実行終了時に以下のメッセージがでることがありました。
原因: NULLポインタ(0番地)やシステム予約領域または自分で確保した領域以外にアクセスした。
    // アクセス違反が発生する例
    CString Str;
    this->GetWindowText( Str );
    Str.Format( "%s test", Str );    // << ここでアクセス違反!
    // 改良版
    CString Str;
    this->GetWindowText( Str );
    Str += " test";
 
 
・例外処理 (初回) は xxxx.exe (KERNEL32.DLL) にあります: 0xC0000005: アクセス違反。
ここ にもあるように「(初回)」のエラーは致命的では
なさそうなのでほおっておいても良さそうですが、(^^; 気持ち悪いので調べてみました。
 
・例外処理 (初回) は xxxx.exe (MSONSEXT.DLL) にあります: 0x006D007E: (unknown)。
原因:CFileDialogを開いたときに存在しないファイル名を指定したときに発生する。
 
・CArchive exception: endOfFile.
原因:CArchive ar.ReadString() でテキスト終端に到達したときに発生する。
 
他の場合もあると思いますが、とりあえず今回は以上。
 
タブコントロールはプロパティシートについているようなタブを制御するクラスです。
ダイアログをタブによって切り換えたい場合はプロパティシートそのものである
CPropertySheetクラスを使えばCTabCtrlクラスを使用しなくても簡単に実装できますが
(参照 ),タブによってツリーコントロールのような任意のウインドウを
切り換えたい場合はCTabCtrlを実装する必要があります。
メインダイアログにタブによって切り換えられる子ウインドウ(複数)をメンバ登録する。
 リソースエディタでメインダイアログにタブコントロールを貼り付ける。
 クラスウィザードでタブコントロールにコントロール変数(例m_Tab)を割り付ける。
 メインダイアログのOnInitDialogでm_Tab.InsertItem( )によってタブを追加する。
 同じくOnInitDialogでm_Tabを親として全ての子ウインドウをCreateする。
 クラスウィザードでタブのメッセージTCN_SELCHANGEにハンドラOnSelchangeTabを割り付ける。
 タブが切り換わるとOnSelchangeTabハンドラが実行されるのでm_Tab.GetCurSel( )で選択されている
タブを取得し,子ウインドウのどれか一つをShowWindow( SW_SHOW )し,他をShowWindow( SW_HIDE )する。
  
2つのタブを持ちツリーコントロールとリストコントロールを切り換えるようにした
サンプルコードはこちら 。
【2007.08.08追記】
 
    this->m_ListCtrl.SetParent( (CWnd *)&(this->m_TabCtrl) );
 
とすればできます。動的なリストコントロールを Create( ) しなくてよいので楽チンです。
    if( this->m_ListCtrl.m_hWnd ){
        CRect TabRect;
        this->m_TabCtrl.GetItemRect( 0, TabRect );    // 1個のタブサイズを取得
        int nRow = this->m_TabCtrl.GetRowCount();     // タブの行数を取得
        int nMargin = TabRect.left;
        CRect ListRect;
        ListRect.left   = nMargin;
        ListRect.top    = TabRect.Height() * nRow + nMargin * 2;
        ListRect.bottom = TabCtrlRect.bottom - nMargin;
        ListRect.right  = TabCtrlRect.right - nMargin;
        this->m_ListCtrl.MoveWindow( ListRect );
    }
 
上記コードを関数化して,親ダイアログの OnInitDialog( ) と OnSize( ) で呼べばOK!
 
ツリーコントロールとはエクスプローラの左側部分そのものです。階層的にアイテムを管理することができます。
使い方はメニューコントロールとリストビューコントロールのあいのこのようで、アイテムの追加には
InsertItem( )を使用し、アイテムが選択された時のハンドラは TVN_SELCHANGED を実装します。
これ 
を見てください。
エクスプローラの左側のようにサブフォルダツリーを作成する関数を作ってみました。
これ 
です。ついでなので CTreeCtrl クラスの派生クラスとして作成してみました。
使用方法はソースを見てください。
m_TreeCtrl.ModifyStyle( NULL, TVS_SHOWSELALWAYS );
 
【ツリーコントロールの追加TIPS】
 
OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if( nChar == VK_DELETE ){
        HTREEITEM hItem = this->GetSelectedItem();
        if( hItem ){
            this->DeleteItem( hItem );
        }
    }
    CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}
 
★項目ラベルの編集
CImageList  m_ImageList;
 
次に,イメージリストを初期化します。ツリーコントロールに OnCreate() を実装し,以下のコードを追加します。
// アイコンイメージリストの初期化
m_ImageList.Create( 16, 16, ILC_COLOR8, 2, 0 );
m_ImageList.Add( AfxGetApp()->LoadIcon( アイコン0のリソースID ) );
m_ImageList.Add( AfxGetApp()->LoadIcon( アイコン1のリソースID ) );
// ツリーコントロールにアイコンイメージを登録
this->SetImageList( &m_ImageList, TVSIL_NORMAL );
 
そして,InsertItem( "ITEM", 0, 1 ); のように非選択状態と選択状態でのそれぞれのイメージ番号を
イメージリストのインデックスで指定すればアイコン付きのツリーコントロールが実装できます。
// 右クリックハンドラ
void CxxxxTree::OnRButtonDown(UINT nFlags, CPoint point)
{
    // 該当座標に項目があれば選択してポップアップメニューを表示する
    HTREEITEM hItem = this->HitTest( point );
    if( hItem ){
        this->SelectItem( hItem );
        CMenu XxxxxMenu;
        XxxxxMenu.LoadMenu( IDR_MENU_PRJ );
        CMenu *pPopup = XxxxxMenu.GetSubMenu( 0 );
        this->ClientToScreen( &point );
        pPopup->TrackPopupMenu( TPM_LEFTALIGN|TPM_RIGHTBUTTON, point.x, point.y, this );
    }
    CTreeCtrl::OnRButtonDown(nFlags, point);
}
 
【2006.11.18追記】
 
メンバ
    BOOL        m_bDragging;
    HTREEITEM   m_hItemDrag;
    HTREEITEM   m_hItemDrop;
    CImageList  *m_pDragImage;
    BOOL IsChildNodeOf( HTREEITEM hItemChild, HTREEITEM hItemSuspectedParent );
OnCreate()
    // ドラッグ&ドロップ情報の初期化
    this->m_bDragging   = FALSE;
    this->m_hItemDrag   = NULL;
    this->m_hItemDrop   = NULL;
    this->m_pDragImage  = NULL;
OnDestroy()
    CImageList *pImageList;
    pImageList = GetImageList(TVSIL_NORMAL);
    pImageList->DeleteImageList();
    delete pImageList;
OnTvnBegindrag()
    LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR);
    // ドラッグ開始項目の取得
    HTREEITEM hDragItem = this->HitTest( pNMTreeView->ptDrag );
    if( hDragItem ){
        this->m_bDragging = TRUE;
        this->m_hItemDrag = hDragItem;
        this->m_hItemDrop = NULL;
        CPoint ptAction;
        GetCursorPos( &ptAction );
        ScreenToClient( &ptAction );
        this->m_pDragImage = this->CreateDragImage( hDragItem );
        this->m_pDragImage->DragShowNolock( TRUE );
        this->m_pDragImage->SetDragCursorImage( 0, CPoint(0, 0) );
        this->m_pDragImage->BeginDrag( 0, CPoint(0,0) );
        this->m_pDragImage->DragMove( ptAction );
        this->m_pDragImage->DragEnter( this, ptAction );
        this->SetCapture();
    }
OnMouseMove()
    if( this->m_bDragging ){
        if( this->m_pDragImage ){
            this->m_pDragImage->DragMove( point );
            HTREEITEM hItem;
            hItem = this->HitTest( point );
            if( hItem ){
                this->m_pDragImage->DragLeave( this );
                // ドロップ候補を保存
                this->SelectDropTarget( hItem );
                this->m_hItemDrop = hItem;
                this->m_pDragImage->DragEnter( this, point );
            }
        }
    }
OnLButtonUp()
    if( this->m_bDragging ){
        if( this->m_pDragImage ){
            this->m_pDragImage->DragLeave( this );
            this->m_pDragImage->EndDrag();
            delete this->m_pDragImage;
            this->m_pDragImage = NULL;
            if( this->m_hItemDrag != this->m_hItemDrop
                && !this->IsChildNodeOf( this->m_hItemDrop, this->m_hItemDrag )
                && this->GetParentItem( this->m_hItemDrag ) != this->m_hItemDrop ){
                // this->m_hItemDrag がドラッグ元アイテム
                // this->m_hItemDrop がドロップ先アイテム
            }
        }
        ::ReleaseCapture();
        this->m_bDragging = FALSE;
        this->m_hItemDrag   = NULL;
        this->m_hItemDrop   = NULL;
        this->SelectDropTarget( NULL );
    }
// 子項目か調査する
BOOL CxxxxTree::IsChildNodeOf( HTREEITEM hItemChild, HTREEITEM hItemSuspectedParent )
{
    do {
        if( hItemChild == hItemSuspectedParent ){
            break;
        }
    }
    while( (hItemChild = this->GetParentItem( hItemChild ) ) != NULL );
    return ( hItemChild != NULL );
}
  
VC++.NET2003ではCTreeCtrlクラスのヘルプでクラスの概要のページに「MFC サンプル CMNCTRL1」として
サンプルコードを取得することができました。
 
◆ CListCtrl リストビューコントロールの実装
 
リストビューとはエクスプローラの右側部分そのものです。アイコンの一覧表示(アイコンビュー)や
詳細の列表示(レポートビュー)といった形態を持つことができます。
// カラム作成
LV_COLUMN  lvclm;
lvclm.mask = LVCF_TEXT|LVCF_WIDTH;
lvclm.pszText = "名前";                // ヘッダ
lvclm.cx = 80;                         // カラムのドット幅
m_ListCtrl.InsertColumn( 0, &lvclm );  // 追加
lvclm.pszText = "サイズ";
lvclm.cx = 80;
m_ListCtrl.InsertColumn( 1, &lvclm );
lvclm.pszText = "ファイルの種類";
lvclm.cx = 150;
m_ListCtrl.InsertColumn( 2, &lvclm );
 
そして実際の項目(アイテム)を登録します。
int i = 0;
m_ListCtrl.InsertItem( i, "NAME" );
m_ListCtrl.SetItemText( i, 1, "SIZE" );
m_ListCtrl.SetItemText( i, 2, "TYPE" );
// 列幅調整
// m_ListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE );
// m_ListCtrl.SetColumnWidth( 1, LVSCW_AUTOSIZE );
// m_ListCtrl.SetColumnWidth( 2, LVSCW_AUTOSIZE );
 
上記例では i=0 として1個のアイテムしか登録しませんでしたが、
実際はループにして登録数だけi++していく必要があります。
CString Name[100], Size[100], Type[100];
int nListItem = m_ListCtrl.GetItemCount( );  // リスト登録数の取得
if( nListItem ){
    for( int i=0; i
さらに、マウスでクリックした時にどのアイテムがクリックされたかを取得するには
CListCtrl の NM_CLICK ハンドラを実装します。
// リストビュークリックハンドラ (CListCtrlのNM_CLICKハンドラ)
void C????::OnClickList( NMHDR *pNMHDR, LRESULT *pResult )
{
    NM_LISTVIEW *pNMListView = (NM_LISTVIEW *)pNMHDR;
    //  pNMListView->iItem にアイテム番号が入ります
    // 先頭列をクリックされた時のみ反応させます(エクスプローラと同じ)
    if( pNMListView->iSubItem == 0 ){
        CString name = m_ListCtrl.GetItemText( pNMListView->iItem, 0 );
        CString size = m_ListCtrl.GetItemText( pNMListView->iItem, 1 );
        CString type = m_ListCtrl.GetItemText( pNMListView->iItem, 2 );
    }
    *pResult = 0;
}   
 
選択されているアイテムを調査するにはこうします。
int nSelected = m_ListCtrl.GetNextItem( -1, LVNI_SELECTED );
if( nSelected < 0 ) // 選択されていない
 
さらにアイテムを選択状態にするにはこうします。
int i = 0; // アイテム番号
m_ListCtrl.SetFocus( );
m_ListCtrl.SetItemState( i, LVIS_SELECTED, LVIS_SELECTED );
// リストコントロール自身にフォーカスをセットしてからでないとダメ
 
同様にアイテムの属性を変えるにはこうします。
// 選択の解除
m_ListCtrl.SetItemState( i, (UINT)~LVIS_SELECTED, LVIS_SELECTED );
// フォーカスのセットと解除
m_ListCtrl.SetItemState( i, LVIS_FOCUSED, LVIS_FOCUSED );
m_ListCtrl.SetItemState( i, (UINT)~LVIS_FOCUSED, LVIS_FOCUSED );
 
ラベルを編集可能にするにはリソースエディタでスタイルのラベルの編集をチェックしておき
クラスウィザードでLVN_ENDLABELEDITメッセージのハンドラを実装し以下のように変更する。
void CEGZcDlg::OnEndlabeleditListviewDstdir(NMHDR* pNMHDR, LRESULT* pResult) 
{
    LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
    // TRUEを返して変更を有効にする
    *pResult = TRUE;
}
 
m_ListCtrl.ModifyStyle( NULL, LVS_SHOWSELALWAYS );
 
【2007.08.09追記】
 
    int nColumn = this->m_ListCtrl.GetHeaderCtrl()->GetItemCount();
 
【2007.08.27追記】
 
// 右寄せコラム設定
LVCOLUMN lvClm;
lvClm.mask = LVCF_FMT;
lvClm.fmt  = LVCFMT_RIGHT;
this->m_ListCtrl.SetColumn( 1, &lvClm );
 
ただし,一番左のカラムは配置を変更できないようです。その場合はダミーカラムを入れておくことで回避します。
【2007.09.08追記】
 
// 拡張スタイルの設定(選択時に1行全てを反転させる,グリッド線を引く)
this->m_ListCtrl.SetExtendedStyle( (LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES) );
 
【2008.12.04追記】 リストビューコントロールのフォントを変更する方法
 
    // リストコントロールのフォント設定
    this->m_ListCtrlFont.CreatePointFont( 140, "Arial" );
    this->m_ListCtrl.SetFont( &this->m_ListCtrlFont );
 
【2008.12.04追記】 リストビューコントロールのカスタムドロー
 
void CtestDlg::OnNMCustomdrawList(NMHDR *pNMHDR, LRESULT *pResult)
{
    // LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);
    // TODO : ここにコントロール通知ハンドラ コードを追加します。
    LPNMLVCUSTOMDRAW pNMLVCD = (LPNMLVCUSTOMDRAW)pNMHDR;
    switch( pNMLVCD->nmcd.dwDrawStage ){ 
        case CDDS_PREPAINT:
            *pResult = CDRF_NOTIFYSUBITEMDRAW;
            break; 
        case CDDS_ITEMPREPAINT:
            *pResult = CDRF_NOTIFYSUBITEMDRAW;
            break;
        case CDDS_ITEMPREPAINT|CDDS_SUBITEM:
            {
                // 行と列の取得
                DWORD_PTR iRow = pNMLVCD->nmcd.dwItemSpec;
                int iCol = pNMLVCD->iSubItem;
                // 以下,行 iRow と 列 iCol に応じて,さまざまな処理を実装します。
                // ★テキストおよび背景の色を変える場合
                if( iRow == xxxx && iCol == xxxx ){
                    pNMLVCD->clrTextBk = RGB(0,0,0);
                    pNMLVCD->clrText = RGB(255,255,255);
                    *pResult = CDRF_NEWFONT;
                }
                // ★フォントを変える場合
                else if( iRow == xxxx && iCol == xxxx ){
                    SelectObject( pNMLVCD->nmcd.hdc, this->m_ListCtrlFont2.m_hObject );
                    *pResult = CDRF_NEWFONT;
                }
                // ★図形を描く場合
                else if( iRow == xxxx && iCol == xxxx ){
                    // 選択されているときの背景色
                    DWORD dwColorBk;
                    if( pNMLVCD->nmcd.uItemState & CDIS_SELECTED ){
                        dwColorBk = RGB(0,0,192);
                    }
                    // 選択されていないときの背景色
                    else {
                        dwColorBk = RGB(0,0,0);
                    }
                    // 背景領域の取得
                    CRect Rect;
                    this->m_ListCtrl.GetSubItemRect( (int)iRow, iCol, LVIR_BOUNDS, Rect );
                    // 描画準備
                    CDC CD;
                    CD.Attach( pNMLVCD->nmcd.hdc );
                    int iSaveDC = CD.SaveDC();
                    // 背景塗りつぶし
                    CD.FillSolidRect( &Rect, dwColorBk );
                    // ダミー図形
                    Rect.top = Rect.top + Rect.Height() / 2;
                    Rect.bottom = Rect.top+2;
                    CD.FillSolidRect( &Rect, RGB(0,255,0) );
                    // 描画終了
                    CD.RestoreDC( iSaveDC );
                    CD.Detach();
                    // デフォルト描画スキップ
                    *pResult = CDRF_SKIPDEFAULT;
                }
                else {
                    *pResult = CDRF_DODEFAULT;
                }
            }
            break;
        default:
            *pResult = CDRF_DODEFAULT;
            break;
    }
    // *pResult = 0;
}
  
 
時間のかかる処理などでよく進行状況を表示するダイアログが開いたりしますが、
あの棒グラフみたいのなのがプログレスバーです。今回はこれを実装してみます。
リソースビューの Dialog のところで右クリックし "dialogの挿入" を選択します。 
ダイアログ上のOKボタンとキャンセルボタンを削除します。 
クラスウィザードを起動します。
クラスの追加 "IDD_PROGRESS_DIALOGは新規リソースです…" と聞いてくるので
新規クラスの作成を選択します。
 
後は実際にこのプログレスバーのついたダイアログを使用するだけです。
// 親ウインドウを無効にする
EnableWindow( FALSE );
// プログレスバーダイアログの作成
CProgressDialog pd;
pd.m_Label = "プログレスバーをテスト中...";
pd.Create( IDD_PROGRESS_DIALOG );
pd.ShowWindow( SW_SHOW );   // 2007.08.27追記(VC7)
pd.m_ProgressBar.SetRange( 0, 10 );
pd.EnableWindow( FALSE );
// プログレスバーを更新する
Sleep( 1000 ); pd.m_ProgressBar.SetPos( 1 );
Sleep( 1000 ); pd.m_ProgressBar.SetPos( 2 );
                    (略)
Sleep( 1000 ); pd.m_ProgressBar.SetPos( 9 );
Sleep( 1000 ); pd.m_ProgressBar.SetPos( 10 );
// プログレスダイアログを消去する
pd.DestroyWindow( );
// 親ウインドウを有効にする
EnableWindow( TRUE );
 
  
実際は、プログレスバーダイアログの作成部分と、プログレスバーの更新部分は別々の関数のはずで
長い処理に入る前にダイアログを作成し、長い処理の中からコールバック関数の形でプログレスバーの
更新を行う形になります。
 
リソースビューを見ているとVS_VERSION_INFOとかいうVersion管理情報があったりして、
さもここでバージョンを管理してくださいってなっているのですが、同じリソースビューにある
Aboutダイアログにはスタティックテキストでバージョンが書かれていたりして、
ここまでするなら自動的に連動してくれよなんて思うのですが、自分でやらなければ
ならないようです。
GetFileVersionInfoSize( )
GetFileVersionInfo( )
VerQueryValue( )
 
を組み合わせて使えばいいことがわかりました。
これを元にバージョンを取得するクラスなんかを作成すれば便利なのですが、
これが結構面倒くさいのです。
おパパさん
 
という方が作られた大変すばらしい
バージョンリソースクラスのサンプル
 
を見つけてしまいました。マクロの使い方が凄いです。
でもマクロでメンバ関数が記述されているがゆえにデバグの時ちょっと不便だったのと
何をやっているか自分なりにかみくだいてコーディングしてみたかったので
書き換えたクラスが これ  です。
ほとんどパクリです。スイマセン(^^;;;
 
タスクトレイにアイコンを表示し、そのアイコンからのイベントを受け取るには以下のようにします。
・ダイアログまたはメインフレームの定義追加
#define MY_NOTIFYICON (WM_APP+100)    // 通知メッセージ
・ダイアログまたはメインフレームのメンバ変数として追加
NOTIFYICONDATA  nIcon;
・ダイアログなら OnInitDialog( ) で、メインフレームの場合は OnCreate( ) に
  次のコードを追加
// タスクトレイアイコンの初期化
nIcon.cbSize = sizeof( NOTIFYICONDATA );
nIcon.uID = 1;
nIcon.hWnd = this->m_hWnd;
nIcon.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
nIcon.hIcon = AfxGetApp( )->LoadIcon( IDR_MAINFRAME );
nIcon.uCallbackMessage = MY_NOTIFYICON;
// タスクトレイにマウスポインタを置いたときに表示される文字列
lstrcpy( nIcon.szTip, "TEST" );
・タスクトレイにアイコンを表示する
Shell_NotifyIcon( NIM_ADD, &nIcon );
・終了時にアイコンを消去するために DestroyWindow( ) を
  クラスウィザードでオーバライドする。
void C????::DestroyWindow( ) 
{
    Shell_NotifyIcon( NIM_DELETE, &nIcon );
    return CDialog::DestroyWindow( );
}
・アイコンからのイベントを受け取るためにクラスウィザードで
  WindowProc( ) をハンドルし以下のように実装する。
LRESULT C????::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
    if( message == MY_NOTIFYICON ){
        if( lParam == WM_LBUTTONDBLCLK ){
            // アイコンが左ダブルクリックされたときの処理
        }
    }
    return CDialog::WindowProc(message, wParam, lParam);
}
 
またタスクトレイにアイコンを表示している間、メインウインドウを隠す場合は以下のように
するよ良いでしょう。
// メインウインドウを隠す
ShowWindow( SW_HIDE );
// メインウインドウを復帰させる
ShowWindow( SW_RESTORE );
SetForegroundWindow98( this->m_hWnd );
 
SetForegroundWindow98( ) についてはここ を参照してください。
これ です。
 
キーが押されたり離されたりするイベントを受けるには、クラスウィザードで WM_KEYDOWN および
WM_KEYUP メッセージを実装します。メッセージハンドラは OnKeyDown( ) および OnKeyUp( ) で、
何のキーが押されたか引数 nChar でわかるようになっています。
アルファベットのキーであれば'A'などの値と比較することで何のキーが押されたか判定できます。
矢印キーなど特殊なものは仮想キーコードとして扱われ、その内容は msdev\include\winuser.h に
 VK_???? と定義されているので確認してみてください。以下にサンプルを示します。
// キー離上ハンドラ
void C????Dlg::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    switch( nChar ){
    case VK_PRIOR:
        // PageUp
        break;
    case VK_NEXT:
        // PageDown
        break;
    case VK_HOME:
        // Home
        break;
    case VK_END:
        // End
        break;
    case VK_F5:
        // F5
        break;
    case VK_LEFT:
        // ←
        break;
    case VK_RIGHT:
        // →
        break;
    case VK_UP:
        // ↑
        break;
    case VK_DOWN:
        // ↓
        break;
    case 'C':
        if( GetKeyState( VK_CONTROL ) & 0x8000 ){
            // Ctrl + C
        }
        break;
    }
}
 
なおダイアログでボタンなどが実装されている場合、ダイアログ自身にはキーイベントが届きません。
OnKeyDown( ) および OnKeyUp( ) を実装しても実行されないのです。
ダイアログ自身でキーを受けたい場合は、その上に載っているコントロールを全て無効にするか、
以下のようにメッセージハンドラ PreTranslateMessage( ) を実装してメッセージが処理される前に
直接制御を行います。
BOOL C????Dlg::PreTranslateMessage(MSG* pMsg) 
{
    // F5キーの離上に反応する
    if( pMsg->message == WM_KEYUP ){
        if( pMsg->wParam == VK_F5 ){
            // 最新の状態に更新など…
            return( TRUE );
        }
    }
    // ESCキーの押下を無効にする
    if( pMsg->message == WM_KEYDOWN ){
        if( pMsg->wParam == VK_ESCAPE ) return( TRUE );
    }
    return CDialog::PreTranslateMessage(pMsg);
}
 
PreTranslateMessage( )内部でメッセージの処理を行った際は、必ず TRUE で戻ることを
忘れないようにしてください。動作がおかしくなる場合があります。
 
デスクトップの大きさは以下のようにして取得できます。
CRect DesktopRect;
GetDesktopWindow( )->GetClientRect( DesktopRect );
 
ただしこれはタスクバーサイズも含まれています。タスクバーの含まれないデスクトップサイズを
取得するにはこうします。
RECT rect;
SystemParametersInfo( SPI_GETWORKAREA, 0, &rect, 0 );
 
以上。
 
ウインドウの位置と大きさを取得したり設定するには以下のようにします。
// ウインドウの位置と大きさを取得する
CRect rect;
GetWindowRect( rect );
// ウインドウのクライアント領域(描画領域)の大きさを取得する
GetClientRect( rect );
// ウインドウを親ウインドウの中央に配置する。
CenterWindow( );
// ※引数として基準にしたいウインドウへのポインタを指定できる。
//   デスクトップを基準にしたい場合は GetDesktopWindow( ) の戻値を使用する。
// ウインドウの位置のみを変更する
SetWindowPos( &wndTop, 100, 100, 0, 0, SWP_NOSIZE );
// ウインドウの位置と大きさを変更する
SetWindowPos( &wndTop, 100, 100, 640, 480, SWP_DRAWFRAME|SWP_SHOWWINDOW );
// ウインドウを常に手前に表示する
SetWindowPos( &wndTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE );
// 常に手前に表示するのを解除する
SetWindowPos( &wndNoTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE );
// ウインドウを最大化する
ShowWindow( SW_SHOWMAXIMIZED );
// ※リソースエディタでダイアログのプロパティのシステムメニューと
//   最大化を有効にしておく
// ウインドウを最小化(アイコン化)する
ShowWindow( SW_MINIMIZE );
// ※リソースエディタでダイアログのプロパティのシステムメニューと
//   最小化を有効にしておく
// ウインドウが最大化されているか確認する
BOOL bRet = IsZoomed( );
// ウインドウが最小化されているか確認する
BOOL bRet = IsIconic( );
// ウインドウを非表示にする
ShowWindow( SW_HIDE );
// ウインドウを最大化・最小化・非表示から元に戻す
ShowWindow( SW_RESTORE );
 
 
テキストをクリップボードにコピーする方法
// テキストをクリップボードにコピー
char *pText = "copy_text";
HGLOBAL hGlobal = GlobalAlloc( GHND, strlen( pText ) + 1 );
char *pBuf = (char *)GlobalLock( hGlobal );
strcpy( pBuf, pText );
GlobalUnlock( hGlobal );
if( OpenClipboard( ) == TRUE ){
    EmptyClipboard( );
    SetClipboardData( CF_TEXT, hGlobal );
    CloseClipboard( );
}
// [注意] クリップボードにコピーしたメモリは
//        OSの管理下になるので解放してはいけない
 
クリップボードからテキストを取得する方法
// クリップボードからテキストを取得
char szText[256] = { 0 };
if( OpenClipboard( ) == TRUE ){
    HGLOBAL hGlobal = GetClipboardData( CF_TEXT );
    CloseClipboard( );
    if( hGlobal ){
        char *pBuf = (char *)GlobalLock( hGlobal );
        if( pBuf ){
            strcpy( szText, pBuf );
            GlobalUnlock( hGlobal );
        }
    }
}
// [注意] 取得を受けるバッファサイズが足りていることを確認すること
 
ビットマップをクリップボードへコピーする方法
// ピクチャーコントロールに描画されたビットマップを
// クリップボードへコピー
CWnd *pWnd = GetDlgItem( IDC_PICTURECTRL );
CRect Rect;
pWnd->GetClientRect( Rect );
CClientDC DCsrc( pWnd );
CDC DCdst;
DCdst.CreateCompatibleDC( &DCsrc );
CBitmap Bitmap;
Bitmap.CreateCompatibleBitmap( &DCsrc, Rect.Width( ), Rect.Height( ) );
DCdst.SelectObject( &Bitmap );
DCdst.BitBlt( 0, 0, Rect.Width( ), Rect.Height( ), &DCsrc, 0, 0, SRCCOPY );
if( OpenClipboard( ) == TRUE ){
    EmptyClipboard( );
    SetClipboardData( CF_BITMAP, (HBITMAP)Bitmap );
    CloseClipboard( );
}
// [注意] 上の例ではDCsrcとDCdstのデストラクタでデバイスコンテキストの
//        解放が行われるのでわざわざ DCdst.DeleteDC( ) および
//        DCsrc.DeleteDC( ) しなくてもよい
//        またSelectObject( )で戻るオブジェクトを復帰させるとうまく動作しない
 
クリップボードからビットマップを取得して描画する方法
// クリップボードからビットマップを取得して描画
if( OpenClipboard( ) == TRUE ){
    HBITMAP hBitmap = (HBITMAP)GetClipboardData( CF_BITMAP );
    CloseClipboard( );
    if( hBitmap ){
        BITMAP bm;
        if( GetObject( hBitmap, sizeof( BITMAP ), &bm ) ){
            // ピクチャーコントロールに描画
            CWnd *pWnd = GetDlgItem( IDC_PICTURECTRL );
            CClientDC DCdst( pWnd );
            CDC DCsrc;
            DCsrc.CreateCompatibleDC( &DCdst );
            HBITMAP hOld = (HBITMAP)SelectObject( DCsrc.GetSafeHdc( ), hBitmap );
            // 等倍で描画する場合
            DCdst.BitBlt( 0, 0, bm.bmWidth, bm.bmHeight, &DCsrc, 0, 0, SRCCOPY );
            // 領域内に収めて描画する場合
            CRect Rect;
            pWnd->GetClientRect( Rect );
            DCdst.StretchBlt( 0, 0, Rect.Width( ), Rect.Height( ), &DCsrc, 0, 0,
                                             bm.bmWidth, bm.bmHeight, SRCCOPY );
            SelectObject( DCsrc.GetSafeHdc( ), hOld );
        }
    }
}
// [注意] 上の例ではDCdstとDCsrcのデストラクタでデバイスコンテキストの
//        解放が行われるのでわざわざ DCdst.DeleteDC( ) および
//        DCsrc.DeleteDC( ) しなくてもよい
 
 
◆ リリースモードDLLとでバグモードDLLで発生する問題
 
Win95+VC4.0の環境では気づかなかったのですが、NT+VC4.0ではデバッグ実行した後に
HEAP[アプリ名]: Invalid Address specified to RtlFreeHeap( 130000, ef0568 )
 
と表示されることがありました。googleで調べてみるとメモリを確保するようなDLL(例えばSUSIEプラグイン)
を使用し、そのメモリの解放を本体で行う場合に発生することがわかりました。
参考 http://homepage2.nifty.com/tulip-an/soft/scrwrpprblm.html
 
 
ダイアログ一つで済むアプリケーションならともかく、比較的規模が大きくなると
アプリケーション全体からアクセスする必要のある設定値などの変数をどこに確保したら良いかが
問題となってきます。これらは複数のオブジェクトのメンバ変数としてバラバラに確保されたり
すると管理も大変になるので構造体などで一元管理しておきたいものです。
でも複数のオブジェクトからそれぞれどうやって一元管理したデータにアクセスすれば
いいでしょうか?
 
リソースエディタでダイアログにスクロールバーを追加できますが、その制御はコントロール
変数を割り当てて行うのではなく、ダイアログ自身の(正確には親クラスの)メンバ関数で行います。
詳細はヘルプの「CWnd スクロール関数」を確認してください。
SCROLLINFO ScrollInfo;
ScrollInfo.cbSize = sizeof( SCROLLINFO );
ScrollInfo.fMask = SIF_ALL|SIF_DISABLENOSCROLL;
ScrollInfo.nMin = min;          // 最小値
ScrollInfo.nMax = max;          // 最大値
ScrollInfo.nPage = page;        // スクロールバーのサイズ
ScrollInfo.nPos = 0;            // スクロールバーの位置
ScrollInfo.nTrackPos = NULL;    // お約束
// 水平スクロールバーへの設定の場合
this->SetScrollInfo( SB_HORZ, &ScrollInfo );
// 垂直スクロールバーへの設定の場合
this->SetScrollInfo( SB_VERT, &ScrollInfo );
// 両方のスクロールバーへの設定の場合
this->SetScrollInfo( SB_BOTH, &ScrollInfo );
 
スクロールバーが無効になる条件の場合、デフォルトでは非表示になりますが上記のように
SIF_DISABLENOSCROLLオプションを付けておくと非表示の代わりに淡色化されます。
ここ にサンプルを示します。
 
コンパイル時に、
「fatal error C1010: プリコンパイル済みヘッダーの検索中に予期しない EOF を検出しました。」
と表示されエラーになる場合は、そのソースファイルの先頭で
#include "stdafx.h" する必要があります。
 
メニューバーやコンテキストメニュー(ポップアップメニュー)を動的に変更する方法を解説します。
クラスウィザードでは実現できないので、マニュアルでソースを変更しないとできません。
// インプリメンテーション
protected:
    HICON m_hIcon;
    // 生成されたメッセージ マップ関数
    //{{AFX_MSG(CMenutestDlg)
    virtual BOOL OnInitDialog( );
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint( );
    afx_msg HCURSOR OnQueryDragIcon( );
    afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
★  afx_msg void OnInitMenu(CMenu* pMenu);    // メニュー初期化ハンドラ追加
    //}}AFX_MSG
★  afx_msg void OnExecMenu( UINT uID );    // メニュー項目実行ハンドラ追加
    DECLARE_MESSAGE_MAP( )
 
メニュー項目実行ハンドラの追加位置が、//{{AFX_MSG 〜 //}}AFX_MSG の外側に書くのは、
このハンドラ一つで動的に追加された複数のメニュー項目からのメッセージコマンドを受け付ける
ことによるからです。ClassWizardは複数のメッセージコマンドを一つの関数で受け付けるのに
対応していないので、ClassWizardの編集対象外の位置に追加します。なお動的に追加するメニュー
の個数が少なく既定の場合は、個別にハンドラを作成し //{{AFX_MSG 〜 //}}AFX_MSG 内に記述する
こともできると思います。
BEGIN_MESSAGE_MAP(CMenutestDlg, CDialog)
    //{{AFX_MSG_MAP(CMenutestDlg)
    ON_WM_SYSCOMMAND( )
    ON_WM_PAINT( )
    ON_WM_QUERYDRAGICON( )
    ON_WM_CONTEXTMENU( )
    ON_WM_INITMENU( )    // ★メニュー初期化メッセージコマンド追加
    //}}AFX_MSG_MAP
    ON_COMMAND_RANGE( 1000, 1099, OnExecMenu )    // ★メッセージコマンド追加
END_MESSAGE_MAP( )
 
上記変更によってメニューオブジェクトが初期化される際と動的に追加されたメニュー項目が
選択実行された場合に、これから用意するハンドラOnInitMenu( ), OnExecMenu( )が実行されるよう
になります。メッセージコマンド追加部分の1000, 1099というのはメニュー項目を動的に追加する
時に指定する、その項目が選択実行された場合に発行されるメッセージコマンドIDです。
// メニュー初期化ハンドラ
void CMenutestDlg::OnInitMenu(CMenu* pMenu) 
{
    CDialog::OnInitMenu(pMenu);
    // ここでメニューの以前の項目の削除と新しい項目の追加を行う
    CString str;
    pMenu->GetMenuString( 0, str, MF_BYPOSITION );
    if( str == "TEST0" ){
        pMenu = pMenu->GetSubMenu( 0 );
        pMenu->RemoveMenu( 1, MF_BYPOSITION );
        pMenu->AppendMenu( MF_STRING, 1000, "MENU APPEND" );
        DrawMenuBar( );
    }
    else if( str == "POPUP0" ){
        pMenu->AppendMenu( MF_STRING, 1010, "POPUP APPEND" );
    }
}
 
上記の例では先頭項目が"TEST0"というメニューバーに「最近使ったファイル」のような感じで
"MENU APPEND"という項目を追加しているのと、先頭項目が"POPUP0"というコンテキストメニューに
"POPUP APPEND"という項目を追加しています。
// メニュー項目実行ハンドラ
void CMenutestDlg::OnExecMenu( UINT uID )
{
    afxDump << uID << "\n";
}
 
uIDの値を見て、どのメニュー項目が選択実行されたかを知るわけです。このハンドラはIDが
1000〜1099までのものを渡されるので、メニュー項目を追加する場合にそれらのIDを用いる
ようにします。なおIDの値には特に制限はないようですが、シンボルブラウザで重複しないように
定義しておくのが気持ちがいいかしれません。これ 
はお気に入りフォルダ以下を検索し階層メニューを作成するサンプルです。
 
複数のCStringオブジェクトを管理したい場合にCStringListオブジェクトが便利です。
自分で作ろうかと思ってたら既にありました。ぐはー。
CStringList StrList;
CString str = "TEST0";
StrList.AddHead( str );
StrList.AddHead( "TEST1" );
StrList.AddHead( "TEST2" );
StrList.AddHead( "TEST3" );
afxDump << StrList.GetCount( ) << "\n";
POSITION pos = StrList.GetHeadPosition( );
while( pos ){
    str = StrList.GetNext( pos );
    afxDump << "[" << pos << "]: " << str << "\n";
}
 
注意するべき点はリスト内部で管理されたCStringオブジェクトの位置はPOSITION型だと
いうことです。連続して読み出す場合などは上記例のwhileループ内部を参考にしてください。
詳細はCStringListのヘルプ参照のこと。
CStringArray StrArray;
CString str = "TEST0";
StrArray.Add( str );     if( StrArray.GetSize( ) > 5 ) StrArray.RemoveAt( 0 );
StrArray.Add( "TEST1" ); if( StrArray.GetSize( ) > 5 ) StrArray.RemoveAt( 0 );
StrArray.Add( "TEST2" ); if( StrArray.GetSize( ) > 5 ) StrArray.RemoveAt( 0 );
for( int i=0; i
上の例では配列要素数を最大5個に制限しています。
【2008.10.28追記】ソート可能な CStringArray
 [HOWTO] MFC では、CStringArray 並べ替え方法 
class CSortStringArray : public CStringArray {
public:
    void Sort(int dir = 0)
    {
        BOOL bNotDone = TRUE;
        while (bNotDone)
        {
            bNotDone = FALSE;
            for(int pos = 0;pos < GetUpperBound();pos++)
                bNotDone |= CompareAndSwap(pos, dir);
        }
    }
private:
    BOOL CompareAndSwap(int pos, int dir)
    {
        CString temp;
        int posFirst = pos;
        int posNext = pos + 1;
        if( dir == 0 ){
            if (GetAt(posFirst).CompareNoCase(GetAt(posNext)) > 0){
                temp = GetAt(posFirst);
                SetAt(posFirst, GetAt(posNext));
                SetAt(posNext, temp);
                return TRUE;
            }
        }
        else {
            if (GetAt(posFirst).CompareNoCase(GetAt(posNext)) <= 0){
                temp = GetAt(posFirst);
                SetAt(posFirst, GetAt(posNext));
                SetAt(posNext, temp);
                return TRUE;
            }
        }
        return FALSE;
    }
};
 
 
ファイル名を指定するのに良く使われる「ファイルを開く」ダイアログはコモンダイアログと
呼ばれており以下のようにして簡単に使用できます。
下の例では取得したファイル名をエディットボックスのコントロール変数にセットしています。
/* TRUEだと「ファイルを開く」,FALSEだと「ファイルを名前を付けて保存」 */
CFileDialog dlg( TRUE );
long nResponse = dlg.DoModal( );
if( nResponse == IDOK ){
    /* エディットボックスにセット */
    m_Filename.SetWindowText( dlg.GetPathName( ) );
}
 
また拡張子を選択するような場合は,以下のように行います。
CFileDialog dlg( TRUE, ".jpg", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
    "読込み可能ファイル(*.jpg;*.bmp)|*.jpg;*.bmp|全てのファイル(*.*)|*.*||" );
long ret = dlg.DoModal( );
if( ret == IDOK ){
    m_FileName.SetWindowText( dlg.GetPathName( ) );
}
 
フォルダの初期位置やキャプションを指定するにはDoModal( )を行う前にダイアログのメンバ変数を変更します。
CFileDialog dlg( FALSE );
dlg.m_ofn.lpstrTitle = "お気に入りに追加";            // キャプションの変更
dlg.m_ofn.lpstrInitialDir = "C:\\WINDOWS\\FAVORITE";  // フォルダの初期位置の変更
dlg.m_ofn.lpstrFile = "filename.dat";                 // デフォルトファイル名の設定
dlg.m_ofn.nMaxFile = MAX_PATH;                        // ファイル名の最大文字数
long nResponse = dlg.DoModal( );
 
lpstrFileをフルパスでセットした場合はlpstrInitialDirを設定しなくてもそのフォルダが開きます。
(VC7)2006.10.20追記 
    CFileDialog dlg( FALSE );
    char szFilename[MAX_PATH];
    strcpy( szFilename, "filename.dat" );
    dlg.m_ofn.lpstrTitle = "お気に入りに追加";            // キャプションの変更
    dlg.m_ofn.lpstrInitialDir = "C:\\WINDOWS\\FAVORITE";  // フォルダの初期位置の変更
    dlg.m_ofn.lpstrFile = szFilename;                     // デフォルトファイル名の設定
    dlg.m_ofn.nMaxFile = MAX_PATH;                        // ファイル名の最大文字数
    INT_PTR nRet = dlg.DoModal();
    if( nRet == IDOK ){
        this->GetDlgItem(IDC_EDIT_FILE)->SetWindowText( dlg.GetPathName() );
    }
 
またフォルダを選択するダイアログも見たことがあるかと思いますが、
残念ながらコモンダイアログでは用意されていません。
これ です。かぶ さんという方の
サイトを参考にしました。
ここ の
シェルエクステンションという項目です。
CFolderDialog dlg( "フォルダを選択してください" );
if( dlg.DoModal( this->GetSafeHwnd( ) ) == IDOK ){
    CString path = dlg.GetPathName( );
}
 
 
ダイアログベースのアプリケーションにメニューを追加したり,マウス右ボタンのクリックで
表示されるコンテキストメニュー(ポップアップメニュー)を実装する方法を解説します。
リソースエディタのリソース一覧表示部分で右クリックし「挿入...」を選択,Menuを挿入する。
 新規追加されたメニューリソースのプロパティで適当なIDをつける。例"IDR_MENU"
 メニューアイテムを作成する。このときアイテムのプロパティで「ポップアップ」をチェックすると,
子メニューを作成することができる。
 リソースエディタでメニューのプロパティからクラスウィザードを起動すると「クラスの追加」という
問い合わせてくるので「既存のクラスを選択」しメニューを表示するダイアログを選択する。
 起動したクラスウィザードで追加したメニューアイテムのIDを選択しメッセージからCOMMANDを
ダブルクリックすると,メニューアイテムを選択したときに実行されるハンドラ関数名を聞いてくるので設定する。この操作を各アイテム毎に行う。
 それぞれのハンドラ関数にやりたいことを実装する。
  
ダイアログにメニューを追加する場合はリソースエディタでダイアログ自身のプロパティを変更します。
一般タブの「メニュー」に追加するメニューIDを設定します。メニューアイテムを有効・無効にしたり
チェックマークを付けたりするにはダイアログオブジェクト内で以下のようにします。
CMenu *pMenu = GetMenu( );
pMenu->EnableMenuItem( IDM_?, MF_GRAYED|MF_BYCOMMAND ); // 無効(淡色)にする
pMenu->EnableMenuItem( IDM_?, MF_ENABLED|MF_BYCOMMAND ); // 有効にする
pMenu->CheckMenuItem( IDM_?, MF_CHECKED|MF_BYCOMMAND ); //チェックマークを付ける
pMenu->CheckMenuItem( IDM_?, MF_UNCHECKED|MF_BYCOMMAND ); //チェックマークを外す
DrawMenuBar( ); // メニューの再描画
 
コンテキストメニューを作成する場合はクラスウィザードを起動してダイアログのメッセージから
WM_CONTEXTMENUを選択して以下のようにハンドラ関数を実装します。
void CTmpDlg::OnContextMenu(CWnd* pWnd, CPoint point) 
{
    CMenu menu;
    menu.LoadMenu( IDR_MENU ); // IDR_MENU はメニューリソースID
    CMenu *pPopup = menu.GetSubMenu( 0 );
    // コンテキスト(ポップアップ)メニュー表示
    pPopup->TrackPopupMenu( TPM_LEFTALIGN|TPM_RIGHTBUTTON, point.x, point.y, this );
}
 
【メニュー追記】
 
// メニュー項目の ON_COMMAND メッセージハンドラ
    BOOL bShow = m_wndXXXX.IsVisible();
    ShowControlBar(&m_wndXXXX, !bShow, FALSE);
// メニュー項目の ON_UPDATE_COMMAND_UI メッセージハンドラ
    pCmdUI->Enable();
    pCmdUI->SetCheck(m_wndXXXX.IsVisible());
 
上記の例ではウインドウ m_wndXXXX の表示,非表示をメニューでトグル的に操作しています。
【2008.10.28追記】
 
void CMainFrame::OnUpdateXXXX(CCmdUI *pCmdUI)
{
    // 有効にする場合
    pCmdUI->Enable(TRUE);
    // 無効にする場合
    pCmdUI->Enable(FALSE);
    // チェックを入れる場合
    pCmdUI->SetCheck(TRUE);
    // チェックを外す場合
    pCmdUI->SetCheck(FALSE);
 
pCmdUI->xxxx のメンバ関数で,メニュー項目の表示前に色々できるようです。
 
オプション設定などでよく用いられるタブがいくつかついたダイアログはプロパティシートで作成できます。
挿入→コンポーネントでプロパティシートを追加する(アクセス権はベースとなるダイアログに設定する)
 ウィザードによりプロパティシートクラスと各シートのクラス,およびリソースが自動的に作成される。クラス名はわかりやすい名前に変更した方がいいでしょう。
 リソースエディタで追加された各シートのプロパティのキャプションを変更しタブ文字列とする。
 同じくリソースエディタで追加された各シートに好きなコントロールをひょいひょいと載せる。ID設定も気の利いたものを。
 搭載したコントロールにメンバ変数を「値」で割り付ける。
 リソースビューで「挿入...」しMenuを追加する。
 メニューバーについてクラスウィザードを起動すると「クラスの追加」ウインドウが開くので
「既存のクラスを選択」にし,メニューを表示させる元ダイアログやフレームを選択する。
 メニューバーを表示させるダイアログやフレームについてリソースエディタでプロパティを表示し
「一般」タブ内にある「メニュー」をさっき追加したメニューのIDを設定する。
 メニューバーに項目「オプション」を追加する。IDはIDM_OPTION等。
 追加した項目についてクラスウィザードを起動し,メッセージのCOMMANDをダブルクリックして、
メニュー選択ハンドラを実装する。
 メニュー選択ハンドラから、プロパティシートを追加したウィザードによって自動追加されたOnProperties( )を呼び出す。
 OnProperties( )内で適宜以下のような変更を行う。
CMyPropertySheet propSheet;
// 適用ボタンを消す
propSheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;  
// ヘルプボタンを消す
propSheet.m_psh.dwFlags &= ~PSH_HASHELP;
propSheet.m_Page1.m_psp.dwFlags &= ~PSP_HASHELP;
propSheet.m_Page2.m_psp.dwFlags &= ~PSP_HASHELP;
propSheet.m_Page3.m_psp.dwFlags &= ~PSP_HASHELP;
//現在の設定値をメンバ変数にセットする。
m_Static1.GetWindowText( propSheet.m_Page1.m_Edit );
m_Static2.GetWindowText( propSheet.m_Page2.m_Edit );
m_Static3.GetWindowText( propSheet.m_Page3.m_Edit );
if( propSheet.DoModal( ) == IDOK ){
    // OKが押されたときにメンバ変数を読み取って設定に反映させる。
    m_Static1.SetWindowText( propSheet.m_Page1.m_Edit );
    m_Static2.SetWindowText( propSheet.m_Page2.m_Edit );
    m_Static3.SetWindowText( propSheet.m_Page3.m_Edit );
}
 
とする。
  
プロパティシートの大きさも各シートの大きさに自動的にあわせてくれるし,うぉー結構簡単だったぜぃ!
解説 によると…
メモ
対応するダイアログ リソースから最初にプロパティ ページが作成されるとき、
初回例外が発生する可能性があります。これは、プロパティ ページによって、
ページの作成前にダイアログ リソースのスタイルが必要なスタイルに変更される
ためです。通常、リソースは書き込み禁止であるため、例外が発生する可能性が
あります。この例外はシステムによって処理され、システムによって、更新された
リソースのコピーが自動的に作成されます。このような処理により、初回例外は
無視できるようになります。
 
だそうです。
// WINBUG: Windows currently does not support DIALOGEX resources!
// Assert that the template is *not* a DIALOGEX template.
// DIALOGEX templates are not supported by the PropertySheet API.
// To change a DIALOGEX template back to a DIALOG template,
// remove the following:
//  1. Extended styles on the dialog
//  2. Help IDs on any control in the dialog
//  3. Control IDs that are DWORDs
//  4. Weight, italic, or charset attributes on the dialog's font
 
ようするにバグというか仕様らしいです。
プロパティページに載せるダイアログアイテムのプロパティの拡張スタイルは全て設定しないように
すれば大丈夫みたいです。
2006.10.20追記 
VC++4.0では上記のように「挿入」→「コンポーネント」でプロパティシートを自動的に追加してくれましたが,
VC++.NET2003ではやってくれないようです。しょうがないので以下のように手作業します。
	プロパティシートのタブで開く各ページをダイアログで新規に作成します。 該当ダイアログを右クリックして「プロパティ」を選択し,IDとCaptionを適切に設定。
     プロパティシートのタブの数だけ上記のようにダイアログを作成。
     それぞれのダイアログにコントロールを載せて適切にIDなどを設定する。
     それぞれのダイアログのクラスを新規作成する。
        該当ダイアログを右クリックして「クラスの追加」を選択し,基本クラスを「CPropertyPage」にする。クラス名を適切に命名して「完了」。 これらのページをまとまるプロパティシートを新規に作成します。プロジェクト→「クラスの追加」で Visual C++ のツリーから MFC を選択し,「MFCクラス」を選んで「開く」。
        基本クラスを「CPropertySheet」にしてクラス名を適切に命名して「完了」。
     新たに作成されたプロパティシートのヘッダファイルに全てのプロパティページのヘッダファイルを#includeするとともに,パブリックメンバとしてプロパティページの実体を追加する(以下参照)。
#include "PropertyPageXXXX.h"
#include "PropertyPageYYYY.h"
#include "PropertyPageZZZZ.h"
            .
            .
            .
public:
    CPropertyPageXXXX m_PropertyPageXXXX;
    CPropertyPageYYYY m_PropertyPageYYYY;
    CPropertyPageZZZZ m_PropertyPageZZZZ;
 
     さらにプロパティシートのソースファイルのコンストラクタ(2つある)の部分に以下のようにページを追加する処理を実装する。
IMPLEMENT_DYNAMIC(CPropertySheetOption, CPropertySheet)
CPropertySheetAAAA::CPropertySheetAAAA(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
    this->AddPage( &this->m_PropertyPageXXXX );
    this->AddPage( &this->m_PropertyPageYYYY );
    this->AddPage( &this->m_PropertyPageZZZZ );
}
CPropertySheetAAAA::CPropertySheetAAAA(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
    this->AddPage( &this->m_PropertyPageXXXX );
    this->AddPage( &this->m_PropertyPageYYYY );
    this->AddPage( &this->m_PropertyPageZZZZ );
}
 
     あとはプロパティシートを表示させたいハンドラでプロパティシートをDoModal()するだけです。
        ただ,実体を宣言するときは VC4++ の時と異なり,ちゃんとコンストラクタに引数を渡さなければならないようです。
        具体例は以下を見てください。
    CPropertySheetAAAA PropSheet( "AAAA", this, 0 );
    // ;;;; 現在の設定をセット
    INT_PTR nRet = PropSheet.DoModal();
    if( nRet == IDOK ){
        // ;;;; 設定を変更
    }
 
  
 
CFile クラスを使用するのが簡単です。
CFile File;
BYTE *pFileImage;
/* ファイルダイアログによるファイル名の取得 */
CFileDialog dlg( TRUE );
INT_PTR nRet = dlg.DoModal();
if( nRet == IDOK ){
    // ファイル読み込み
    if( File.Open( dlg.GetPathName(), CFile::modeRead ) == TRUE ){
        UINT nLen = (UINT)File.GetLength();
        pFileImage = new BYTE [nLen];
        File.Read( pFileImage, nLen );
        File.Close();
    }
    /* ファイル新規作成(上書き) */
    if( File.Open( dlg.GetPathName(), CFile::modeCreate|CFile::modeWrite ) == TRUE ){
        unsigned int nData;
        File.Write( &nData, sizeof(unsigned int) );
        File.Close( );
    }
}
 
1行ずつテキストを読み書きするだけなら以下のように CStdioFile を使うのが一番簡単です。
CStdioFile File( pszFilename, CFile::modeReadWrite|CFile::typeText );
CString String;
File.ReadString( String );
File.WriteString( String );
 
 
// 描画領域としてリソースエディタでピクチャーコントロールを作成し
// コントロール変数を付加
CStatic    m_Area;
/* ペン形式を設定する変数を確保しておく */
CPen Pen;
/* 初期化関数でペン形式を設定しておく */
Pen.CreatePen( PS_SOLID, 1, RGB( 0x00, 0xff, 0x00 ) );
/* 描画が必要な場所で以下のように描画を行う */
long x0, y0, x1, y1;
CPen *pPenOld;
CDC *pDC;
pDC = m_Area.GetDC( );
pPenOld = pDC->SelectObject( &Pen );
/* もちろん座標の設定が必要(x0,y0,x1,y1) */
pDC->MoveTo( x0, y0 ); /* 始点 */
pDC->LineTo( x1, y1 ); /* 終点 */
pDC->SelectObject( pPenOld );
/* デバイスコンテキストの開放を忘れないこと */
ReleaseDC( pDC );
 
 
例えば1秒毎に何か処理をしたいというプログラムは「タイマー」を利用すると可能です。
【2010.04.27追記】
 
// 高分解能待機関数
void WaitPerformance( LONGLONG llCount )
{
    LARGE_INTEGER liCountStart, liCountNew;
    LONGLONG llDiff;
    BOOL bRet = QueryPerformanceCounter( &liCountStart );
    // 高分解能パフォーマンスカウンタ非対応なら1msecスリープ
    if( bRet == 0 || llCount == 0 ){
        Sleep( 1 );
    }
    else {
        for(;;){
            QueryPerformanceCounter( &liCountNew );
            llDiff = liCountNew.QuadPart - liCountStart.QuadPart;
            if( llDiff >= llCount ){
                break;
            }
        }
    }
}
    // 0.2msec以上待つ
    LARGE_INTEGER liFrequency;
    BOOL bRet = QueryPerformanceFrequency( &liFrequency );
    if( bRet ){
        LONGLONG llWaitCount;
        // 0.2msec = 0.0002sec = 1/5000sec
        llWaitCount = liFrequency.QuadPart / 5000;
        WaitPerformance( llWaitCount );
    }
    else {
        Sleep( 1 );
    }
 
 
メニューからオプションを選択、何々の設定…とかやる方法。
リソースビューでDialogを右クリックし「Dialog」の挿入を行う。
 追加したダイアログに好きなコントロールをひょいひょいと載せる。ID設定も気の利いたものを。
 搭載したコントロールにメンバ変数を「値」で割り付ける。
 追加したダイアログについてクラスウィザードを起動すると「クラスの追加」ウインドウが開くので
で「新規クラスの作成」を選択する。
 クラス名はCOption等,基本クラスはCDialogにする。
 リソースビューで「挿入...」しMenuを追加する。
 メニューバーについてクラスウィザードを起動すると「クラスの追加」ウインドウが開くので
「既存のクラスを選択」にし,メニューを表示させる元ダイアログやフレームを選択する。
 メニューバーを表示させるダイアログやフレームについてリソースエディタでプロパティを表示し
「一般」タブ内にある「メニュー」をさっき追加したメニューのIDを設定する。
 メニューバーに項目を追加する。IDは何でもいいらしい。ID_MENU等。
 さらに追加した項目についてクラスウィザードを起動し,メッセージのCOMMANDをダブルクリックして、
ハンドラ関数名を決定する。
 そのハンドラ内で
// TODO: この位置にコマンド ハンドラ用のコードを追加してください
COption dlg;
// 現在の設定値をメンバ変数にセットする。
dlg.m_data = data;
if( dlg.DoModal( ) == IDOK ){
    // OKが押されたときにメンバ変数を読み取って設定に反映させる。
    data = dlg.m_data;
}
 
とする。
 上記ハンドラが記述されるファイルでCOptionクラスの定義ヘッダファイルを#includeする。
  
 
エクスプローラからドラッグ&ドロップしてきたファイルを開かずに、
そのファイル名だけ受け取る方法。
1. ダイアログ.h にメッセージマップ関数を追加定義
// 生成されたメッセージ マップ関数
//{{AFX_MSG(C????Dlg)
    virtual BOOL OnInitDialog( );
    afx_msg void OnPaint( );
    afx_msg HCURSOR OnQueryDragIcon( );
    afx_msg void OnDropFiles( HDROP hDropInfo ); // ★ ドロップハンドラ追加
    virtual void OnOK( );
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP( )
2. ダイアログ.cpp にメッセージマップ関数を追加
BEGIN_MESSAGE_MAP(C????Dlg, CDialog)
    //{{AFX_MSG_MAP(C????Dlg)
    ON_WM_PAINT( )
    ON_WM_QUERYDRAGICON( )
    ON_WM_DROPFILES( ) // ★ ドロップハンドラ追加
    //}}AFX_MSG_MAP
END_MESSAGE_MAP( )
3. 同じくダイアログ.cpp の OnInitDialog( ) にドロップイベントを許可する旨を追加
// TODO: 特別な初期化を行う時はこの場所に追加してください。
DragAcceptFiles( );    // ★ ファイルドロップを許可
 4. ドロップイベントハンドラを追加
★// ファイルドロップイベントハンドラ
★void C????Dlg::OnDropFiles( HDROP hDropInfo )
★{
★    long files;
★    char filename[256];
★
★    /* ドロップされたファイル数を取得 */
★    files = DragQueryFile( hDropInfo, 0xFFFFFFFF, filename, 256 );
★
★    /* ドロップされた一番目のファイル名文字列を取得 */
★    DragQueryFile( hDropInfo, 0, filename, 256 );
★
★    CEdit *pEdit = (CEdit*)GetDlgItem( IDC_???? );
★    pEdit->SetWindowText( filename );
★
★    // CWnd::OnDropFiles( hDropInfo );
★}
 
上記の例ではエディットボックスに取得したファイル名をセットしています。
 1. クラスウィザードで MainFrame に OnDropFiles イベントを追加する。
 2. OnDropFiles イベントハンドラを以下のように修正。
    void CMainFrame::OnDropFiles(HDROP hDropInfo) 
    {
        // TODO: この位置にメッセージ ハンドラ用のコードを追加するか
        // またはデフォルトの処理を呼び出してください
★        CM3UView *pView;
★        pView = (CM3UView *)GetActiveView( ); // これが勝利の鍵だ!!
★        pView->OnDropFiles( hDropInfo );
★
★        DragFinish( hDropInfo );
★        // CFrameWnd::OnDropFiles(hDropInfo);
    }
 デフォルトのイベントハンドラをコメントアウトして、
 ビューの OnDropFiles を呼び出すようにする。
 3. MainFrm.cpp にドキュメントクラスとビュークラスの .h ファイルを #include する。
 4. クラスウィザードでビューに  OnDropFiles イベントを追加する。
 5. ビューの OnDropFiles イベントハンドラを以下のように修正。
    void CM3UView::OnDropFiles(HDROP hDropInfo) 
    {
        // TODO: この位置にメッセージ ハンドラ用のコードを追加するか
        // またはデフォルトの処理を呼び出してください
★        long files;
★        char filename[256];
★
★        /* ドロップされたファイル数を取得 */
★        files = DragQueryFile( hDropInfo, 0xFFFFFFFF, filename, 256 );
★
★        /* ドロップされた一番目のファイル名文字列を取得 */
★        DragQueryFile( hDropInfo, 0, filename, 256 );
★
★        CEdit *pEdit = &GetEditCtrl( );
★        CString string;
★        pEdit->GetWindowText( string );
★        if( string.GetLength( ) > 0 ) string += "\r\n";
★        string += filename;
★        pEdit->SetWindowText( string );
★
★    //    CEditView::OnDropFiles(hDropInfo);
    }
 
この例では CEditView に取得したファイル名をどんどん追加していってます。
ドロップするファイルは1つとは限らないので、ファイル数の分だけループする
と良いでしょう。
 
エクスプローラなどでは一つのウインドウが左と右に別れていますが、
これがウインドウのスプリットです。
PowerWaveも上の方には波形全体を表示するウインドウ、
下の方には拡大できるエディットウインドウを作成しようと思っています。
1. AppWizardでMDIを選択しプロジェクトを作成する。
2. 途中ステップ6/6で、ビュークラスを適当な名前に変更する。
   (プロジェクト名と同じ名前のビューだとよくわからなくなるので)
3. ClassWizardで新規ビュークラスを作成する。
4. ChildFrm.h のCChildFrameに
    public:
        CSplitterWnd m_wndSplitter;
   を追加する。
5. ClassWizardで ChildFrame::OnCreateClient のイベントハンドラを実装する。
6. そのハンドラに以下のコードを追加する。
    if( !m_wndSplitter.CreateStatic( this, 2, 1 ) ||
        !m_wndSplitter.CreateView( 0, 0, RUNTIME_CLASS( CWholeView ),
            CSize( 100, 100 ), pContext ) ||
        !m_wndSplitter.CreateView( 1, 0, RUNTIME_CLASS( CPartialView ),
            CSize( 0, 0 ), pContext ) ){
        MessageBox( "スプリットウインドウの作成に失敗しました", NULL, NULL );
        return( FALSE );
    }
   上の例ではCWholeViewとCPartialViewがスプリットされたウインドウの
   2つのビュークラスです。
7. ChildFrm.cppにビュークラスのヘッダファイルを#includeする。
 
こんな感じです。関数の詳しい情報などはヘルプを読めばわかると思います。
 
マウスを置いてしばらくすると、漫画のセリフのような小さなヘルプがでるヤツが
ツールヒントです。これを出すのは凄く簡単!!
挿入→コンポーネントでツールヒントを選択し、導入して下さい。
自動的にコードにインプリメントされます。
やらなくちゃいけないのは、どのオブジェクトでどの文字列を出すかという設定だけ。
以下のようなコードが自動的に挿入されているはずなので、適切に設定します。
// TODO: コントロールの追加には以下のどちらかの形式を使用してください:
// m_tooltip.AddTool(GetDlgItem(IDC_), );
// m_tooltip.AddTool(GetDlgItem(IDC_), "");
// 実際の設定例
m_tooltip.AddTool(GetDlgItem(IDC_EDIT), IDS_TIP_EDIT );
     
これこそストリングテーブルで用意した方が後々らくですね。;-)
protected:
    CToolTipCtrl m_tooltip;
 
次にダイアログの OnInitDialog( ) で以下のコードを追加します。
m_tooltip.Create( this );
m_tooltip.Activate( TRUE );
// 以下のどちらかの形式でヒントを表示したいダイアログアイテムに
// 表示するテキストを割り当てます
// m_tooltip.AddTool( GetDlgItem(IDC_),  );
// m_tooltip.AddTool( GetDlgItem(IDC_), "");
     
さらにダイアログに PreTranslateMessage ハンドラをクラスウィザードで実装し以下のように
コーディングします。
BOOL C????Dlg::PreTranslateMessage(MSG* pMsg)
{
    // ツールヒント処理
    m_tooltip.RelayEvent(pMsg);
    return CDialog::PreTranslateMessage(pMsg);
}
 
以上でツールヒントの実装は終了です。
これ が
複数行対応のCToolTipsCtrlです。色とフォントも変えられます。
CToolTipCtrlと差し替えてそのまま使用できます。
 
スピンボタンのコントロールと同じように、クラスウィザードでオブジェクトへの
コントロール変数を割り当てます。CStaticだったりCButtonだったりCEditだったり
しますが、これらは全てCWndの派生クラスです。つまりメンバ関数である
GetWindowTextでそのオブジェクトのキャプションを取得でき、SetWindowTextで
キャプションを設定(変更)することが可能です。
これでCEditの場合はエディットボックスの中身を参照・変更できます。
 
管理上、メッセージなんかはストリングテーブルで
リソースとして一元管理したいところ。
リソースエディタのツリーにて右クリックし、挿入、String Tableを選択すると
ストリングテーブルを作成することができます。
このテーブルにIDS_からはじまるIDとそれに定義するメッセージを追加し、
ソース中でこの文字列を使いたくなったら、以下のようにしてロードして使用します。
CString str;            // ロード用CString変数が必要
str.LoadString( IDS_MESSAGE );    // リソースから文字列をロード
pDC->TextOut( 0, 0, str );        // などとして描画
 
これで多言語化もバッチリ?! (^^
 
ウインドウに何らかの描画を行う場合デバイスコンテキストを取得しますがここでまとめておきます。
// ピクチャーコントロールのアドレスを取得
CWnd *pWnd = GetDlgItem( IDC_PICTURE );
// 描画領域座標を取得
CRect rect;
pWnd->GetClientRect( rect );
// ピクチャーコントロールのデバイスコンテキストを取得
CDC *pDC = pWnd->GetDC( );
// 塗る
pDC->FillSolidRect( rect, RGB(0,255,0) );
// フレームの再描画(必要であれば)
pWnd->Invalidate( );
// デバイスコンテキストの開放を忘れないこと
pWnd->ReleaseDC( pDC );
// ピクチャーコントロールのアドレスを取得
CWnd *pWnd = GetDlgItem( IDC_PICTURE );
// ピクチャーコントロール用のデバイスコンテキストを生成
CClientDC dc( pWnd );
// 描画領域座標を取得
CRect rect;
pWnd->GetClientRect( rect );
// 塗る
dc.FillSolidRect( rect, RGB(0,255,0) );
// フレームの再描画(必要であれば)
pWnd->Invalidate( );
 
後者では自分で生成したデバイスコンテキストでピクチャーコントロールのコンテキストを
コンストラクタ内部で取得していますが、変数が破棄される際にデストラクタが解放を
自動的に行っているのでアプリケーションで明示的に解放する必要はありません。
// デバイスコンテキストの保存
int SavedDC = pDC->SaveDC( );
// 背景モードを透明に設定
pDC->SetBkMode( TRANSPARENT );
// テキスト色を設定
pDC->SetTextColor( RGB( 0, 0, 0 ) );
    // 塗り塗り書き書き
// デバイスコンテキストの復帰
pDC->RestoreDC( SavedDC );
 
どうやらシステムに保存するスタックがあるようです。
CClientDC DC(this);
// 背景の塗りつぶし
CRect Rect;
this->GetClientRect( Rect );
DC.FillSolidRect( Rect, RGB(0,0,0) );
// 枠の描画
DC.FrameRect( CRect(100,100,200,200), &CBrush( RGB(255,0,0) ) );
// 文字の描画
DC.SetBkMode( TRANSPARENT );
DC.SetTextColor( RGB(255,255,255) );
DC.TextOut( 0, 0, "ふがふが" );
 
 
ある決められた領域を塗りたい場合、その領域の座標x,y,w,hを#defineしておいて
// ピクチャーコントロールのアドレスを取得
CWnd *pWnd = GetDlgItem( IDC_PICTURE );
// 描画領域座標を取得
CRect rect;
pWnd->GetClientRect( rect );
// ピクチャーコントロールのデバイスコンテキストを取得
CDC *pDC = pWnd->GetDC( );
// 塗る
pDC->FillSolidRect( rect, RGB(0,255,0) );
// フレームの再描画(必要であれば)
pWnd->Invalidate( );
// デバイスコンテキストの開放を忘れないこと
pWnd->ReleaseDC( pDC );
 
レイアウトが気に入らなくなったらピクチャーコントロールの位置や大きさを
リソースエディタで変更するだけでいいのでとっても楽チンですね。
CRect Rect;
GetDlgItem( IDC_PICTURE )->GetWindowRect( Rect );
this->ScreenToClient( &Rect );
CWnd Window;
Window.Create( NULL, NULL, WS_VISIBLE, Rect, this, IDC_PICTURE );
Window.ModifyStyleEx( NULL, WS_EX_STATICEDGE, SWP_DRAWFRAME );
 
 
ウインドウのタイトルバーにアイコンを追加・変更する場合は以下のようにします。
    // アイコン変更
    HICON hIcon = AfxGetApp()->LoadIcon( アイコンのリソースID );
    this->SetIcon( hIcon, FALSE );
 
ボタンにアイコンを貼り付けるにも SetIcon( ) が使えますが,その前にひと手間あります。
    ((CButton *)GetDlgItem(IDC_BUTTON))->SetIcon(AfxGetApp( )->LoadIcon(IDI_ICON));
 
 
マウスカーソルの座標を取得および設定するには以下のようにします。
POINT Pos;
GetCursorPos( &Pos ); // 取得
SetCursorPos( Pos.x, Pos.y ); // 設定
 
マウスカーソルが動いたときは、WM_MOUSEMOVE メッセージハンドラで受けられますが
タイトルバーなど非クライアント領域でのイベントは得られません。※VC++4.0以降の環境では選択できるかも。
VC++.NET 2003ではリソースプロパティのメッセージ一覧から選択できました。 
「ドロップしたファイルの名前を受け取る」 で OnDropFiles( ) を
実装した方法と同じです。ヘッダファイルとソースファイルにメッセージマップとハンドラの登録
を行います。
ヘッダファイル.h に
    afx_msg void OnNcMouseMove( UINT nHitTest, CPoint point );
を追加
ソースファイル.cpp にメッセージマップ
    ON_WM_NCMOUSEMOVE( )
とハンドラ
    void C????Dlg::OnNcMouseMove( UINT nHitTest, CPoint point )
    {
    
    }
を追加
 
なおウインドウ外のマウスイベントを取得するには SetCapture( ) すると得られます。
ただし受けるウインドウがアクティブである必要があります。
// マウスカーソルを砂時計にする。
SetCapture( );
SetCursor( AfxGetApp( )->LoadStandardCursor( IDC_WAIT ) );
// マウスカーソルを元に戻す
ReleaseCapture( );
 
砂時計などの標準のカーソルは上記のように LoadStandardCursor( ) でロードしますが、
自分で作ったカーソルを表示するにはその代わりに LoadCursor( IDC_MYCURSOR ) を使い、
リソースエディタで作成したカーソルのIDを指定します。
 
ラジオボタンは複数の選択肢から一つを選択するためのボタン群です。
((CButton *)GetDlgItem( IDC_RADIO_???? ))->SetCheck( 1 );
 
などとしてチェックマークを付けることができます。
 
リソースエディタでエディットボックスとスピンボタンを関連づけて作成するには
それぞれのコントロールを配置した後、レイアウトメニューのタブオーダーでスピンボタンの直前に
エディットボックスがナンバリングされるようにします。タブオーダーの設定の仕方は、
ダブルクリックして1番をセットし、あとは順番にシングルクリックしてセットしていきます。
またスピンボタンのプロパティでスタイルの自動関連付けと数値の自動表示を設定しておきます。
こうしてデフォルトでは0から100までしか変更できない、しかも増減が逆な感のあるヘンテコなものが
できあがります。
CSpinButtonCtrl m_SpinButton;        // クラスウィザードが自動で*.hに追加
    …
m_SpinButton.SetRange( 0, 255 );    // OnInitDialog( )などで最小・最大を設定
 
とすると、期待通りの動作になります。
 
例えばエディットボックス内の数値が変更されたら、画面も更新したいと思って
エディットボックスのEN_UPDATEハンドラで画面更新処理Invalidate(FALSE)等を
呼んだりすると、エディットボックスでのカーソルの動きが変になります。
カーソルが進んだと思っても書き直しでまた先頭に戻ってしまう。
// エディットボックス更新ハンドラ
void C????Dlg::OnChangeEditBox( ) 
{
    // 更新前の文字列
    static CString StringOld;
    // 更新後の文字列を取得
    CString StringNew;          
    m_EditBox.GetWindowText( StringNew );
    // 自動補完する条件に当てはまった場合
    if( StringNew.GetLength( ) == 2 && StringOld.GetLength( ) == 1 ){
        // 補完して更新
        StringNew += ":";
        m_EditBox.SetWindowText( StringNew );
        // 更新後のカレットを末尾に移動
        m_EditBox.SendMessage( WM_KEYDOWN, (WPARAM)VK_END, (LPARAM)0 );
    }
    // 更新前の文字列を更新
    StringOld = StringNew;
}
 
CDC dc_root;
dc_root.CreateDC( "DISPLAY", NULL, NULL, NULL );
 
これで取得したdc_rootは他の時と同じようにCDCのメンバ関数を使うことが可能です。
テキスト出力を行うと壁紙の部分にテキストが描画されます(笑)。
// pDC デバイスコンテキストへのポインタ
pDC->SetBkMode( TRANSPARENT );        // 背景モードを透明に設定
pDC->SetTextColor( RGB( 255, 0, 0 ) );    // テキスト色を設定
pDC->TextOut( 0, 80, "背景色透明 テキスト色を赤に変えた" );
pDC->SetBkMode( OPAQUE );            // 背景モードを塗に設定
pDC->SetBkColor( RGB( 0, 0, 255 ) );        // 背景色を設定
pDC->SetTextColor( RGB( 255, 255, 255 ) );    // テキスト色を設定
pDC->TextOut( 0, 120, "背景色青 テキスト色を白に変えた" );
 
// フォントサイズをポイントで指定する場合
CFont Font;
Font.CreatePointFont( 90, "MS Pゴシック" );
CFont *pFontOld = pDC->SelectObject( &Font );
pDC->TextOut( 0, 0, "フォントを変えてみた" );
pDC->SelectObject( pFontOld );
 
1ポイントは1/72インチなので,1ポイントが何ピクセル(ドット)に相当するかは以下のように計算できます。
(ピクセル数) = (ポイント数) / 72 * (dpi値)
 
dpi値とは画面のプロパティで設定されている値で1インチ(2.54cm)あたりを何ピクセルで
画面表示するかを表したものです。通常ディスプレイは96dpiとされているので計算してみると
9ポイントのフォントは12ピクセルで表示されます。なおWindowsはテキトーなので
15インチ1024×768のディスプレイでも17インチ1024×768のディスプレイでも
96dpiが使用されているので注意が必要です。
CString String = "サンプル文字列";
CSize Size = pDC->GetTextExtent( String );
// Size.cx に幅,Size.cyに高さが格納されます。
 
なお斜体や太字など,より詳しくフォントの設定をしたい場合は、以下のようにします。
CFont Font;
Font.CreateFont( 48, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
                      SHIFTJIS_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
                      DEFAULT_QUALITY, DEFAULT_PITCH, "MS Pゴシック" );
CFont *pFontOld = pDC->SelectObject( &Font );
pDC->TextOut( 0, 0, "フォントを変えてみた" );
pDC->SelectObject( pFontOld );
 
詳細はヘルプを見てください。
m_Font.CreatePointFont( 240, "MS Pゴシック" );
m_EditCtrl.SetFont( &m_Font );
 
とすればそのエディットボックスは常に変更したフォントが使用されます。
void C????Dlg::OnOK( ) 
{
    // ダイアログが閉じないように小細工
    // Dialog::OnOK( );
}
 
BOOL C????Dlg::PreTranslateMessage(MSG* pMsg) 
{
    // ESCキーを無効にする
    if( pMsg->message == WM_KEYDOWN ){
        if( pMsg->wParam == VK_ESCAPE ) return( TRUE );
    }
    return CDialog::PreTranslateMessage(pMsg);
}
 
// CWndクラスにはメンバ関数としてメッセージボックスを表示する関数が
// あるのでその派生クラスのオブジェクトからは以下のように使えます。
this->MessageBox( "ほげほげ" );
// CWndクラス以外のオブジェクトでは次のような関数が使用できます。
AfxMessageBox( "ふがふが" );
::MessageBox( NULL, "へろへろ", NULL, NULL );
 
ヘルプを見るとアプリケーションは基本的に AfxMessageBox( ) 関数を使えと書いてあります。
AfxMessageBox( ) はメッセージボックスのウインドウタイトル文字列(キャプション)が
自動的にアプリケーション名になります。ただしアプリケーションのコンストラクタ実行中は
まだアプリケーション名(CWinApp.m_pszAppName)がセットされていないようで,デフォルトである
「エラー」という文字列が表示されてしまいます。これを避けるにはAPIである ::MessageBox( )
を使用するしかないようです。
CString str;
int i = 2;
str.Format( "TEST: 1+1=%d", i );
this->m_edit_ctrl.SetWindowText( str ); // エディットボックスに表示
 
またCString型のデータをNULLで終わる文字列に変換したい場合は、
CString型のデータを(LPCTSTR)でキャストすれば可能である。その逆は直接代入可能。
// CString型のデータを文字列に変換
CString str;
char *buf[256];
sprintf( buf, "%s", (LPCTSTR)str );
// 文字列からCString型に変換
CString str;
str = "TEST";
 
©Yutaka Wada(ana53), AirparkLab ALL RIGHTS RESERVED.
 
 海外旅行