INameSpaceTreeControlCustomDrawの通知を受け取ることに成功しました。指定されるIIDはこれ以外にもいくつか存在します。アイテムが選択されたことを通知するINameSpaceTreeControlEventsがあります。NameSpaceTreeControlの動作を大きく拡張することができます。今回のプログラムは、INameSpaceTreeControlEventsを実装してコンテキストメニューに関する通知を受け取ります。コンテキストメニューに独自の項目を追加します。TEXT("アイテムが選択されました。lParam);}CNameSpaceTreeHostが実装するメソッドの中で、INameSpaceTreeControlEventsのメソッドはOnItemClickからOnGetDefaultIconIndexまでです。この中でコンテキストメニューに関するメソッドは次の2つです。S_OK;}OnBeforeContextMenuは、コンテキストメニューが表示する前に呼ばれます。ppvはIContextMenu(2)を実装したオブジェクトのアドレスを返すことができます。ppvにNULLを格納してE_NOTIMPLを返しています。OnAfterContextMenuは、OnBeforeContextMenuが呼ばれた後に呼ばれます。ppvに独自のオブジェクトのアドレスを格納してS_OKを返すようにします。OnAfterContextMenuが制御を返すと、実際にコンテキストメニューが表示されます。ppvに指定するオブジェクトはこれらを実装していなければなりません。よって、CContextMenuというクラスを定義し、これを型としてオブジェクトを作成しています。CContextMenuのコンストラクタは次のようになっています。1;pDefContextMenu->QueryInterface(IID_PPV_ARGS(&m_pDefContextMenu2));}第1引数は、既定のコンテキストメニューを表すIContextMenuです。これは、OnAfterContextMenuの第2引数から取得できます。既定のコンテキストメニューを受け取るようにしています。既定のコンテキストメニューがWM_INITMENUPOPUPなどのメッセージを必要としているからです。これを呼び出すことで既定のコンテキストメニューにメッセージを渡すことができます。IShellItemを引数として受け取るようにします。コンテキストメニューが表示される段階になると、IContextMenu::QueryContextMenuが呼ばれます。ここでは、第1引数のメニューハンドルに既定の項目と独自の項目を追加しています。GetMenuItemCount(hmenu));}uFlagsにCMF_DEFAULTONLYが含まれる場合は項目を追加する必要がないので、処理を続行しないようにします。既定のコンテキストメニューのQueryContextMenuを呼び出せば、hmenuには既定の項目が追加されるようになります。ここでどれだけの項目が追加されたかは、GetMenuItemCountを呼び出すことで分かります。ID_ITEMのようにidCmdFirstをベースにしていなければなりません。IDの値はidCmdLastを超えないようにします。これを避ける適切な方法がよく分かりません。既定の項目のIDの値は小さいようなので、できるだけ大きい値を指定すれば安全なのかもしれません。InsertMenuItemの第3引数にTRUEを指定した場合は、第2引数に追加する項目の位置を指定します。今回は、最後尾に追加するということでGetMenuItemCountの戻り値を指定しています。これはGetMenuItemCountの戻り値を指定します。QueryContextMenuでメニューハンドルを初期化した呼び出し側は、実際にコンテキストメニューを表示することになります。項目が選択された場合はInvokeCommandが呼ばれます。TEXT("アイテムが選択されました。S_OK;}pici->lpVerbの上位ワードが0でない場合は、処理を続行する必要はありません。idCmdは選択された項目のIDであり、これがID_ITEMと一致する場合は独自の処理を行います。逆に一致しない場合は、既定の項目ということでm_pDefContextMenu2のInvokeCommandを呼び出します。これにより、既定の処理が実行されることになります。