テクニカル ノート 3: Windows ハンドルとオブジェクト間のマップ
更新 : 2007 年 11 月
ここでは、Windows オブジェクト ハンドルと C++ オブジェクト間のマップをサポートする MFC ルーチンについて説明します。
問題
Windows オブジェクトは、通常、さまざまな HANDLE オブジェクトによって表されます。MFC クラスは、Windows オブジェクト ハンドルを C++ オブジェクトでラップします。MFC クラス ライブラリのハンドル ラッパー関数を使用すると、特定のハンドルを持つ Windows オブジェクトをラップする C++ オブジェクトを取得できます。ただし、C++ ラップ オブジェクトのない Windows オブジェクトもあります。このような場合は、C++ ラップ オブジェクトとして動作する一時オブジェクトがシステムによって作成されます。
ハンドル マップを使用する Windows オブジェクトを次に示します。
HWND (CWnd クラスおよび CWnd からの派生クラス)
HDC (CDC クラスおよび CDC からの派生クラス)
HMENU (CMenu)
HPEN (CGdiObject)
HBRUSH (CGdiObject)
HFONT (CGdiObject)
HBITMAP (CGdiObject)
HPALETTE (CGdiObject)
HRGN (CGdiObject)
HIMAGELIST (CImageList)
SOCKET (CSocket)
これらのオブジェクトの 1 つへのハンドルを指定して、静的メソッド FromHandle を呼び出すと、そのハンドルをラップする MFC オブジェクトを取得できます。たとえば、hWnd という HWND がある場合、次の行は hWnd をラップする CWnd へのポインタを返します。
CWnd::FromHandle(hWnd)
hWnd に対応する特定のラッパー オブジェクトがない場合は、hWnd をラップする一時的な CWnd が作成されます。このように、どのハンドルからでも有効な C++ オブジェクトを取得できます。
ラップ オブジェクトを取得した後は、そのハンドルをラッパー クラスのパブリック メンバ変数から取得できます。CWnd の場合は、m_hWnd にこのオブジェクトの HWND が保持されています。
MFC オブジェクトにハンドルを対応付ける
新しく作成したハンドル ラッパー オブジェクトと Windows オブジェクトへのハンドルがあるとき、次の例のように、Attach 関数を呼び出して 2 つを対応付けることができます。
CWnd myWnd;
myWnd.Attach(hWnd);
これにより、myWnd と hWnd を対応付けるエントリがパーマネント マップに登録されます。ここで CWnd::FromHandle(hWnd) を呼び出すと、myWnd へのポインタが返されます。myWnd が削除されるときには、デストラクタが Windows 関数 DestroyWindow を呼び出して自動的に hWnd を破棄します。この動作が望ましくない場合は、myWnd が破棄される前 (通常は myWnd オブジェクトを定義したスコープから離れるとき) に、hWnd を myWnd から切り離す必要があります。これは Detach メソッドで行います。
myWnd.Detach();
一時オブジェクトについて
ラップ オブジェクトを持たないハンドルを FromHandle に渡すと、そのつど一時オブジェクトが作成されます。これらの一時オブジェクトは DeleteTempMap 関数によってハンドルから切り離され、削除されます。既定では、CWinThread::OnIdle は一時ハンドル マップをサポートする各クラスに対して自動的に DeleteTempMap を呼び出します。つまり、一時オブジェクトのポインタを取得した関数の終了以降は、そのポインタが有効であると見なすことはできません。
ラップ オブジェクトとマルチスレッド
一時オブジェクトと永続オブジェクトはスレッド単位で保持されます。つまり、オブジェクトが一時的か永続的かにかかわらず、1 つのスレッドから別のスレッドの C++ ラップ オブジェクトにアクセスすることはできません。
一時オブジェクトを別のスレッドに渡すときは、必ずネイティブな HANDLE 型として渡します。C++ のラップ オブジェクトを別のスレッドに渡すと、予期しない結果になることが少なくありません。