EternalWindows
ExplorerBrowser / ExplorerBrowserとの併用

今回のプログラムは、NameSpaceTreeControlとExplorerBrowserを同時に表示します。 NameSpaceTreeControlのアイテムが選択された場合にExplorerBrowserを更新します。

#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>

#pragma comment (lib, "shlwapi.lib")

class CNameSpaceTreeHost : public INameSpaceTreeControlEvents
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();

	STDMETHODIMP OnItemClick(IShellItem *psi, NSTCEHITTEST nstceHitTest, NSTCSTYLE nsctsFlags);
	STDMETHODIMP OnPropertyItemCommit(IShellItem *psi);
	STDMETHODIMP OnItemStateChanging(IShellItem *psi, NSTCITEMSTATE nstcisMask, NSTCITEMSTATE nstcisState);
	STDMETHODIMP OnItemStateChanged(IShellItem *psi, NSTCITEMSTATE nstcisMask, NSTCITEMSTATE nstcisState);
	STDMETHODIMP OnSelectionChanged(IShellItemArray *psiaSelection);
	STDMETHODIMP OnKeyboardInput(UINT uMsg, WPARAM wParam, LPARAM lParam);
	STDMETHODIMP OnBeforeExpand(IShellItem *psi);
	STDMETHODIMP OnAfterExpand(IShellItem *psi);
	STDMETHODIMP OnBeginLabelEdit(IShellItem *psi);
	STDMETHODIMP OnEndLabelEdit(IShellItem *psi);
	STDMETHODIMP OnGetToolTip(IShellItem *psi, LPWSTR pszTip, int cchTip);
	STDMETHODIMP OnBeforeItemDelete(IShellItem *psi);
	STDMETHODIMP OnItemAdded(IShellItem *psi, BOOL fIsRoot);
	STDMETHODIMP OnItemDeleted(IShellItem *psi, BOOL fIsRoot);
	STDMETHODIMP OnBeforeContextMenu(IShellItem *psi, REFIID riid, void **ppv);
	STDMETHODIMP OnAfterContextMenu(IShellItem *psi, IContextMenu *pcmIn, REFIID riid, void **ppv);
	STDMETHODIMP OnBeforeStateImageChange(IShellItem *psi);
	STDMETHODIMP OnGetDefaultIconIndex(IShellItem *psi, int *piDefaultIcon, int *piOpenIcon);

	CNameSpaceTreeHost();
	int Run(HINSTANCE hinst, int nCmdShow);
	LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	BOOL Create();

private:
	LONG                  m_cRef;
	HWND                  m_hwnd;
	DWORD                 m_dwTreeCookie;
	IExplorerBrowser      *m_pExplorerBrowser;
	INameSpaceTreeControl *m_pNameSpaceTreeControl;
};

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

CNameSpaceTreeHost *g_pNameSpaceTreeHost = NULL;

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	int nResult = 0;

	OleInitialize(NULL);
	
	g_pNameSpaceTreeHost = new CNameSpaceTreeHost;
	if (g_pNameSpaceTreeHost != NULL) {
		nResult = g_pNameSpaceTreeHost->Run(hinst, nCmdShow);
		g_pNameSpaceTreeHost->Release();
	}

	OleUninitialize();

	return nResult;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return g_pNameSpaceTreeHost->WindowProc(hwnd, uMsg, wParam, lParam);
}


// CNameSpaceTreeHost


CNameSpaceTreeHost::CNameSpaceTreeHost()
{
	m_cRef = 1;
	m_hwnd = NULL;
	m_dwTreeCookie = 0;
	m_pExplorerBrowser = NULL;
	m_pNameSpaceTreeControl = NULL;
}

STDMETHODIMP CNameSpaceTreeHost::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_INameSpaceTreeControlEvents))
		*ppvObject = static_cast<INameSpaceTreeControlEvents *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CNameSpaceTreeHost::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CNameSpaceTreeHost::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CNameSpaceTreeHost::OnItemClick(IShellItem *psi, NSTCEHITTEST nstceHitTest, NSTCSTYLE nsctsFlags)
{
	if ((nstceHitTest == NSTCEHT_ONITEMICON || nstceHitTest == NSTCEHT_ONITEMLABEL) && nsctsFlags == NSTCECT_LBUTTON) {
		PIDLIST_ABSOLUTE pidl;
		SHGetIDListFromObject(psi, &pidl);
		m_pExplorerBrowser->BrowseToIDList(pidl, SBSP_ABSOLUTE);
		CoTaskMemFree(pidl);
		return S_OK;
	}

	return S_FALSE;
}

STDMETHODIMP CNameSpaceTreeHost::OnPropertyItemCommit(IShellItem *psi)
{
	return S_FALSE;
}

STDMETHODIMP CNameSpaceTreeHost::OnItemStateChanging(IShellItem *psi, NSTCITEMSTATE nstcisMask, NSTCITEMSTATE nstcisState)
{

	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnItemStateChanged(IShellItem *psi, NSTCITEMSTATE nstcisMask, NSTCITEMSTATE nstcisState)
{
	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnSelectionChanged(IShellItemArray *psiaSelection)
{
	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnKeyboardInput(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg == WM_KEYDOWN && wParam == VK_RETURN) {
		HRESULT          hr;
		IShellItem       *pShellItem;
		IShellItemArray  *pShellItemArray;
		PIDLIST_ABSOLUTE pidl;
		
		hr = m_pNameSpaceTreeControl->GetSelectedItems(&pShellItemArray);
		if (hr != S_OK)
			return S_FALSE;

		pShellItemArray->GetItemAt(0, &pShellItem);
		
		SHGetIDListFromObject(pShellItem, &pidl);
		m_pExplorerBrowser->BrowseToIDList(pidl, SBSP_ABSOLUTE);
		CoTaskMemFree(pidl);

		pShellItem->Release();
		pShellItemArray->Release();

		return S_OK;
	}
	
	return S_FALSE;
}

STDMETHODIMP CNameSpaceTreeHost::OnBeforeExpand(IShellItem *psi)
{
	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnAfterExpand(IShellItem *psi)
{
	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnBeginLabelEdit(IShellItem *psi)
{
	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnEndLabelEdit(IShellItem *psi)
{
	return S_OK;
}

STDMETHODIMP CNameSpaceTreeHost::OnGetToolTip(IShellItem *psi, LPWSTR pszTip, int cchTip)
{
	return E_NOTIMPL;
}

STDMETHODIMP CNameSpaceTreeHost::OnBeforeItemDelete(IShellItem *psi)
{
	return E_NOTIMPL;
}

STDMETHODIMP CNameSpaceTreeHost::OnItemAdded(IShellItem *psi, BOOL fIsRoot)
{
	return E_NOTIMPL;
}

STDMETHODIMP CNameSpaceTreeHost::OnItemDeleted(IShellItem *psi, BOOL fIsRoot)
{
	return E_NOTIMPL;
}

STDMETHODIMP CNameSpaceTreeHost::OnBeforeContextMenu(IShellItem *psi, REFIID riid, void **ppv)
{
	*ppv = NULL;

	return E_NOTIMPL;
}


STDMETHODIMP CNameSpaceTreeHost::OnAfterContextMenu(IShellItem *psi, IContextMenu *pcmIn, REFIID riid, void **ppv)
{
	*ppv = NULL;

	return E_NOTIMPL;
}

STDMETHODIMP CNameSpaceTreeHost::OnBeforeStateImageChange(IShellItem *psi)
{
	return S_OK;
}


STDMETHODIMP CNameSpaceTreeHost::OnGetDefaultIconIndex(IShellItem *psi, int *piDefaultIcon, int *piOpenIcon)
{
	return E_NOTIMPL;
}

int CNameSpaceTreeHost::Run(HINSTANCE hinst, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = ::WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CNameSpaceTreeHost::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_CREATE:
		m_hwnd = hwnd;
		if (!Create())
			return -1;
		return 0;
	
	case WM_SIZE: {
		HWND hwndTree;
		RECT rc;
		int  nTreeWidth;
		int  nViewWidth;

		nTreeWidth = 300;
		nViewWidth = LOWORD(lParam) - nTreeWidth;

		IUnknown_GetWindow(m_pNameSpaceTreeControl, &hwndTree);
		MoveWindow(hwndTree, 0, 0, nTreeWidth, HIWORD(lParam), TRUE);
		
		SetRect(&rc, nTreeWidth, 0, nTreeWidth + nViewWidth, HIWORD(lParam));
		m_pExplorerBrowser->SetRect(NULL, rc);

		return 0;
	}

	case WM_DESTROY:
		if (m_pExplorerBrowser != NULL) {
			m_pExplorerBrowser->Destroy();
			m_pExplorerBrowser->Release();
		}
		if (m_pNameSpaceTreeControl != NULL) {
			m_pNameSpaceTreeControl->TreeUnadvise(m_dwTreeCookie);
			m_pNameSpaceTreeControl->Release();
		}
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL CNameSpaceTreeHost::Create()
{
	RECT           rc;
	HRESULT        hr;
	IShellItem     *pShellItem;
	FOLDERSETTINGS fs;

	hr = CoCreateInstance(CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pExplorerBrowser));
	if (FAILED(hr))
		return FALSE;

	hr = CoCreateInstance(CLSID_NamespaceTreeControl, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pNameSpaceTreeControl));
	if (FAILED(hr))
		return FALSE;

	fs.ViewMode = FVM_DETAILS;
	fs.fFlags   = 0;
	SetRectEmpty(&rc);
	hr = m_pExplorerBrowser->Initialize(m_hwnd, &rc, &fs);
	if (FAILED(hr))
		return FALSE;

	SetRectEmpty(&rc);
	hr = m_pNameSpaceTreeControl->Initialize(m_hwnd, &rc, NSTCS_HASEXPANDOS | NSTCS_SHOWSELECTIONALWAYS);
	if (FAILED(hr))
		return FALSE;
	
	SHCreateItemInKnownFolder(FOLDERID_Desktop, 0, NULL, IID_PPV_ARGS(&pShellItem));
	m_pNameSpaceTreeControl->AppendRoot(pShellItem, SHCONTF_FOLDERS, NSTCRS_VISIBLE | NSTCRS_EXPANDED, NULL);
	pShellItem->Release();

	m_pNameSpaceTreeControl->TreeAdvise(static_cast<INameSpaceTreeControlEvents *>(this), &m_dwTreeCookie);

	return TRUE;
}

今回はツリーアイテムの選択時にExplorerBrowserを表示するため、 CreateでIExplorerBrowserを取得しています。 また、いつでも使用できるようにInitializeで初期化も行っています。 ツリーアイテムの選択はイベントとして通知されるため、 INameSpaceTreeControl::TreeAdviseでオブジェクトを登録しておくことになります。

ツリーアイテムの選択は、アイテムのクリックによって行われることがあります。 アイテムがクリックされた際には、OnItemClickが呼ばれます。

STDMETHODIMP CNameSpaceTreeHost::OnItemClick(IShellItem *psi, NSTCEHITTEST nstceHitTest, NSTCSTYLE nsctsFlags)
{
	if ((nstceHitTest == NSTCEHT_ONITEMICON || nstceHitTest == NSTCEHT_ONITEMLABEL) && nsctsFlags == NSTCECT_LBUTTON) {
		PIDLIST_ABSOLUTE pidl;
		SHGetIDListFromObject(psi, &pidl);
		m_pExplorerBrowser->BrowseToIDList(pidl, SBSP_ABSOLUTE);
		CoTaskMemFree(pidl);
		return S_OK;
	}

	return S_FALSE;
}

psiはクリックされたアイテムを表し、 nstceHitTestはアイテムのどの位置がクリックされたかを示す定数が格納されています。 NSTCEHT_ONITEMICONはアイテムのアイコンを意味し、NSTCEHT_ONITEMLABELはアイテムの名前を意味するため、 これらの位置の場合はクリックを許可することにします。 クリックは左クリックでなければならないため、 nsctsFlagsにNSTCECT_LBUTTONが格納されているかも確認します。 条件が満たされたら、SHGetIDListFromObjectでIShellItemからPIDLを取得し、 それをIExplorerBrowser::BrowseToIDListに指定します。 これにより、ExplorerBrowserの中身はIShellItemで表されるアイテムの中身を反映するはずです。 IExplorerBrowserにはBrowseToObjectというインターフェースを指定できるメソッドがありますが、 このメソッドにIShellItemを指定しても上手く機能しないようです。

ツリーアイテムの選択は、キーの入力で行われることもあります。 キーが入力された場合は、OnKeyboardInputが呼ばれます。

STDMETHODIMP CNameSpaceTreeHost::OnKeyboardInput(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg == WM_KEYDOWN && wParam == VK_RETURN) {
		HRESULT          hr;
		IShellItem       *pShellItem;
		IShellItemArray  *pShellItemArray;
		PIDLIST_ABSOLUTE pidl;
		
		hr = m_pNameSpaceTreeControl->GetSelectedItems(&pShellItemArray);
		if (hr != S_OK)
			return S_FALSE;

		pShellItemArray->GetItemAt(0, &pShellItem);
		
		SHGetIDListFromObject(pShellItem, &pidl);
		m_pExplorerBrowser->BrowseToIDList(pidl, SBSP_ABSOLUTE);
		CoTaskMemFree(pidl);

		pShellItem->Release();
		pShellItemArray->Release();

		return S_OK;
	}
	
	return S_FALSE;
}

uMsgにはWM_KEYDOWNやWM_CHARなどが格納されますが、今回はWM_KEYDOWNを処理するようにしています。 ExplorerBrowserを更新するのはEnterキーが押された場合が妥当と思われるため、 wParamがVK_RETURNであるかも確認しています。 条件が満たされた場合は、OnItemClickのときと同じようにIExplorerBrowser::BrowseToIDListを呼び出すことになりますが、 OnKeyboardInputにはIShellItemが引数として渡されません。 よって、INameSpaceTreeControl::GetSelectedItemsを呼び出すことより、 現在選択されたアイテムの配列を取得することになります。 このアイテムの配列はIShellItemArrayで表すことができ、 GetItemAtを呼び出すことで第1引数のインデックスで表されるIShellItemを取得することができます。 IShellItemを取得すれば後の処理はOnItemClickのときと同様です。

今回はアイテムの選択をクリックやキー入力で検出したわけですが、 実はアイテムが選択された際にはOnSelectionChangedというメソッドが呼ばれることになっています。 よって、このメソッド内でIExplorerBrowser::BrowseToIDListを呼び出せばよいように思えますが、 そのようにすると方向キーで選択状態を移行させただけで、ExplorerBrowserを更新することになってしまいます。 キー入力によるExplorerBrowserの更新はEnterキーだけにしたいため、 OnSelectionChangedではなくOnKeyboardInputを処理するようにしています。


戻る