EternalWindows
シェル拡張 / 仮想フォルダサンプル(フォルダビュー)
サンプルはこちら

これまでのサンプルでは、SHCreateShellFolderViewでデフォルトのフォルダビューを作成しましたが、 今回は次のような独自のフォルダビューを作成することにします。

独自のフォルダビューの最大の利点は、シェル拡張内で作成したウインドウを表示できることです。 このウインドウには、コントロールを配置することもイメージを描画することもできますから、 フォルダ内のアイテムを自由に表現することができるようになります。 アイテムを選択して起こる動作は前節と同様にしています。

独自のフォルダビューを作成するというのは、IShellViewを継承したオブジェクトを作成するということですから、 そのオブジェクトのためのクラスを定義することになります。

class CShellView : public IShellView, public IObjectWithSite
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP GetWindow(HWND *phwnd);
	STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
	
	STDMETHODIMP TranslateAccelerator(LPMSG lpmsg);
	STDMETHODIMP EnableModeless(BOOL fEnable);
	STDMETHODIMP UIActivate(UINT uState);
	STDMETHODIMP Refresh(VOID);
	STDMETHODIMP CreateViewWindow(IShellView *psvPrevious, LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd);
	STDMETHODIMP DestroyViewWindow(VOID);
	STDMETHODIMP GetCurrentInfo(LPFOLDERSETTINGS lpfs);
	STDMETHODIMP AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam);
	STDMETHODIMP SaveViewState(VOID);
	STDMETHODIMP SelectItem(PCUITEMID_CHILD pidlItem, UINT uFlags);
	STDMETHODIMP GetItemObject(UINT uItem, REFIID riid, LPVOID *ppv);

	STDMETHODIMP SetSite(IUnknown *pUnkSite);
	STDMETHODIMP GetSite(REFIID riid, void **ppvSite);	

	CShellView(IShellFolder *pShellFolder);
	~CShellView();
	void CreateChildWindow();
	LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

private:
	LONG           m_cRef;
	HWND           m_hwndView;
	HWND           m_hwndParent;
	IShellBrowser  *m_pShellBrowser;
	IShellFolder   *m_pShellFolder;
	IUnknown       *m_pUnkSite;
	FOLDERSETTINGS m_folderSettings;
};

フォルダビューとして最低限、実装しなければならないのはIShellViewのみです。 よって、IObjectWithSiteの実装は必須ではないのですが、 今回のサンプルでは設計上このインターフェースが必要になります。 この他、実装が推奨されるインターフェースとしてはIFolderViewが挙げられます。 IFolderViewを実装すれば、GetFolderを通じてフォルダビューに関連するIShellFolderを返すことができるため、 ShellBrowserにとっては何かと都合が良くなるはずです。 ちなみに、Windows XP以降のデフォルトのフォルダビューはIFolderViewを実装しています。

CShellView型のオブジェクトはフォルダビューですから、IShellFolder::CreateViewObjectで作成することになります。

STDMETHODIMP CShellFolder::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
{
	HRESULT hr = E_NOINTERFACE;
	
	if (IsEqualIID(riid, IID_IShellView)) {
		CShellView *p;

		p = new CShellView(static_cast<IShellFolder *>(this));
		hr = p->QueryInterface(riid, ppv);
		p->Release();
	}

	return hr;
}

CShellViewは内部でIShellFolderのメソッドを呼び出すことになるため、 CShellFolderをIShellFolderでキャストして渡すようにしています。 ちなみに、下記のようなコードを記述すると、デフォルトのフォルダビューと独自のフォルダビューを使い分けることができます。

STDMETHODIMP CShellFolder::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
{
	HRESULT hr = E_NOINTERFACE;
	
	if (IsEqualIID(riid, IID_IShellView)) {
		if (m_level == 0) {
			SFV_CREATE sfvCreate;

			sfvCreate.cbSize   = sizeof(SFV_CREATE);
			sfvCreate.pshf     = static_cast<IShellFolder *>(this);
			sfvCreate.psvOuter = NULL;
			sfvCreate.psfvcb   = NULL;
			
			hr = SHCreateShellFolderView(&sfvCreate, (IShellView **)ppv);
		}
		else {
			CShellView *p;

			p = new CShellView(static_cast<IShellFolder *>(this));
			hr = p->QueryInterface(riid, ppv);
			p->Release();
		}
	}

	return hr;
}

m_levelには現在のフォルダの階層が格納されており、これが0である場合は最上位のフォルダであることを意味しています。 つまり、上記コードは、最上位のフォルダのみデフォルトのフォルダビューを表示し、 それ以下のフォルダでは独自のフォルダビューを表示することを意味しています。

CShellViewが継承しているIShellViewは、IOleWindowを継承するようになっています。 このため、CShellViewはIOleWindowの2つのメソッドを実装することになります。

STDMETHODIMP CShellView::GetWindow(HWND *phwnd)
{
	*phwnd = m_hwndView;

	return S_OK;
}

STDMETHODIMP CShellView::ContextSensitiveHelp(BOOL fEnterMode)
{
	return E_NOTIMPL;
}

GetWindowではウインドウハンドルを返すことになっているため、 現在のフォルダビューのウインドウハンドルを返すようにしています。 ContextSensitiveHelpは、呼ばれることがないようなのでE_NOTIMPLを返すだけで問題ありません。

IShellViewのメソッドを順に見ていきます。

STDMETHODIMP CShellView::TranslateAccelerator(LPMSG lpmsg)
{
	return E_NOTIMPL;
}

TranslateAcceleratorは、何らかのキーが入力された際に呼ばれます。 フォルダビューがアクセラレータテーブルを持つ場合は、これと入力されたキーが一致するかを調べることになりますが、 今回はアクセラレータテーブルを作成していないため、E_NOTIMPLを返しています。

STDMETHODIMP CShellView::EnableModeless(BOOL fEnable)
{
	return E_NOTIMPL;
}

EnableModelessは、呼ばれることがないようなのでE_NOTIMPLを返すだけで構いません。

STDMETHODIMP CShellView::UIActivate(UINT uState)
{
	return S_OK;
}

UIActivateは、フォルダビューのウインドウの状態が変化した場合に呼ばれます。 今回は特に行うことがないためS_OKを返しているだけですが、 ShellBrowserのメニューに独自の項目を追加したい場合は、 そうした処理を行うことになるでしょう。 また、ツールバーへのボタンの追加もこのメソッドで行うべきとされていますが、 どうやらWindows Vistaではこの操作は失敗する模様です。 たとえば、ツールバーの操作にはIShellBrowserのSendControlMsgやSetToolbarItemsを呼び出すことになっていますが、 これを行ってもツールバーには何も変化がありません。 これを不思議に思ってGetControlWindowでツールバーのウインドウハンドルを取得してみたところ、 そのウインドウクラスはToolbarWindow32になっていませんでした。 つまり、対象となるウインドウ自体がツールバーではなくなっているため、 ツールバーの操作が成功することはないように思えます。 ステータスバーの操作にはSetStatusTextSBがありますが、これはCoTaskMemAllocで確保したメモリを指定することで成功しました。

STDMETHODIMP CShellView::Refresh(VOID)
{
	return E_NOTIMPL;
}

Refreshは、F5キーなどでフォルダビューを更新しようとした際に呼ばれます。 今回のサンプルでは特に更新する要素がないため、E_NOTIMPLを返しています。

STDMETHODIMP CShellView::CreateViewWindow(IShellView *psvPrevious, LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prcView, HWND *phWnd)
{
	m_folderSettings = *pfs;
	
	m_pShellBrowser = psb;
	m_pShellBrowser->AddRef();

	m_pShellBrowser->GetWindow(&m_hwndParent);

	m_hwndView = CreateWindowEx(0, g_szClassName, NULL, WS_CHILD | WS_VISIBLE, prcView->left, prcView->top, prcView->right, prcView->bottom, m_hwndParent, (HMENU)ID_WINDOW, g_hinstDll, NULL);
	CreateChildWindow();
	SetWindowLongPtr(m_hwndView, GWLP_USERDATA, (LONG_PTR)this);
	*phWnd = m_hwndView;

	return S_OK;
}

CreateViewWindowは、フォルダビューとして表示すべきウインドウを作成する際に呼ばれます。 第2引数は、前に表示されていたフォルダビューのウインドウの表示情報が格納されており、 これは前のフォルダに戻す際に必要となるため、メンバ変数として保存しています。 第3引数のIShellBrowserはShellBrowserの機能を使用する場合に必要ですから、これもメンバ変数として保存するようにします。 IShellBrowser::GetWindowでShellBrowserのウインドウハンドルを取得しているのは、 このウインドウの子ウインドウとしてフォルダビューを作成することになるからです。 つまり、フォルダビューのウインドウをCreateWindowExで作成する場合は、 ウインドウスタイルにWS_CHILDを指定し、親ウインドウのハンドルとしてm_hwndParentを指定することになります。 SetWindowLongPtrを呼び出してthisポインタをウインドウに関連付けているのは、 ウインドウプロシージャでCShellViewを参照できるようにするためです。 CreateChildWindowについては後述します。

STDMETHODIMP CShellView::DestroyViewWindow(VOID)
{
	m_pShellBrowser->Release();
	m_pShellFolder->Release();

	return S_OK;
}

DestroyViewWindowは、フォルダビューのウインドウが破棄された際に呼ばれます。 この時点で、IShellBrowserとIShellFolderへの参照が不要になるため、Releaseを呼び出しています。

STDMETHODIMP CShellView::GetCurrentInfo(LPFOLDERSETTINGS lpfs)
{
	*lpfs = m_folderSettings;

	return S_OK;
}

GetCurrentInfoは、フォルダビューの表示情報を取得する際に呼ばれます。 これは具体的には、フォルダの切り替えやフォルダを閉じる場合に相当します。 単純に、m_folderSettingsの中身をコピーするだけで構いません。

STDMETHODIMP CShellView::AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam)
{
	return E_NOTIMPL;
}

AddPropertySheetPagesは、メニューの「ツール」から「フォルダオプション」を選択した際に呼ばれます。 ここで独自のプロパティシートを第2引数に指定した場合は、 表示されるダイアログに独自のプロパティシートが追加されると思われます。 今回はそうした実装を行わないということでE_NOTIMPLを返すようにしています。

STDMETHODIMP CShellView::SaveViewState(VOID)
{
	return E_NOTIMPL;
}

SaveViewStateは、フォルダビューの表示情報を保存する機会を与える目的で呼ばれます。 これは具体的には、GetCurrentInfoの後に相当します。 今回のサンプルでは保存すべき表示情報などが存在しないため、E_NOTIMPLを返しています。

STDMETHODIMP CShellView::SelectItem(PCUITEMID_CHILD pidlItem, UINT uFlags)
{
	return E_NOTIMPL;
}

SelectItemは、呼ばれることがないようなのでE_NOTIMPLを返すだけで構いません。

STDMETHODIMP CShellView::GetItemObject(UINT uItem, REFIID riid, LPVOID *ppv)
{
	return E_NOTIMPL;
}

GetItemObjectは、ShellBrowserが何らかのインターフェースを要求する場合に呼ばれます。 具体的には、新しいフォルダに切り替わった場合にIDispatchやIPersistが指定され、 前のフォルダに戻る場合にITargetContainerやITargetFrame、ITravelLogClientが指定されるようです。 こうしたインターフェースを実装したオブジェクトを返すことは必須ではないため、 今回はE_NOTIMPLを返しています。

CShellViewはIObjectWithSiteを継承しているため、このインターフェースのメソッドも実装することになります。 IObjectWithSiteを継承していればShellBrowserから何らかのオブジェクトのアドレスを受け取れる利点があります。

STDMETHODIMP CShellView::GetSite(REFIID riid, void **ppvSite)
{
	if (m_pUnkSite == NULL)
		return E_NOINTERFACE;

	return m_pUnkSite->QueryInterface(riid, ppvSite);
}

STDMETHODIMP CShellView::SetSite(IUnknown *pUnkSite)
{
	m_pUnkSite = pUnkSite;

	return S_OK;
}

ShellBrowserは、フォルダが新しく表示される段階になるとSetSiteを呼び出します。 インターフェースの型がIUnknownであることから、オブジェクトがどのようなインターフェースを実装しているかは一目で分かりませんが、 IServiceProviderを実装していることは間違いないようです。 そして、IServiceProvider::QueryServiceを呼び出せば、ShellBrowserを識別するIShellBrowserを取得できることになっています。 pUnkSiteは、CUIObject::InvokeCommandを呼び出す場合に必要になるため、メンバとして保存することになります。

CShellView::CreateViewWindowで作成したウインドウは、 子ウインドウとしてSysLinkコントロールを持つことにしています。 これは、ユーザーがSysLinkコントロールのクリックを通じて、アイテムの内容を実行させるようにするためです。

void CShellView::CreateChildWindow()
{
	TCHAR         szWindowName[256];
	LPITEMDATA    lpData;
	IEnumIDList   *pEnumIDList;
	PITEMID_CHILD pidl;
	int           i = 0;

	m_pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList);

	while (pEnumIDList->Next(1, &pidl, NULL) == S_OK) {
		lpData = (LPITEMDATA)pidl;
		wsprintf(szWindowName, TEXT("%s"), lpData->szName);
		CreateWindowEx(0, WC_LINK, szWindowName, WS_CHILD | WS_VISIBLE | LWS_TRANSPARENT, 50, 20 + (i * 40), 120, 35, m_hwndView, (HMENU)(i + 1), g_hinstDll, NULL);
		CoTaskMemFree(pidl);
		i++;
	}

	pEnumIDList->Release();
}

SysLinkコントロールはアイテムの数だけ列挙する必要があるため、 IShellFolder::EnumObjectsを呼び出してIEnumIDListを取得することになります。 SysLinkコントロールのウインドウクラスはWC_LINKであり、 ウインドウの名前はhtmlタグのフォーマットに準拠している必要があります。 ウインドウスタイルにLWS_TRANSPARENTを指定すると、コントロールの背景が透過されることになります。

CShellView::CreateViewWindowで作成したウインドウが、 どのような見た目を持ってどのような操作が可能であるかは、WindowProcの実装によって決定します。

case WM_NOTIFY:
	if (((LPNMHDR)lParam)->code == NM_CLICK) {
		IEnumIDList   *pEnumIDList;
		PITEMID_CHILD pidl;
		int           i = 0;
		
		m_pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList);
		
		while (pEnumIDList->Next(1, &pidl, NULL) == S_OK) {
			if (((LPNMHDR)lParam)->idFrom == i + 1) {
				HMENU               hmenuPopup;
				IContextMenu        *pContextMenu;
				IObjectWithSite     *pObjectWithSite;
				CMINVOKECOMMANDINFO ici;

				m_pShellFolder->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_IContextMenu, NULL, (void **)&pContextMenu);

				pContextMenu->QueryInterface(IID_PPV_ARGS(&pObjectWithSite));
				pObjectWithSite->SetSite(m_pUnkSite);
				pObjectWithSite->Release();
				
				hmenuPopup = CreatePopupMenu();
				pContextMenu->QueryContextMenu(hmenuPopup, 0, 1, 0x7fff, CMF_DEFAULTONLY);

				ici.cbSize = sizeof(CMINVOKECOMMANDINFO);
				ici.lpVerb = (LPCSTR)MAKEINTRESOURCE(0);
				pContextMenu->InvokeCommand(&ici);

				pContextMenu->Release();
			}

			CoTaskMemFree(pidl);
			i++;
		}
		
		pEnumIDList->Release();
	}
	return 0;

WM_NOTIFYの通知コードがNM_CLICKである場合は、SysLinkコントロールがクリックされたことを意味します。 このときには、クリックされたSysLinkコントロールと関連するアイテムの内容を実行しなければなりませんから、 まずはそのアイテムを特定する必要があります。 各コントロールのIDが1を基準としてカウントされていたことから、 iが0のときに作成されたコントロールのIDはi + 1で表すことができます。 よって、idFrom(クリックされたコントロールのID)がi + 1である場合は、 iで識別されるアイテムを実行してもよいことになります。 この実行というのは、IContextMenu::InvokeCommandで行うことになっていたため、 IShellFolder::GetUIObjectOfでIContextMenuを取得し、 QueryContextMenuでポップアップメニューの内容を初期化します。 追加される項目はデフォルトの項目だけで構いませんから、第5引数にはCMF_DEFAULTONLYを指定しています。 MAKEINTRESOURCEマクロに指定する値は、デフォルトの項目を実行する場合は常に0になります。 InvokeCommandではIShellBrowserに関連するインターフェースが必要になるため、 IObjectWithSite::SetSiteを通じてそれを渡しておくことになります。

case WM_PAINT: {
	HDC           hdc;
	PAINTSTRUCT   ps;
	HICON         hicon;
	IEnumIDList   *pEnumIDList;
	PITEMID_CHILD pidl;
	IExtractIcon  *pExtractIcon;
	int           i = 0;
	
	hdc = BeginPaint(hwnd, &ps);

	m_pShellFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnumIDList);

	while (pEnumIDList->Next(1, &pidl, NULL) == S_OK) {
		m_pShellFolder->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_IExtractIcon, NULL, (void **)&pExtractIcon);
		pExtractIcon->Extract(NULL, 0, &hicon, NULL, 0);
		DrawIcon(hdc, 10, 15 + (i * 40), hicon);
		pExtractIcon->Release();
		CoTaskMemFree(pidl);
		i++;
	}
	pEnumIDList->Release();

	EndPaint(hwnd, &ps);

	return 0;
}

WM_PAINTでは、アイテムに関連するアイコンを描画することになります。 アイコンのハンドルを取得するには、IExtractIcon::Extractを呼び出す必要があるため、 IShellFolder::GetUIObjectOfでIExtractIconを取得することになります。 アイコンの描画はDrawIconで行うことができます。

CShellView::CreateViewWindowではCreateWindowExを呼び出していましたが、 このためには予めウインドウクラスが登録されていなければなりません。 ウインドウクラスの登録と解除は、RegisterWindowClassによって行われます。

BOOL RegisterWindowClass(BOOL bRegister, HINSTANCE hinst)
{
	BOOL bResult;

	if (bRegister) {
		WNDCLASSEX wc;
		
		wc.cbSize        = sizeof(WNDCLASSEX);
		wc.style         = 0;
		wc.lpfnWndProc   = ::WindowProc;
		wc.cbClsExtra    = 0;
		wc.cbWndExtra    = 0;
		wc.hInstance     = hinst;
		wc.hIcon         = NULL;
		wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
		wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
		wc.lpszMenuName  = NULL;
		wc.lpszClassName = g_szClassName;
		wc.hIconSm       = NULL;
		
		bResult = (BOOL)RegisterClassEx(&wc);
	}
	else
		bResult = UnregisterClass(g_szClassName, hinst);

	return bResult;
}

RegisterWindowClassを呼び出すのは、DLLのエントリポイントであるDllMainです。 DLL_PROCESS_ATTACHのときに第1引数をTRUEにして呼び出し、 DLL_PROCESS_DETACHのときに第2引数をFALSEにして呼び出します。 wc.lpfnWndProcに指定しているWindowProcはクラスのメソッドではなく、通常のコールバック関数です。 このコールバック関数からCShellView::WindowProcを呼び出すには次のような処理が必要になります。

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	CShellView *p = (CShellView *)GetWindowLongPtr(hwnd, GWLP_USERDATA);

	if (p != NULL)
		return p->WindowProc(hwnd, uMsg, wParam, lParam);
	else
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

CShellView::CreateViewWindowでは、SetWindowLongPtrを通じてウインドウにthisポインタを関連付けていました。 よって、GetWindowLongPtrを呼び出せば、CShellViewへのアドレスを取得することができます。 SetWindowLongPtrはCreateWindowExが制御を返した後に呼び出していたため、 CreateWindowExで内部的に呼ばれるメッセージ(WM_CREATE)などは、 クラスのメソッドで処理することができません。 つまり、thisポインタが設定されていない際にもWindowProcが呼ばれるため、 その際にはDefWindowProcを呼び出すようにしています。

独自のメニュー項目について

フォルダビューの機能をフォルダのメニューから使用できるようにするためには、 IShellBrowser::InsertMenusSBを呼び出すことになります。 このメソッドに空のメニューハンドルを指定した場合は、 「ファイル」や「編集」といったメニュー項目が追加されますから、 この下に独自の項目を追加することになります。

STDMETHODIMP CShellView::UIActivate(UINT uState)
{
	if (uState != SVUIA_DEACTIVATE) {
		HMENU              hmenuPopup;
		MENUITEMINFO       mii;
		OLEMENUGROUPWIDTHS menuWidths = {0, 0, 0, 0, 0, 0};
		
		m_pShellBrowser->OnViewWindowActive(this);

		m_hmenu = CreateMenu();
		m_pShellBrowser->InsertMenusSB(m_hmenu, &menuWidths);

		mii.cbSize = sizeof(MENUITEMINFO);
		mii.fMask  = MIIM_SUBMENU;
		GetMenuItemInfo(m_hmenu, FCIDM_MENU_TOOLS, FALSE, &mii);
		hmenuPopup = mii.hSubMenu;

		mii.cbSize     = sizeof(MENUITEMINFO);
		mii.fMask      = MIIM_ID | MIIM_TYPE;
		mii.wID        = ID_BACK;
		mii.fType      = MFT_STRING;
		mii.dwTypeData = TEXT("前のフォルダに戻る");
		InsertMenuItem(hmenuPopup, ID_BACK, FALSE, &mii);
		
		m_pShellBrowser->SetMenuSB(m_hmenu, NULL, m_hwndView);
	}
	else {
		m_pShellBrowser->SetMenuSB(NULL, NULL, NULL);
		m_pShellBrowser->RemoveMenusSB(m_hmenu);
		DestroyMenu(m_hmenu);
	}

	return S_OK;
}

メニューの設定は、CShellView::UIActivateで行うことになります。 まず、InsertMenusSBを呼び出す前にIShellBrowser::OnViewWindowActiveを呼び出しておき、 その後にInsertMenusSBに空のメニューのハンドルを指定します。 第2引数のOLEMENUGROUPWIDTHS構造体については、0で初期化しておくだけで構いません。 メニューを初期化したら、特定の項目の下に独自の項目を追加するために、 ポップアップメニューのハンドルを取得するようにしています。 これには、MENUITEMINFO構造体のfMaskにMIIM_SUBMENUを指定してGetMenuItemInfoを呼び出すことになります。 このとき、第2引数にFCIDM_MENU_TOOLSを指定しているため対象の項目は「ツール」となりますが、 FCIDM_MENU_FILEにすれば「ファイル」が対象になります。 ポップアップメニューのハンドルを取得したら、独自の項目を追加するためにInsertMenuItemを呼び出します。 項目のIDは、FCIDM_SHVIEWFIRSTからFCIDM_SHVIEWLASTの間でなければならないことに注意してください。 メニューの内容が完成したら、IShellBrowser::SetMenuSBを呼び出すことでメニューを設定します。 UIActivateの引数がSVUIA_DEACTIVATEである場合はウインドウが破棄されることを意味しているため、 SetMenuSBにNULLを指定してメニューの設定を解除することになります。 その後、RemoveMenusSBを呼び出してメニューの中身の削除し、 最後にDestroyMenuでメニュー自体を破棄します。

独自に追加した項目が選択された場合は、WindowProcにWM_COMMANDが送られることになります。 どの項目が選択されたかは、wParamの下位ワードから判断することができます。

case WM_COMMAND:
	if (LOWORD(wParam) == ID_BACK)
		m_pShellBrowser->BrowseObject(NULL, SBSP_NAVIGATEBACK);
	return 0;

前のフォルダに戻るための処理は、BrowseObjectの第2引数にSBSP_NAVIGATEBACKを指定するだけです。 特定のフォルダに切り替えるわけではないので、第1引数にフォルダのPIDLを指定する必要はありません。



戻る