« 2006年12月 | メイン | 2007年02月 »

1  2

2007年01月 記事一覧

2007年01月01日

ファイルやフォルダのプロパティを表示する

test93.gif

SHMultiFilePropertiesはエクスプローラなどでファイルやフォルダのプロパティを表示するためのAPIで、複数のファイルなどを選択した状態でも利用できるものだ。

ここではこのSHMultiFilePropertiesを利用して1つのファイルやフォルダのプロパティを表示した。複数選択時のプロパティの表示方法は...よくわからなかったので現在調査中。

#include "atlstr.h"

#include "shlobj.h"


//
//	ファイルのプロパティを表示する
//
bool	ShowPropertyDialog(LPCTSTR pszPath)
{
	LPITEMIDLIST	pItemIdList;
	IShellFolder*	pIShellFolder;
	IDataObject*	pIDataObject;
	HRESULT			hr;
	ULONG			nAttribute;

	hr = ::SHGetDesktopFolder(&pIShellFolder);
	if(FAILED(hr))
		return	false;

	nAttribute = ::PathIsDirectory(pszPath) ? SFGAO_FOLDER : 0;

	pItemIdList = NULL;
#ifdef	UNICODE
	hr = pIShellFolder->ParseDisplayName(NULL,NULL,pszPath,NULL,&pItemIdList,NULL);
#else
	{
		CAtlStringW	ustrPath;

		//簡単にUnicode変換
		ustrPath = pszPath;
		hr = pIShellFolder->ParseDisplayName(NULL,NULL,ustrPath.GetBuffer(0),NULL,&pItemIdList,&nAttribute);
	}
#endif

	if(SUCCEEDED(hr))
		hr = pIShellFolder->GetUIObjectOf(NULL,1,(LPCITEMIDLIST*)&pItemIdList,IID_IDataObject,NULL,(void**)&pIDataObject);

	if(SUCCEEDED(hr))
	{

		//この関数を複数のファイルのプロパティを表示できるように改造する場合は
		//以下のコメントアウト処理をSHMultiFilePropertiesの前に実行する(KB330040)
		//{
		//	LPITEMIDLIST	pItemIdList;
		//	IDataObject*	pIDataObject;
		//
		//	hr = ::SHGetSpecialFolderLocation(NULL,CSIDL_DRIVES,&pItemIdList);
		//	if(SUCCEEDED(hr))
		//	{
		//		hr = pIShellFolder->GetUIObjectOf(NULL,1,(LPCITEMIDLIST*)&pItemIdList,IID_IDataObject,NULL,(void**)&pIDataObject);
		//		::ILFree(pItemIdList);
		//	}
		//	pIDataObject->Release();
		//}

		hr = ::SHMultiFileProperties(pIDataObject,0);
		pIDataObject->Release();
	}

	pIShellFolder->Release();

	if(pItemIdList)
		::ILFree(pItemIdList);

	return	SUCCEEDED(hr) ? true : false;
}




void	Test(void)
{
	::CoInitialize(NULL);

	ShowPropertyDialog(_T("c:\\autoexec.bat"));

	::MessageBox(NULL,_T("ダミーメッセージボックス"),_T(""),MB_OK);

	::CoUninitialize();
}

プロジェクトファイルをダウンロード

2007年01月02日

パスからPIDLを取得する

ファイル名やフォルダ名などの文字列からIDLを取得する方法にはいくつかある。
ここでは
ILCreateFromPath、SHSimpleIDListFromPath、IShellFolderを利用する3つの方法で順番に取得を試みている。ILCreateFromPathやSHSimpleIDListFromPathだけでは取得できない場合でもこの3つを併用すると確実に取得できる。

依存環境:ATL
#include "atlstr.h"


#include "shlobj.h"


/*!
 * \brief
 * パスからPIDLを取得する
 * 
 * 取得したPIDLは必ず CoTaskMemFree、ILFree、IMalloc::Free() のいずれかで解放すること!
 * 
 * \param[in]	pszPath
 * PIDLを取得したいパス
 * 
 * \param[out]	ppItemIdList
 * 取得したPIDL
 * 
 * \retval	true	成功
 * \retval	false	失敗
 * 
 */
bool	GetItemIdListFromPath(LPCTSTR pszPath,LPITEMIDLIST* ppItemIdList)
{
	if(ppItemIdList == NULL || pszPath == NULL || *pszPath == NULL)
		return	false;

	//ILCreateFromPathで取得
	*ppItemIdList = ::ILCreateFromPath(pszPath);
	if(*ppItemIdList)
		return	true;		//取得成功

	//SHSimpleIDListFromPathで取得
	*ppItemIdList = ::SHSimpleIDListFromPath((CAtlStringW)pszPath);
	if(*ppItemIdList)
		return	true;		//取得成功

	//IShellFolderで取得
	{
		CComPtr<IShellFolder>	pIShellFolder;
		HRESULT			hr;

		hr = ::SHGetDesktopFolder(&pIShellFolder);
		if(pIShellFolder == NULL)
			return	false;

	#ifdef	UNICODE
		hr = pIShellFolder->ParseDisplayName(NULL,NULL,pszPath,NULL,&pItemIdList,NULL);
	#else
		{
			CAtlStringW	ustrPath;

			//簡単にUnicode変換
			ustrPath = pszPath;
			hr = pIShellFolder->ParseDisplayName(NULL,NULL,ustrPath.GetBuffer(0),NULL,ppItemIdList,NULL);
		}
	#endif

		if(SUCCEEDED(hr))
			return	true;		//取得成功
	}

	return	false;
}



void	Test(void)
{
	bool			ret;
	LPITEMIDLIST	pidl;

	ret = GetItemIdListFromPath(_T("c:\\autoexec.bat"),&pidl);

	if(ret)
		::MessageBox(NULL,_T("取得に成功しました"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("失敗しました"),_T(""),MB_OK);

	if(ret)
		::ILFree(pidl);

}


プロジェクトファイルをダウンロード

ファイルやフォルダの作成や削除などを検出する

SHChangeNotifyRegisterを使うとWindows上でファイルやフォルダが作成されたことなどを瞬時に知ることができる。

以下のフラグなどを組み合わせることでフォルダの作成だけを知るなどもできる
#define SHCNE_RENAMEITEM          0x00000001L
#define SHCNE_CREATE              0x00000002L
#define SHCNE_DELETE              0x00000004L
#define SHCNE_MKDIR               0x00000008L
#define SHCNE_RMDIR               0x00000010L
#define SHCNE_MEDIAINSERTED       0x00000020L
#define SHCNE_MEDIAREMOVED        0x00000040L
#define SHCNE_DRIVEREMOVED        0x00000080L
#define SHCNE_DRIVEADD            0x00000100L
#define SHCNE_NETSHARE            0x00000200L
#define SHCNE_NETUNSHARE          0x00000400L
#define SHCNE_ATTRIBUTES          0x00000800L
#define SHCNE_UPDATEDIR           0x00001000L
#define SHCNE_UPDATEITEM          0x00002000L
#define SHCNE_SERVERDISCONNECT    0x00004000L
#define SHCNE_UPDATEIMAGE         0x00008000L
#define SHCNE_DRIVEADDGUI         0x00010000L
#define SHCNE_RENAMEFOLDER        0x00020000L
#define SHCNE_FREESPACE           0x00040000L


ここではWTLアプリケーションウイザードでダイアログベースのプロジェクトを作成し、生成されたMainDlg.hを以下のように変更した。

// MainDlg.h : CMainDlg クラスのインターフェイス
//
/////////////////////////////////////////////////////////////////////////////

#pragma once


#include "atlstr.h"


#define	WM_CHANGENOTIFY	WM_APP + 1





class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
		public CMessageFilter, public CIdleHandler
{
public:
	enum { IDD = IDD_MAINDLG };

	virtual BOOL PreTranslateMessage(MSG* pMsg)
	{
		return CWindow::IsDialogMessage(pMsg);
	}

	virtual BOOL OnIdle()
	{
		return FALSE;
	}

	BEGIN_UPDATE_UI_MAP(CMainDlg)
	END_UPDATE_UI_MAP()

	BEGIN_MSG_MAP(CMainDlg)

		//シェル通知用メッセージハンドラの追加
		MESSAGE_HANDLER(WM_CHANGENOTIFY, OnChangeNotify)

		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
	END_MSG_MAP()

// ハンドラーのプロトタイプ (引数が必要な場合はコメントを外してください):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)


	/*!
	 * \brief
	 * パスからPIDLを取得する
	 * 
	 * 取得したPIDLは必ず CoTaskMemFree、ILFree、IMalloc::Free() のいずれかで解放すること!
	 * 
	 * \param[in]	pszPath
	 * PIDLを取得したいパス
	 * 
	 * \param[out]	ppItemIdList
	 * 取得したPIDL
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	GetItemIdListFromPath(LPCTSTR pszPath,LPITEMIDLIST* ppItemIdList)
	{
		if(ppItemIdList == NULL || pszPath == NULL || *pszPath == NULL)
			return	false;

		//ILCreateFromPathで取得
		*ppItemIdList = ::ILCreateFromPath(pszPath);
		if(*ppItemIdList)
			return	true;		//取得成功

		//SHSimpleIDListFromPathで取得
		*ppItemIdList = ::SHSimpleIDListFromPath((CAtlStringW)pszPath);
		if(*ppItemIdList)
			return	true;		//取得成功

		//IShellFolderで取得
		{
			CComPtr<IShellFolder>	pIShellFolder;
			HRESULT			hr;

			hr = ::SHGetDesktopFolder(&pIShellFolder);
			if(pIShellFolder == NULL)
				return	false;

		#ifdef	UNICODE
			hr = pIShellFolder->ParseDisplayName(NULL,NULL,pszPath,NULL,&pItemIdList,NULL);
		#else
			{
				CAtlStringW	ustrPath;

				//簡単にUnicode変換
				ustrPath = pszPath;
				hr = pIShellFolder->ParseDisplayName(NULL,NULL,ustrPath.GetBuffer(0),NULL,ppItemIdList,NULL);
			}
		#endif

			if(SUCCEEDED(hr))
				return	true;		//取得成功
		}

		return	false;
	}


	//
	//	シェル通知の登録/登録解除関数
	//
	//pszPathは登録時のみ必須
	//
	bool	RegisterNotify(bool bRegister,ULONG* pnID,LPCTSTR pszPath=NULL)
	{
		if(pnID == NULL)
			return	false;

		//登録
		if(bRegister)
		{
			bool			ret;
			LPITEMIDLIST	pidl;
			SHChangeNotifyEntry sEntry;

			if(pszPath == NULL)
				return	false;

			ret = GetItemIdListFromPath(pszPath,&pidl);
			if(ret == false)
				return	false;

			sEntry.fRecursive = TRUE;
			sEntry.pidl = pidl;

			//ファイルやフォルダの作成と名前変更のみを処理
			*pnID = ::SHChangeNotifyRegister(m_hWnd,SHCNRF_ShellLevel | SHCNRF_InterruptLevel,SHCNE_RENAMEITEM | SHCNE_CREATE | SHCNE_DELETE | SHCNE_MKDIR | SHCNE_RMDIR | SHCNE_RENAMEFOLDER,WM_CHANGENOTIFY,1,&sEntry);

			::ILFree(pidl);

			return	*pnID ? true : false;
		}

		if(*pnID == 0)
			return	false;

		//登録解除
		BOOL	ret;

		ret = ::SHChangeNotifyDeregister(*pnID);

		return	ret ? true : false;

	}




	//
	//	シェル通知ハンドラー
	//
	//イベントIDはlParamに入る
	//関連するフォルダやパスを示すPIDLはwParamに入る
	//
	LRESULT OnChangeNotify(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		LONG			nEvent;
		LPITEMIDLIST	pidl;
		LPITEMIDLIST	pidlExtra;

		nEvent		= lParam;
		pidl		= ((LPITEMIDLIST*)wParam)[0];
		pidlExtra	= ((LPITEMIDLIST*)wParam)[1];




		BOOL		ret;
		TCHAR		pszPath[MAX_PATH*2];
		CAtlString	strMessage;

		ret = ::SHGetPathFromIDList(pidl,pszPath);
		if(ret)
		{
			strMessage += pszPath;
			strMessage += _T("\n");
		}

		switch(nEvent)
		{
		case SHCNE_CREATE:
			strMessage += _T("ファイルを作成しました");
			break;

		case SHCNE_DELETE:
			strMessage += _T("ファイルを削除しました");
			break;

		case SHCNE_RENAMEITEM:
			ret = ::SHGetPathFromIDList(pidlExtra,pszPath);
			if(ret)
			{
				strMessage += pszPath;
				strMessage += _T("\n");
			}
			strMessage += _T("ファイル名を変更しました");
			break;

		case SHCNE_MKDIR:
			strMessage += _T("フォルダを作成しました");
			break;

		case SHCNE_RMDIR:
			strMessage += _T("フォルダを削除しました");
			break;

		case SHCNE_RENAMEFOLDER:
			ret = ::SHGetPathFromIDList(pidlExtra,pszPath);
			if(ret)
			{
				strMessage += pszPath;
				strMessage += _T("\n");
			}
			strMessage += _T("フォルダ名を変更しました");
			break;

		default:
			strMessage += _T("その他の操作");
			break;
		}

		MessageBox(strMessage);

		return	0;
	}



	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		// 画面の中心へダイアログを移動
		CenterWindow();

		// アイコンの設定
		HICON hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), 
			IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
		SetIcon(hIcon, TRUE);
		HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), 
			IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
		SetIcon(hIconSmall, FALSE);

		// メッセージ フィルターおよび画面更新用のオブジェクト登録
		CMessageLoop* pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->AddMessageFilter(this);
		pLoop->AddIdleHandler(this);

		UIAddChildWindowContainer(m_hWnd);

		//シェル通知の登録
		RegisterNotify(true,&_nID,_T("c:\\"));

		return TRUE;
	}

	ULONG	_nID;

	LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CAboutDlg dlg;
		dlg.DoModal();
		return 0;
	}

	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		// TODO: チェックコードの追加
		CloseDialog(wID);
		return 0;
	}

	LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CloseDialog(wID);
		return 0;
	}

	void CloseDialog(int nVal)
	{
		//シェル通知の登録解除
		RegisterNotify(false,&_nID);

		DestroyWindow();
		::PostQuitMessage(nVal);
	}
};

プロジェクトファイルをダウンロード

2007年01月03日

ファイルやフォルダのプロパティを表示する

test96.gif
SHMultiFilePropertiesを利用するとファイルやフォルダなどのプロパティを表示できる。複数のファイルなどを選択した場合でも使える。

依存環境:ATL
#include "atlstr.h"
#include "atlcoll.h"

#include "shlobj.h"


/*!
 * \brief
 * パスからPIDLを取得する
 * 
 * 取得したPIDLは必ず CoTaskMemFree、ILFree、IMalloc::Free() のいずれかで解放すること!
 * 
 * \param[in]	pszPath
 * PIDLを取得したいパス
 * 
 * \param[out]	ppItemIdList
 * 取得したPIDL
 * 
 * \retval	true	成功
 * \retval	false	失敗
 * 
 */
bool	GetItemIdListFromPath(LPCTSTR pszPath,LPITEMIDLIST* ppItemIdList)
{
	if(ppItemIdList == NULL || pszPath == NULL || *pszPath == NULL)
		return	false;

	//ILCreateFromPathで取得
	*ppItemIdList = ::ILCreateFromPath(pszPath);
	if(*ppItemIdList)
		return	true;		//取得成功

	//SHSimpleIDListFromPathで取得
	*ppItemIdList = ::SHSimpleIDListFromPath((CAtlStringW)pszPath);
	if(*ppItemIdList)
		return	true;		//取得成功

	//IShellFolderで取得
	{
		CComPtr<IShellFolder>	pIShellFolder;
		HRESULT			hr;

		hr = ::SHGetDesktopFolder(&pIShellFolder);
		if(pIShellFolder == NULL)
			return	false;

	#ifdef	UNICODE
		hr = pIShellFolder->ParseDisplayName(NULL,NULL,pszPath,NULL,&pItemIdList,NULL);
	#else
		{
			CAtlStringW	ustrPath;

			//簡単にUnicode変換
			ustrPath = pszPath;
			hr = pIShellFolder->ParseDisplayName(NULL,NULL,ustrPath.GetBuffer(0),NULL,ppItemIdList,NULL);
		}
	#endif

		if(SUCCEEDED(hr))
			return	true;		//取得成功
	}

	return	false;
}



//
//	ファイルやフォルダのプロパティを表示する
//
bool	ShowPropertyDialog(LPCTSTR pszPath)
{
	HRESULT			hr;
	bool			ret;
	LPITEMIDLIST	pItemIdList;
	CComPtr<IShellFolder>	pIShellFolder;
	CComPtr<IDataObject>	pIDataObject;

	hr = ::SHGetDesktopFolder(&pIShellFolder);
	if(FAILED(hr))
		return	false;

	ret = GetItemIdListFromPath(pszPath,&pItemIdList);
	if(ret == false)
		return	false;

	hr = pIShellFolder->GetUIObjectOf(NULL,1,(LPCITEMIDLIST*)&pItemIdList,IID_IDataObject,NULL,(void**)&pIDataObject);
	::ILFree(pItemIdList);
	if(FAILED(hr))
		return	false;

	hr = ::SHMultiFileProperties(pIDataObject,0);

	return	SUCCEEDED(hr) ? true : false;
}





//
//	複数のファイルやフォルダのプロパティを表示する
//
bool	ShowMultiPropertyDialog(const CAtlArray<CAtlString>& astrPath)
{
	HRESULT		hr;
	CComPtr<IShellFolder>	pIShellFolder;
	CComPtr<IDataObject>	pIDataObject;

	hr = ::SHGetDesktopFolder(&pIShellFolder);
	if(FAILED(hr))
		return	false;

	//複数ファイルのPIDLを取得し、IDataObjectに結びつける
	{
		size_t	i;
		size_t	nSize;
		bool	ret;
		LPITEMIDLIST*	ppItemIdList;

		nSize = astrPath.GetCount();
		ppItemIdList = new LPITEMIDLIST[nSize];
		if(ppItemIdList == NULL)
			return	false;

		for(i = 0; i < nSize; i++)
		{
			ret = GetItemIdListFromPath(astrPath[i],&ppItemIdList[i]);
			if(ret == false)
				break;
		}

		if(ret == false)
			nSize = i;

		if(ret)
			hr = pIShellFolder->GetUIObjectOf(NULL,(UINT)nSize,(LPCITEMIDLIST*)ppItemIdList,IID_IDataObject,NULL,(void**)&pIDataObject);

		for(i = 0; i < nSize; i++)
			::ILFree(ppItemIdList[i]);
		delete	ppItemIdList;

		if(ret == false || FAILED(hr))
			return	false;
	}


	//この関数を複数のファイルのプロパティを表示できるように改造する場合は
	//以下のコメントアウト処理をSHMultiFilePropertiesの前に実行する(KB330040)
	{
		LPITEMIDLIST	pItemIdList;
		IDataObject*	pIDataObject;
	
		hr = ::SHGetSpecialFolderLocation(NULL,CSIDL_DRIVES,&pItemIdList);
		if(SUCCEEDED(hr))
		{
			hr = pIShellFolder->GetUIObjectOf(NULL,1,(LPCITEMIDLIST*)&pItemIdList,IID_IDataObject,NULL,(void**)&pIDataObject);
			::ILFree(pItemIdList);
		}
		pIDataObject->Release();
	}

	hr = ::SHMultiFileProperties(pIDataObject,0);

	return	SUCCEEDED(hr) ? true : false;
}






void	Test(void)
{
	::CoInitialize(NULL);

	//1つのファイルのプロパティ表示
	ShowPropertyDialog(_T("c:\\autoexec.bat"));

	//3つのファイルのプロパティ表示
	{
		CAtlArray<CAtlString>	astrPath;

		astrPath.Add(_T("c:\\config.sys"));
		astrPath.Add(_T("c:\\autoexec.bat"));
		astrPath.Add(_T("c:\\pagefile.sys"));
		ShowMultiPropertyDialog(astrPath);
	}

	::MessageBox(NULL,_T("ダミーメッセージボックス"),_T(""),MB_OK);

	::CoUninitialize();
}

プロジェクトファイルをダウンロード

書式文字列を使えるシェルメッセージボックスを表示する

test97.gif
ShellMessageBoxは書式付文字列を利用できるメッセージボックス用のAPIだ。

このAPIを利用すると変数内容を簡単にメッセージボックスとして表示できて便利だが...文字列の書式はFormatMessageというAPIの書式であり、通常用いられるprintf形式とは大きく異なるため使い勝手が悪いのが残念。

#include "shellapi.h"
#pragma	comment(lib,"shlwapi.lib")

void	Test(void)
{
	int	nDummy;

	nDummy = 10;
	::ShellMessageBox(NULL,NULL,_T("nDummy = 「%1!d!」\nprintfのようにフォーマット文字列を出力可能"),_T("シェルメッセージボックスサンプル"),MB_OK,nDummy);
}

プロジェクトファイルをダウンロード

2007年01月12日

WTLでエクスプローラ形式のウインドウを作る

test98_01.gif
Windows XPまでのエクスプローラのように左側がツリー、右側がリストビューのウインドウをWTLを利用して作成する。



ATL/WTLアプリケーションウイザードを使ってプロジェクトのひな型を作る。

「ファイル」メニューの「新規作成」から「プロジェクト」を選択して新しいプロジェクトのテンプレートとして「ATL/WTLアプリケーションウイザード」を選択する。

test98_02.gif
アプリケーションウイザードではデフォルトの設定のまま「完了」ボタンを押す。

test98_03.gif
生成したソースコードの中から~View.hを開く。

ここではプロジェクト名を「Test98」としたので「Test98View.h」というファイル名になっている。

test98_04.gif 既存のビュークラスは必要がないので全部消してしまい、リストビュースタイルのCRightViewとツリービュースタイルのCLeftViewを定義する。
class CRightView : public CWindowImpl<CRightView, CListViewCtrl>
{
public:
	DECLARE_WND_SUPERCLASS(NULL, CListViewCtrl::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		pMsg;
		return FALSE;
	}

	BEGIN_MSG_MAP(CRightView)
	END_MSG_MAP()

// ハンドラーのプロトタイプ (引数が必要な場合はコメントを外してください):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
};


class CLeftView : public CWindowImpl<CLeftView, CTreeViewCtrl>
{
public:
	DECLARE_WND_SUPERCLASS(NULL, CTreeViewCtrl::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		pMsg;
		return FALSE;
	}

	BEGIN_MSG_MAP(CLeftView)
	END_MSG_MAP()

// ハンドラーのプロトタイプ (引数が必要な場合はコメントを外してください):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
};

test98_05.gif
次に「MainFrm.h」を開く。ここでは赤枠で示した部分を書き換えるとともに若干追加する。

test98_06.gif まず先頭部分に1行インクルード設定を追加する。
そして上で示した2箇所の赤枠部分を書き換える。それぞれメンバークラス定義とプリトランスレートのメッセージ処理に当たる。
#include <atlsplit.h>
	CSplitterWindow		_wndSplitter;
	CRightView			_viewRight;
	CLeftView			_viewLeft;
		// 左ペインのPreTranslateMessageを呼び出す
		if(_viewLeft.PreTranslateMessage(pMsg))
			return TRUE;

		// 右ペインのPreTranslateMessageを呼び出す
		return _viewRight.PreTranslateMessage(pMsg);

test98_07.gif
さらに同じく「MainFrm.h」のOnCreate内のビューを作成する部分を次の図のように書き換える。


test98_08.gif ここではもともとあったビューの作成の代わりに、スプリッターウインドウと左右のビューを作成している。
		// スプリッタウィンドウを作成
		_wndSplitter.Create (m_hWnd, rcDefault, NULL,WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

		// スプリッタウィンドウ拡張スタイルを設定
		_wndSplitter.SetSplitterExtendedStyle(0);

		// 左ペインのビューウィンドウを作成
		_viewLeft.Create(_wndSplitter);
		_wndSplitter.SetSplitterPane(SPLIT_PANE_LEFT,_viewLeft);

		// 右ペインのビューウィンドウを作成
		_viewRight.Create(_wndSplitter, rcDefault, NULL,LVS_REPORT | LVS_SHOWSELALWAYS | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,WS_EX_CLIENTEDGE);
		_wndSplitter.SetSplitterPane(SPLIT_PANE_RIGHT,_viewRight);

		m_hWndClient = _wndSplitter;
		UpdateLayout();

		// 分割バーの位置を設定
		_wndSplitter.SetSplitterPos(250);

test98_09.gif
これで実行するとこのような左右2つの部分に分けられたウインドウになる。

test98_10.gif 上のままでは本当にエクスプローラのようなスタイルのウインドウになっているかどうかわかりづらいのでテスト用のコードをOnCreate内に追加する。
		_viewRight.SetViewType(LVS_REPORT);
		_viewRight.SetExtendedListViewStyle(LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT);
		_viewRight.InsertColumn(0, _T("カラム1"), LVCFMT_LEFT, 190, -1);
		_viewRight.AddItem(0,0,_T("アイテム1"));
		_viewRight.AddItem(1,0,_T("アイテム2"));


		_viewLeft.ModifyStyle(0,TVS_HASLINES);
		HTREEITEM	hItem;
		hItem = _viewLeft.InsertItem(_T("ルート1"),0,0,TVI_ROOT,TVI_ROOT);
		_viewLeft.InsertItem(_T("チャイルド1"),0,0,hItem,hItem);
		_viewLeft.InsertItem(_T("チャイルド2"),0,0,hItem,hItem);
		_viewLeft.Expand(hItem);

test98_11.gif
これで実行するときちんと左側がツリー、右側がリストビューのエクスプローラスタイルのウインドウになっていることが分かる。

プロジェクトファイルをダウンロード

2007年01月13日

日本語版WTL 8.0.6536のアプリケーションウィザード

wtl01.gif
■はじめに
WTLはライセンスがCPLのオープンソースプロジェクトとして公開されている。そのためおそらく勝手に日本語化したものを配布しても問題ないと思い用意しました。ライセンス的に問題があるようでしたらお知らせください。削除します。

wtl02.gif
■メリット
従来の英語版WTLではプロジェクト作成後にリソースエディタでメニューなどの言語を日本語に切り替えて、表示文字列を編集する必要がありました。しかしこの日本語版WTL 7.5ではプロジェクト生成直後から日本語リソースが利用可能です。

wtl03.gif
■日本語化内容:
 ・Windows用アプリケーションウィザード
 ・生成されるソースコード
 ・生成されるソースコードのコメント
など

※サンプルやWindows CE用のアプリケーションウィザードはダウンロード可能なファイルに含まれません。
※incldueファイルやreadme.html、cpl.txtは日本語化されていません。
※短時間で翻訳処理したため変な日本語になっている部分があります。

wtl04.gif
■ダウンロード


日本語版WTL 8.0.6536をダウンロード

wtl05.gif
右の画像はデフォルトで生成したアプリケーション例です。


WTLのリストビューで背景色を1行ごとに変える

test99_01.gif
ATL/WTLアプリケーションウイザードでプロジェクトを作成する際に、プロジェクトウイザードの3枚目のユーザーインターフェース機能の設定画面でビューウインドウの形式として「リストビュー」を選択する。

生成したソースコードの中でMainFrm.hのメッセージマップの末尾にREFLECT_NOTIFICATIONSを追加する。
	BEGIN_MSG_MAP(CMainFrame)
  (...省略...)
		REFLECT_NOTIFICATIONS()
	END_MSG_MAP()

次にビュークラスのメッセージマップに2行追加して下記のようにする。ここではプロジェクト名を「Test99」としたためクラス名が「CTest99View」になっている。
	BEGIN_MSG_MAP(CTest99View)
		REFLECTED_NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
	END_MSG_MAP()

さらに同じくビュークラスの中に新しいメンバー変数を追加する。
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		LRESULT	lRet = DefWindowProc();

		SetViewType(LVS_REPORT);
		SetExtendedListViewStyle(LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT);

		InsertColumn(0, _T("カラム"), LVCFMT_LEFT, 100, -1);
		AddItem(0,0,_T("アイテム1"));
		AddItem(1,0,_T("アイテム2"));
		AddItem(2,0,_T("アイテム3"));
		AddItem(3,0,_T("アイテム4"));
		AddItem(4,0,_T("アイテム5"));

		return	lRet;
	}

	LRESULT OnCustomDraw(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
	{
		LPNMLVCUSTOMDRAW	pCustomDraw;

		pCustomDraw = (LPNMLVCUSTOMDRAW)pnmh;

		switch(pCustomDraw->nmcd.dwDrawStage)
		{
		case	CDDS_PREPAINT:
			return	CDRF_NOTIFYITEMDRAW;

		case	CDDS_ITEMPREPAINT:
			if(pCustomDraw->nmcd.dwItemSpec % 2)
			{
				pCustomDraw->clrTextBk = RGB(250, 240, 150);
				pCustomDraw->clrText = RGB(0, 0, 200);
				return CDRF_NEWFONT;
			}
		}

		bHandled = FALSE;
		return	0;
	}


test99_02.gif
これで実行するとアイテムが1行ごとに背景色が変わるリストビューとなる。

プロジェクトファイルをダウンロード

WTLでリストビューをオーナードローする

test100_01.gif
ATL/WTLアプリケーションウイザードでプロジェクトを作成する際に、プロジェクトウイザードの3枚目のユーザーインターフェース機能の設定画面でビューウインドウの形式として「リストビュー」を選択する。

生成したソースコードの中でMainFrm.hのメッセージマップの末尾にかなりトリッキーな方法だがWM_DRAWITEMメッセージをビューで処理するためのコードを追加する。
	BEGIN_MSG_MAP(CMainFrame)
(...省略...)
		{
			if(uMsg == WM_DRAWITEM && ((DRAWITEMSTRUCT*)lParam)->hwndItem == m_view.m_hWnd)
				return	m_view.ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID);
		}
	END_MSG_MAP()

OnCreate内にあるビュークラスの生成部分でウインドウスタイルとしてLVS_OWNERDRAWFIXEDを追加する。
		m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, LVS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE);

さらにOnCreate内にテスト用のカラムとアイテムを追加するコードを入れる。
		m_view.InsertColumn(0, _T("カラム1"), LVCFMT_LEFT, 190, -1);
		m_view.InsertColumn(0, _T("カラム2"), LVCFMT_LEFT, 190, -1);
		m_view.AddItem(0,0,_T("アイテム1"));
		m_view.AddItem(0,1,_T("アイテム1あああああああああああああああああ"));
		m_view.AddItem(1,0,_T("アイテム2"));
		m_view.AddItem(1,1,_T("アイテム2あああああああああああああああああ"));

次にビュークラスの先頭にインクルード宣言を追加する。
#include "atlstr.h"

同じくビュークラス内のメッセージマップにWM_DRAWITEMメッセージの処理用ハンドラーを追加する。
	BEGIN_MSG_MAP(CTest100View)
		MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)
	END_MSG_MAP()

最後にビュークラス内にオーナードロー用のコードを追加する。ここではイメージリストなどの複雑な処理は一切省き、単にカラム内の文字列を表示するだけの処理にしている。
	LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
	{
		LPDRAWITEMSTRUCT	pDrawItem;

		pDrawItem = (LPDRAWITEMSTRUCT)lParam;

		int		i;
		int		nSize;
		BSTR	bstrText;
		BOOL	ret;
		RECT	rect;
		HBRUSH		hBrush;
		COLORREF	clrOldBack;
		COLORREF	clrOldText;

		nSize = GetHeader().GetItemCount();
		for(i = 0; i < nSize; i++)
		{
			//描画文字列の取得(BSTRとして取得)
			bstrText = NULL;
			ret = GetItemText(pDrawItem->itemID,i,bstrText);
			if(ret == FALSE)
				continue;

			//描画領域を取得
			GetHeader().GetItemRect(i,&rect);
			rect.top	= pDrawItem->rcItem.top;
			rect.bottom	= pDrawItem->rcItem.bottom;

			//アイテム選択時処理
			if(pDrawItem->itemState & ODS_SELECTED)
			{
				clrOldBack = ::SetBkColor(pDrawItem->hDC,::GetSysColor(COLOR_HIGHLIGHT));
				clrOldText = ::SetTextColor(pDrawItem->hDC,::GetSysColor(COLOR_HIGHLIGHTTEXT));
				hBrush = ::GetSysColorBrush(COLOR_HIGHLIGHT);
				::FillRect(pDrawItem->hDC,&rect,hBrush);
			}

			//文字の描画
			::DrawText(pDrawItem->hDC,(CAtlString)(WCHAR*)bstrText,-1,&rect,DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS);

			//BSTRの解放
			::SysFreeString(bstrText);


			//アイテム選択時処理
			if(pDrawItem->itemState & ODS_SELECTED)
			{
				::SetBkColor(pDrawItem->hDC,clrOldBack);
				::SetTextColor(pDrawItem->hDC,clrOldText);
				::DeleteObject(hBrush);
			}
		}

		return	0;
	}

test100_02.gif
これで実行すると見た目には普通のリストビューが表示される。しかし内部的にはオーナードローにて描画されている。

プロジェクトファイルをダウンロード

WTLでリストビューでアイテムの高さを指定する

前回作成した「WTLでリストビューをオーナードローする 」を元にリストビューの行サイズを指定できるようにする。

まずMainFrm.hのメッセージマップでWM_MEASUREITEMメッセージをビュー内で処理するように指定する。
			if(uMsg == WM_MEASUREITEM && ((LPMEASUREITEMSTRUCT)lParam)->CtlType == ODT_LISTVIEW)
				return	m_view.ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID);

そしてビュークラス内のメッセージマップにメッセージハンドラを追加する。
	BEGIN_MSG_MAP(CTest100View)
(...省略...)
		MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)
	END_MSG_MAP()

最後にビュークラスにメンバー関数を追加する。
	LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		LPMEASUREITEMSTRUCT lpMeasureItemStruct = (LPMEASUREITEMSTRUCT)lParam;

		//行の高さを指定
		lpMeasureItemStruct->itemHeight = 100;
		return	0;
	}

test101.gif
要はWM_MEASUREITEMでitemHeightに新しい行の高さを代入するだけでいい。

プロジェクトファイルをダウンロード

1  2





usefullcode@gmail.com

About 2007年01月

2007年01月にブログ「UsefullCode.net」に投稿されたすべてのエントリーです。

前の記事は2006年12月です。

次の記事は2007年02月です。

他にも多くのエントリーがあります。メインページ記事一覧も見てください。