今回は次のような独自のフォルダビューを作成することにします。独自のフォルダビューの最大の利点は、シェル拡張内で作成したウインドウを表示できることです。フォルダ内のアイテムを自由に表現することができるようになります。アイテムを選択して起こる動作は前節と同様にしています。そのオブジェクトのためのクラスを定義することになります。m_folderSettings;};フォルダビューとして最低限、実装しなければならないのはIShellViewのみです。今回のサンプルでは設計上このインターフェースが必要になります。この他、実装が推奨されるインターフェースとしてはIFolderViewが挙げられます。ShellBrowserにとっては何かと都合が良くなるはずです。XP以降のデフォルトのフォルダビューはIFolderViewを実装しています。CShellView型のオブジェクトはフォルダビューですから、IShellFolder::CreateViewObjectで作成することになります。CShellFolderをIShellFolderでキャストして渡すようにしています。ちなみに、下記のようなコードを記述すると、デフォルトのフォルダビューと独自のフォルダビューを使い分けることができます。hr;}m_levelには現在のフォルダの階層が格納されており、これが0である場合は最上位のフォルダであることを意味しています。それ以下のフォルダでは独自のフォルダビューを表示することを意味しています。CShellViewが継承しているIShellViewは、IOleWindowを継承するようになっています。このため、CShellViewはIOleWindowの2つのメソッドを実装することになります。現在のフォルダビューのウインドウハンドルを返すようにしています。ContextSensitiveHelpは、呼ばれることがないようなのでE_NOTIMPLを返すだけで問題ありません。IShellViewのメソッドを順に見ていきます。E_NOTIMPL;}TranslateAcceleratorは、何らかのキーが入力された際に呼ばれます。今回はアクセラレータテーブルを作成していないため、E_NOTIMPLを返しています。E_NOTIMPL;}EnableModelessは、呼ばれることがないようなのでE_NOTIMPLを返すだけで構いません。S_OK;}UIActivateは、フォルダビューのウインドウの状態が変化した場合に呼ばれます。した処理を行うことになるでしょう。Vistaではこの操作は失敗する模様です。これを行ってもツールバーには何も変化がありません。そのウインドウクラスはToolbarWindow32になっていませんでした。ツールバーの操作が成功することはないように思えます。ステータスバーの操作にはSetStatusTextSBがありますが、これはCoTaskMemAllocで確保したメモリを指定することで成功しました。E_NOTIMPL;}Refreshは、F5キーなどでフォルダビューを更新しようとした際に呼ばれます。今回のサンプルでは特に更新する要素がないため、E_NOTIMPLを返しています。S_OK;}CreateViewWindowは、フォルダビューとして表示すべきウインドウを作成する際に呼ばれます。これは前のフォルダに戻す際に必要となるため、メンバ変数として保存しています。第3引数のIShellBrowserはShellBrowserの機能を使用する場合に必要ですから、これもメンバ変数として保存するようにします。このウインドウの子ウインドウとしてフォルダビューを作成することになるからです。ウインドウスタイルにWS_CHILDを指定し、親ウインドウのハンドルとしてm_hwndParentを指定することになります。ウインドウプロシージャでCShellViewを参照できるようにするためです。CreateChildWindowについては後述します。S_OK;}DestroyViewWindowは、フォルダビューのウインドウが破棄された際に呼ばれます。この時点で、IShellBrowserとIShellFolderへの参照が不要になるため、Releaseを呼び出しています。S_OK;}GetCurrentInfoは、フォルダビューの表示情報を取得する際に呼ばれます。これは具体的には、フォルダの切り替えやフォルダを閉じる場合に相当します。単純に、m_folderSettingsの中身をコピーするだけで構いません。E_NOTIMPL;}AddPropertySheetPagesは、メニューの「ツール」から「フォルダオプション」を選択した際に呼ばれます。表示されるダイアログに独自のプロパティシートが追加されると思われます。今回はそうした実装を行わないということでE_NOTIMPLを返すようにしています。E_NOTIMPL;}SaveViewStateは、フォルダビューの表示情報を保存する機会を与える目的で呼ばれます。これは具体的には、GetCurrentInfoの後に相当します。今回のサンプルでは保存すべき表示情報などが存在しないため、E_NOTIMPLを返しています。E_NOTIMPL;}SelectItemは、呼ばれることがないようなのでE_NOTIMPLを返すだけで構いません。E_NOTIMPL;}GetItemObjectは、ShellBrowserが何らかのインターフェースを要求する場合に呼ばれます。前のフォルダに戻る場合にITargetContainerやITargetFrame、ITravelLogClientが指定されるようです。今回はE_NOTIMPLを返しています。CShellViewはIObjectWithSiteを継承しているため、このインターフェースのメソッドも実装することになります。IObjectWithSiteを継承していればShellBrowserから何らかのオブジェクトのアドレスを受け取れる利点があります。S_OK;}ShellBrowserは、フォルダが新しく表示される段階になるとSetSiteを呼び出します。IServiceProviderを実装していることは間違いないようです。IServiceProvider::QueryServiceを呼び出せば、ShellBrowserを識別するIShellBrowserを取得できることになっています。pUnkSiteは、CUIObject::InvokeCommandを呼び出す場合に必要になるため、メンバとして保存することになります。子ウインドウとしてSysLinkコントロールを持つことにしています。これは、ユーザーがSysLinkコントロールのクリックを通じて、アイテムの内容を実行させるようにするためです。IShellFolder::EnumObjectsを呼び出してIEnumIDListを取得することになります。ウインドウの名前はhtmlタグのフォーマットに準拠している必要があります。ウインドウスタイルにLWS_TRANSPARENTを指定すると、コントロールの背景が透過されることになります。どのような見た目を持ってどのような操作が可能であるかは、WindowProcの実装によって決定します。0;WM_NOTIFYの通知コードがNM_CLICKである場合は、SysLinkコントロールがクリックされたことを意味します。そのアイテムを特定する必要があります。1で表すことができます。iで識別されるアイテムを実行してもよいことになります。QueryContextMenuでポップアップメニューの内容を初期化します。追加される項目はデフォルトの項目だけで構いませんから、第5引数にはCMF_DEFAULTONLYを指定しています。MAKEINTRESOURCEマクロに指定する値は、デフォルトの項目を実行する場合は常に0になります。IObjectWithSite::SetSiteを通じてそれを渡しておくことになります。0;}WM_PAINTでは、アイテムに関連するアイコンを描画することになります。IShellFolder::GetUIObjectOfでIExtractIconを取得することになります。アイコンの描画はDrawIconで行うことができます。このためには予めウインドウクラスが登録されていなければなりません。ウインドウクラスの登録と解除は、RegisterWindowClassによって行われます。bResult;}RegisterWindowClassを呼び出すのは、DLLのエントリポイントであるDllMainです。DLL_PROCESS_DETACHのときに第2引数をFALSEにして呼び出します。wc.lpfnWndProcに指定しているWindowProcはクラスのメソッドではなく、通常のコールバック関数です。このコールバック関数からCShellView::WindowProcを呼び出すには次のような処理が必要になります。lParam);}CShellView::CreateViewWindowでは、SetWindowLongPtrを通じてウインドウにthisポインタを関連付けていました。よって、GetWindowLongPtrを呼び出せば、CShellViewへのアドレスを取得することができます。クラスのメソッドで処理することができません。その際にはDefWindowProcを呼び出すようにしています。IShellBrowser::InsertMenusSBを呼び出すことになります。この下に独自の項目を追加することになります。S_OK;}メニューの設定は、CShellView::UIActivateで行うことになります。その後にInsertMenusSBに空のメニューのハンドルを指定します。第2引数のOLEMENUGROUPWIDTHS構造体については、0で初期化しておくだけで構いません。ポップアップメニューのハンドルを取得するようにしています。これには、MENUITEMINFO構造体のfMaskにMIIM_SUBMENUを指定してGetMenuItemInfoを呼び出すことになります。FCIDM_MENU_FILEにすれば「ファイル」が対象になります。ポップアップメニューのハンドルを取得したら、独自の項目を追加するためにInsertMenuItemを呼び出します。項目のIDは、FCIDM_SHVIEWFIRSTからFCIDM_SHVIEWLASTの間でなければならないことに注意してください。メニューの内容が完成したら、IShellBrowser::SetMenuSBを呼び出すことでメニューを設定します。SetMenuSBにNULLを指定してメニューの設定を解除することになります。最後にDestroyMenuでメニュー自体を破棄します。独自に追加した項目が選択された場合は、WindowProcにWM_COMMANDが送られることになります。どの項目が選択されたかは、wParamの下位ワードから判断することができます。0;前のフォルダに戻るための処理は、BrowseObjectの第2引数にSBSP_NAVIGATEBACKを指定するだけです。特定のフォルダに切り替えるわけではないので、第1引数にフォルダのPIDLを指定する必要はありません。