« 2007年04月 | メイン | 2009年04月 »

1  2

2009年03月 記事一覧

2009年03月18日

スタックを実現するクラス

CAtlListを使えば簡単にスタックを作ることができる。

ここではテンプレートクラスとして作成したため、スタック化する変数の型は自由に設定できる。



ちなみにこのクラスは画像をスライドショー表示するユーティリティを作ったときに作ったクラス。表示した画像を順次スタックに入れておき、ユーザーが「戻る」ボタンを押したときにスタックから画像を取り出して表示する、、、というようにすることで履歴機能を作成した。また、24時間連続で表示するようなスライドショーなため、無限に画像をスタックに入れておくとメモリー消費量が大変なことになる。そのためスタックへ追加する最大数をコンストラクタで設定できるようにした。



■使用例
int型のスタックを用意して「1 2 3 4 5」の順番でデータを追加。順番に取り出すと「5 4 3 2 1」という順でデータが取り出せる。
#include "DnpStack.h"

void	Test()
{
	int		n;
	bool	ret;
	CDnpStack<int>	cStack;		//ここを「cStack(3)」にして最大数を3に設定すると出力は「5 4 3」になる

	cStack.AddData(1);		//データ追加、1,2,3,4,5の順に追加
	cStack.AddData(2);
	cStack.AddData(3);
	cStack.AddData(4);
	cStack.AddData(5);

	do
	{
		ret = cStack.GetData(n);
		if(ret)
		{
			ATLTRACE("%d ",n);
		}
	}
	while(ret);
	ATLTRACE("\n");
}




■以下が「DnpStack.h」の内容

依存環境:ATL
#pragma	once

#include "atlcoll.h"

/*!
 * \brief
 * スタックテンプレートクラス
 * 
 * データ格納順序を「A->B->C->D」とすると、
 * 「D->C->B->A」の順で取得できる。
 * 
 * 以下のコードでは「5 4 3 2 1」と出力される
 * \code
	int		n;
	bool	ret;
	CDnpStack<int>	cStack;		//ここを「cStack(3)」にして最大数を3に設定すると出力は「5 4 3」になる

	cStack.AddData(1);		//データ追加
	cStack.AddData(2);
	cStack.AddData(3);
	cStack.AddData(4);
	cStack.AddData(5);

	do
	{
		ret = cStack.GetData(n);
		if(ret)
		{
			ATLTRACE("%d ",n);
		}
	}
	while(ret);
	ATLTRACE("\n");
 * \endcode
*/
template<class VALUE>
class CDnpStack
{
	UINT		_nMaxSize;

public:
	CAtlList<VALUE>	_listData;			//!< スタック用内部データ


	/*!
	 * \brief
	 * コンストラクタ(最大保持サイズの設定可能)
	 * 
	 * \param[in]	nMaxSize
	 * データ最大保持サイズ(0なら無制限)
	 * 
	 * nMaxSizeに3を設定して、「A->B->C->D」の順で格納すると、
	 * 「D->C->B」の順で取得できる。「A」のデータは内部で削除される。
	 * 
	 */
	CDnpStack(int nMaxSize = 0)
	{
		_nMaxSize = nMaxSize;
	}

	virtual ~CDnpStack()
	{
	}


	/*!
	 * \brief
	 * スタックにデータ追加
	 * 
	 * \param[in]	data
	 * 追加するデータ
	 * 
	 */
	void	AddData(VALUE data)
	{
		_listData.AddTail(data);

		//古いデータ削除(ポインタだとメモリリークの可能性あり)
		if(_nMaxSize > 0 && _listData.GetCount() > _nMaxSize)
			_listData.RemoveHead();
	};


	/*!
	 * \brief
	 * スタックからデータを取り出す
	 * 
	 * \param[out]	data
	 * 取りだしたデータ
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	GetData(VALUE& data)
	{
		if(_listData.GetCount() > 0)
			data = _listData.RemoveTail();
		else
		{
//			data = NULL;
			return	false;
		}
		return	true;
	};


	/*!
	 * \brief
	 * スタックからデータを取り出す
	 * 
	 * \return	取りだしたデータ
	 * 
	 * \attention
	 * データ取得に失敗した場合、どのようなデータが返るかわからないので
	 * 安全のために CDnpStack<VALUE>::GetData(VALUE&) を利用すべき
	 */
	VALUE	GetData(void)
	{
		VALUE	ret;
		GetData(ret);
		return	ret;
	};


	/*!
	 * \brief
	 * 先頭のデータを取得する(GetDataのように内部データの削除はしない)
	 * 
	 * \param[out]	data
	 * 取りだしたデータ
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 */
	bool	ReferData(VALUE& data)
	{
		if(_listData.GetCount() > 0)
			data = _listData.GetTail();
		else
		{
//			data = NULL;
			return	false;
		}
		return	true;
	}



	/*!
	 * \brief
	 * 先頭のデータを取得する(GetDataのように内部データの削除はしない)
	 * 
	 * \return	取りだしたデータ
	 * 
	 * \attention
	 * データ取得に失敗した場合、どのようなデータが返るかわからないので
	 * 安全のために CDnpStack<VALUE>::ReferData(VALUE&) を利用すべき
	 */
	VALUE	ReferData(void)
	{
		VALUE	ret;
		ReferData(ret);
		return	ret;
	}



	/*!
	 * \brief
	 * スタックに格納されているデータ数取得
	 * 
	 * \return
	 * スタックに格納されているデータ数
	 */
	int		GetSize(void)
	{
		return	(int)_listData.GetCount();
	};


	/*!
	 * \brief
	 * 内部データの全削除
	 */
	void	RemoveAll(void)
	{
		_listData.RemoveAll();
	};
};
プロジェクトファイルをダウンロード

キューを実現するクラス

CAtlListを使えば簡単にキューを作ることができる。

ここではテンプレートクラスとして作成したため、キュー化する変数の型は自由に設定できる。

■使用例
int型のキューを用意して「1 2 3 4 5」の順番でデータを追加。順番に取り出すと「1 2 3 4 5」という順でデータが取り出せる。
#include "DnpQue.h"

void	Test()
{
	int		n;
	bool	ret;
	CDnpQue<int>	cQue;

	cQue.AddData(1);		//データ追加
	cQue.AddData(2);
	cQue.AddData(3);
	cQue.AddData(4);
	cQue.AddData(5);

	do
	{
		ret = cQue.GetData(n);
		if(ret)
		{
			ATLTRACE("%d ",n);
		}
	}
	while(ret);
}


■以下が「DnpQue.h」の内容

依存環境:ATL
#pragma	once

#include "atlcoll.h"


/*!
 * \brief
 * キューテンプレートクラス
 * 
 * 先に入れたデータが先に出力される。
 * 
 * 以下のコードでは「1 2 3 4 5」が出力する。
 * \code
	int		n;
	bool	ret;
	CDnpQue<int>	cQue;

	cQue.AddData(1);		//データ追加
	cQue.AddData(2);
	cQue.AddData(3);
	cQue.AddData(4);
	cQue.AddData(5);

	do
	{
		ret = cQue.GetData(n);
		if(ret)
		{
			ATLTRACE("%d ",n);
		}
	}
	while(ret);
 * \endcode
 */
template<class VALUE>
class CDnpQue
{

public:
	CDnpQue()
	{
	}

	virtual ~CDnpQue()
	{
	}

	CAtlList<VALUE>	_listData;		//!< 保持しているキューデータ


	/*!
	 * \brief
	 * キューにデータ追加
	 * 
	 * \param	data
	 * 追加するデータ
	 * 
	 */
	void	AddData(VALUE data)
	{
		_listData.AddTail(data);
	};


	/*!
	 * \brief
	 * キューの先頭にデータ追加
	 * 
	 * \param	data
	 * 追加するデータ
	 * 
	 * \remarks
	 * 通常のキュー動作を無視して先頭にデータを挿入する。
	 * 挿入したデータは次回の CDnpQue<VALUE>::GetData(VALUE&) により取得される。
	 */
	void	AddPrimeData(VALUE data)
	{
		_listData.AddHead(data);
	};




	/*!
	 * \brief
	 * キューからデータ取得
	 * 
	 * \param[out]	data
	 * 取得したデータ
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	GetData(VALUE& data)
	{
		if(_listData.GetCount() > 0)
			data = _listData.RemoveHead();
		else
		{
//			data = NULL;
			return	false;
		}
		return	true;
	};


	/*!
	 * \brief
	 * キューからデータ取得
	 * 
	 * \return
	 * 取得したデータ
	 * 
	 * \attention
	 * データがなかった場合に何が返るか不定になるので、この関数は使わずに
	 * CDnpQue<VALUE>::GetData(VALUE&) を利用するべき。
	 */
	VALUE	GetData(void)
	{
		VALUE	ret;
		GetData(ret);
		return	ret;
	};


	/*!
	 * \brief
	 * キューデータを見る(GetDataのように削除はしない)
	 * 
	 * \param[out]	data
	 * 取得したデータ
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 */
	bool	ReferData(VALUE& data)
	{
		if(_listData.GetCount() > 0)
			data = _listData.GetHead();
		else
		{
//			data = NULL;
			return	false;
		}
		return	true;
	}


	/*!
	 * \brief
	 * キューデータを見る(GetDataのように削除はしない)
	 * 
	 * \return
	 * 取得したデータ
	 * 
	 * \attention
	 * データがなかった場合に何が返るか不定になるので、この関数は使わずに
	 * CDnpQue<VALUE>::ReferData(VALUE&) を利用するべき。
	 */
	VALUE	ReferData(void)
	{
		VALUE	ret;
		ReferData(ret);
		return	ret;
	}



	/*!
	 * \brief
	 * 現在保持しているデータ数を返す
	 * 
	 * \return
	 * データ数
	 */
	int		GetSize(void)
	{
		return	(int)_listData.GetCount();
	};


	/*!
	 * \brief
	 * 現在保持している全データを削除
	 * 
	 */
	void	RemoveAll(void)
	{
		_listData.RemoveAll();
	};
};

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

ウインドウの位置情報を保存/復元するクラス

前回閉じたときと同じ場所にウインドウを開きたいと思うことがしばしばある。そういうときに使うためのクラス。

ウインドウ位置の取得、復元、レジストリへの書き込み、レジストリからの読み込みなどに対応する。


■ウインドウの位置を保存したいときは WM_DESTROY などが呼ばれたときに以下のように呼ぶ。
	CDnpWindowPlacement	cPlacement;
	cPlacement.GetCurrentPos(m_hWnd);
	cPlacement.SaveToReg(HKEY_CURRENT_USER,_T("software\\dinop\\test"),_T("WindowPlacement"));


■ウインドウの位置を復元したいときは CreateWindow によるウインドウ生成処理が終わった後で以下のように呼ぶ。
	CDnpWindowPlacement	cPlacement;
	cPlacement.LoadFromReg(HKEY_CURRENT_USER,_T("software\\dinop\\test"),_T("WindowPlacement"));
	cPlacement.Restore(hWnd);


依存環境:ATL
#pragma	once


#include "atlfile.h"
#include "atlstr.h"


/*!
 * \brief
 * ウインドウ位置取得/復元クラス
 * 
 * アプリケーション終了時の位置を保存しておく用途を想定
 * 
 * \remarks
 * APIのGetWindowPlacecement/SetWindowPlacecementにより
 *ウインドウ位置の取得と復元を行う
 * 
 * \attention
 * GetMonitorInfoを利用しているためWindows 2000以上必須
 * 
 */
class	CDnpWindowPlacement
{
public:
	WINDOWPLACEMENT	_sWindowPlacement;		//!< ウインドウの位置情報


	CDnpWindowPlacement()
	{
		::ZeroMemory(&_sWindowPlacement,sizeof(WINDOWPLACEMENT));
		//IsValid()で_sWindowPlacement.lengthを利用しているので
		//ここでそのメンバを初期化しない
	}




	/*!
	 * \brief
	 * このクラスに正常な情報が入っているかどうかのチェック
	 * 
	 * \retval	true	保持しているWINDOWPLACEMENT構造体に正しい情報が入っている
	 * \retval	false	正しい情報を保持していない
	 * 
	 * \todo
	 * もう少しチェックした方がいい?
	 */
	bool	IsValid(void)	const
	{
		if(_sWindowPlacement.length != sizeof(WINDOWPLACEMENT))
			return	false;

		return	true;
	}



	/*!
	 * \brief
	 * 指定したウインドウの位置情報をクラス内に読み込む
	 * 
	 * \param hWnd
	 * 位置情報を読み込みたいウインドウのHWND
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	GetCurrentPos(HWND hWnd)
	{
		BOOL	ret;

		::ZeroMemory(&_sWindowPlacement,sizeof(WINDOWPLACEMENT));
		_sWindowPlacement.length = sizeof(WINDOWPLACEMENT);

		ret = ::GetWindowPlacement(hWnd,&_sWindowPlacement);
		if(ret == FALSE)
		{
			::ZeroMemory(&_sWindowPlacement,sizeof(WINDOWPLACEMENT));
			return	false;
		}

		return	true;
	}






	/*!
	 * \brief
	 * ウインドウの位置情報を指定されたウインドウに適用
	 * 
	 * \param hWnd
	 * 
	 * \param bMinimizeToRestore
	 * 位置情報保存時に最小化されていた場合の処理指定\n
	 * trueなら最小化されたウインドウは元に戻して表示\n
	 * falseなら最小化されたウインドウは最小化して表示\n
	 * 
	 * \param bNotModifyPlace
	 * trueならウインドウ位置は保存されていた情報通りに復元\n
	 * falseのときはウインドウ位置が変な場合は適宜変更する\n
	 * 
	 * \param prectDefault
	 * bNotModifyPlace==trueのときは無視される。\n
	 * prectDefaultが設定されていないときで、かつ、、、\n
	 * 復元したときにタイトルバーが一部しか見えない場合は、タイトルバーが見えるように上下移動\n
	 * 復元したときにウインドウが全然見えない場合は、占有モニターの左上に移動する\n
	 * prectDefaultが設定されている場合はprectDefaultに復元箇所を変更する
	 * 
	 * \attention
	 * OnCreate(WM_CREATE処理)内では利用できない!
	 * 起動時に処理したい場合はCreateWindowを呼んだ後もしくは
	 * CreateWindowを派生するか、何度も実行しないようにフラグ
	 * 変数を用意した上で何かのメッセージ処理時に利用する
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	Restore(HWND hWnd,bool bMinimizeToRestore=true,bool bNotModifyPlace=false,const RECT* prectDefault=NULL)	const
	{
		BOOL			ret;
		WINDOWPLACEMENT	sWorkWP;			//作業用のWINDOWPLACEMENT

		if(IsValid() == false)
			return	false;

		sWorkWP = _sWindowPlacement;

		if(bNotModifyPlace == false)
		{
			HMONITOR	hMonitor;
			MONITORINFO	sMonitorInfo;

			hMonitor = ::MonitorFromRect(&sWorkWP.rcNormalPosition,MONITOR_DEFAULTTONEAREST);
			if(hMonitor)
			{
				bool	bModify;
				LONG	cx;
				LONG	cy;
				RECT	rect;

				bModify = false;

				::ZeroMemory(&sMonitorInfo,sizeof(MONITORINFO));
				sMonitorInfo.cbSize = sizeof(MONITORINFO);

				ret = ::GetMonitorInfo(hMonitor,&sMonitorInfo);
				if(ret)
				{
					//ウインドウの上部10ピクセル(タイトルバー全体とは言わないま
					//でも、その一部)が見えるかどうかのチェック
					if(bModify == false)
					{
						RECT	rectUp;

						rectUp = sWorkWP.rcNormalPosition;
						rectUp.bottom = (rectUp.top + 10 < rectUp.bottom) ? rectUp.top + 10 : rectUp.bottom;

						ret = IntersectRect(&rect,&rectUp,&sMonitorInfo.rcMonitor);
						if(ret == FALSE)
						{
							//復元場所はタイトルバーが表示されない位置にある

							//サイズは変更せずにウインドウ上部が見える位置に上下移動
							cy = sWorkWP.rcNormalPosition.bottom - sWorkWP.rcNormalPosition.top;
							sWorkWP.rcNormalPosition.top		= sMonitorInfo.rcMonitor.top;
							sWorkWP.rcNormalPosition.bottom	= sWorkWP.rcNormalPosition.top + cy;
							bModify = true;
						}
					}

					//画面全体のRECTとウインドウのRECTの重複部分領域を調べ
					//きちんと表示されるかをチェック
					if(bModify == false)
					{
						ret = IntersectRect(&rect,&sWorkWP.rcNormalPosition,&sMonitorInfo.rcMonitor);

						if(ret == FALSE)
						{
							//復元場所はウインドウがまったく見えない位置にある

							//サイズは変更せずにモニターの左上に移動
							cx = sWorkWP.rcNormalPosition.right - sWorkWP.rcNormalPosition.left;
							cy = sWorkWP.rcNormalPosition.bottom - sWorkWP.rcNormalPosition.top;
							sWorkWP.rcNormalPosition.left		= sMonitorInfo.rcMonitor.left;
							sWorkWP.rcNormalPosition.top		= sMonitorInfo.rcMonitor.top;
							sWorkWP.rcNormalPosition.right		= sWorkWP.rcNormalPosition.left + cx;
							sWorkWP.rcNormalPosition.bottom		= sWorkWP.rcNormalPosition.top + cy;
							bModify = true;
						}
					}

					//ウインドウの移動が必要な場合で、かつ、prectDefaultが設定されていたら
					//デフォルト表示位置をprectDefaultに変える
					if(bModify && prectDefault)
						sWorkWP.rcNormalPosition = *prectDefault;

					//bModify == falseでも表示できる
				}
			}
		}

		if(bMinimizeToRestore)
		{
			//最小化された状態の位置データが保存されていた
			//「最小化」して表示ではなく、「元の状態に戻す」をして表示する

			switch(sWorkWP.showCmd)
			{
			case	SW_MINIMIZE:
			case	SW_SHOWMINIMIZED:
			case	SW_SHOWMINNOACTIVE:
			case	SW_FORCEMINIMIZE:
			   if(sWorkWP.flags & WPF_RESTORETOMAXIMIZED)
					sWorkWP.showCmd = SW_SHOWMAXIMIZED;
			   else
					sWorkWP.showCmd = SW_RESTORE;
			   break;
			}
		}

		ret = ::SetWindowPlacement(hWnd,&sWorkWP);

		return	ret ? true : false;
	}



	/*!
	 * \brief
	 * ファイルに位置情報を保存(テスト用関数)
	 * 
	 * \param pszFile
	 * 保存するファイルパス
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 * \remarks
	 * これはテスト用の関数です。実用性ゼロ。
	 * 
	 */
	bool	SaveFile(LPCTSTR pszFile)	const
	{
		CAtlFile	cFile;
		HRESULT		hr;
		DWORD		dwWritten;

		if(IsValid() == false)
			return	false;

		hr = cFile.Create(pszFile,GENERIC_WRITE,FILE_SHARE_READ,CREATE_ALWAYS);
		if(FAILED(hr))
			return	false;

		dwWritten = 0;
		cFile.Write(&_sWindowPlacement,sizeof(WINDOWPLACEMENT),&dwWritten);

		cFile.Close();

		if(dwWritten != sizeof(WINDOWPLACEMENT))
			return	false;

		return	true;
	}




	/*!
	 * \brief
	 * ファイルから位置情報を読み込む(テスト用関数)
	 * 
	 * \param pszFile
	 * 保存されているファイルパス
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 * \remarks
	 * これはテスト用の関数です。実用性ゼロ。
	 * 読み込んだ位置情報はクラス内に保持され、 CDnpWindowPlacement::Restore() 関数でウインドウを
	 * 復元できます。
	 * 
	 */
	bool	ReadFile(LPCTSTR pszFile)
	{
		CAtlFile	cFile;
		HRESULT		hr;
		DWORD		dwRead;

		WINDOWPLACEMENT	sTmp;

		hr = cFile.Create(pszFile,GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING);
		if(FAILED(hr))
			return	false;

		dwRead = 0;
		cFile.Read(&sTmp,sizeof(WINDOWPLACEMENT),dwRead);

		cFile.Close();

		if(dwRead != sizeof(WINDOWPLACEMENT))
			return	false;

		if(sTmp.length != sizeof(WINDOWPLACEMENT))
			return	false;

		::memcpy_s(&_sWindowPlacement,sizeof(WINDOWPLACEMENT),&sTmp,sizeof(WINDOWPLACEMENT));

		return	true;
	}



	/*!
	 * \brief
	 * 位置情報のレジストリへの書き込み
	 * 
	 * このクラス内に保持する位置情報を書き込む。事前に GetCurrentPos() を実行して位置情報を取得しておく必要あり。
	 * 
	 * \param[in]	hRootKey
	 * 書き込むレジストリのルート種類。\n
	 * HKEY_CURRENT_USER を推奨
	 * 
	 * \param[in]	pszSubKey
	 * 書き込むレジストリの場所。\n
	 * 「software\\dinop\\test\\」のようにする。
	 * 
	 * \param[in]	pszName
	 * 位置情報を書き込むときのレジストリの名前。たとえば「WindowPlacement」のようにする。
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	SaveToReg(HKEY hRootKey,LPCTSTR pszSubKey,LPCTSTR pszName)
	{
		LONG	ret;
		CRegKey	cReg;

		if(IsValid() == false)
			return	false;

		ret = cReg.Create(hRootKey,pszSubKey);
		if(ret != ERROR_SUCCESS)
			return	false;

		ret = cReg.Open(hRootKey,pszSubKey,KEY_WRITE);
		if(ret != ERROR_SUCCESS)
			return	false;

		ret = cReg.SetBinaryValue(pszName,&_sWindowPlacement,sizeof(WINDOWPLACEMENT));

		cReg.Close();

		if(ret != ERROR_SUCCESS)
			return	false;

		return	true;

	}



	/*!
	 * \brief
	 * 位置情報のレジストリからの読み込み
	 * 
	 * あくまでもこのクラス内に位置情報を読み込むだけで、実際のウインドウの位置は変更しない。
	 * 
	 * \param[in]	hRootKey	SaveToReg() で書き込むときに指定したのと同じ値を指定
	 * \param[in]	pszSubKey	SaveToReg() で書き込むときに指定したのと同じ値を指定
	 * \param[in]	pszName		SaveToReg() で書き込むときに指定したのと同じ値を指定
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	LoadFromReg(HKEY hRootKey,LPCTSTR pszSubKey,LPCTSTR pszName)
	{
		LONG	ret;
		CRegKey	cReg;
		ULONG	nBytes;

		ret = cReg.Open(hRootKey,pszSubKey,KEY_READ);
		if(ret != ERROR_SUCCESS)
			return	false;

		nBytes = sizeof(WINDOWPLACEMENT);
		ret = cReg.QueryBinaryValue(pszName,&_sWindowPlacement,&nBytes);

		cReg.Close();

		if(ret != ERROR_SUCCESS || nBytes != sizeof(WINDOWPLACEMENT) || IsValid() == false)
		{
			::ZeroMemory(&_sWindowPlacement,sizeof(WINDOWPLACEMENT));
			return	false;
		}

		return	true;

	}







	/*!
	 * \brief
	 * 16進文字列として位置情報を返す
	 * 
	 * \param strData
	 * 取得した位置情報文字列
	 * 
	 * \remarks
	 * WINDOWPLACEMENT構造体をASCIIのHEX文字列でベタに返している
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	GetFormatedData(CAtlString& strData)	const
	{
		bool	ret;
		TCHAR	pszTmp[sizeof(WINDOWPLACEMENT) * 2 + 10];

		ret = GetFormatedData(pszTmp,sizeof(WINDOWPLACEMENT) * 2 + 10);
		if(ret == false)
			return	false;

		strData = pszTmp;

		return	true;

	}



	/*!
	 * \brief
	 * 16進文字列として位置情報を返す
	 * 
	 * \param pszData
	 * 取得した位置情報文字列を格納するバッファ
	 * 
	 * \param cchLength
	 * 取得した位置情報文字列を格納するバッファの文字数
	 * (sizeof(WINDOWPLACEMENT) * 2 + 1)以上必要
	 * 
	 * \remarks
	 * WINDOWPLACEMENT構造体をASCIIのHEX文字列でベタに返している
	 * バッファーサイズの指定ミスを減らすため CDnpWindowPlacement::GetFormatedData() を利用すべき
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	GetFormatedData(TCHAR* pszData,int cchLength)	const
	{
		int		i;
		bool	ret;
		TCHAR*	pszTmp;
		BYTE*	pcbData;

		if(pszData == NULL)
			return	false;

		*pszData = NULL;
		if(cchLength < sizeof(WINDOWPLACEMENT) * 2 + 1)
			return	false;

		if(IsValid() == false)
			return	false;

		pszTmp = pszData;
		pcbData = (BYTE*)&_sWindowPlacement;
		for(i = 0; i < sizeof(WINDOWPLACEMENT); i++)
		{
			ret = BinToHex(pcbData[i],pszTmp[0],pszTmp[1]);
			pszTmp += 2;
			if(ret)
				continue;

			*pszData = NULL;
			return	false;
		}
		*pszTmp = NULL;

		return	true;
	}




	/*!
	 * \brief
	 * 16進文字列の位置情報をクラス内に読み込む
	 * 
	 * \param pszData
	 * 読み込む位置情報
	 * 
	 * \retval	true	成功
	 * \retval	false	失敗
	 * 
	 */
	bool	SetFormatedData(LPCTSTR pszData)
	{
		int		i;
		bool	ret;
		LPCTSTR	pszTmp;
		BYTE*	pcbData;

		if(pszData == NULL)
			return	false;

		if(::_tcslen(pszData) != sizeof(WINDOWPLACEMENT) * 2)
			return	false;

		pszTmp = pszData;
		pcbData = (BYTE*)&_sWindowPlacement;
		for(i = 0; i < sizeof(WINDOWPLACEMENT); i++)
		{
			ret = HexToBin(pcbData[i],pszTmp[0],pszTmp[1]);
			pszTmp += 2;
			if(ret)
				continue;

			::ZeroMemory(&_sWindowPlacement,sizeof(WINDOWPLACEMENT));
			return	false;
		}

		if(IsValid())
			return	true;

		::ZeroMemory(&_sWindowPlacement,sizeof(WINDOWPLACEMENT));

		return	false;
	}





private:



	//
	//	ウインドウの重複部分の取得
	//
	//	APIと異なりNormalizeRectされていない場合も失敗しない(内部で正規化しているだけ)
	//
	BOOL	IntersectRect(LPRECT lprcDst,const RECT* lprcSrc1,const RECT* lprcSrc2)	const
	{
		if(lprcDst == NULL || lprcSrc1 == NULL || lprcSrc2 == NULL)
		{
			if(lprcDst)
			{
				lprcDst->left	= 0;
				lprcDst->right	= 0;
				lprcDst->top	= 0;
				lprcDst->bottom	= 0;
			}

			return	FALSE;
		}

		RECT	src1;
		RECT	src2;

		src1 = *lprcSrc1;
		src2 = *lprcSrc2;

		NormalizeRect(&src1);
		NormalizeRect(&src2);

		lprcDst->left	= (src1.left > src2.left) ? src1.left : src2.left;
		lprcDst->right	= (src1.right < src2.right) ? src1.right : src2.right;
		lprcDst->top	= (src1.top > src2.top) ? src1.top : src2.top;
		lprcDst->bottom	= (src1.bottom < src2.bottom) ? src1.bottom : src2.bottom;

		if(lprcDst->left >= lprcDst->right || lprcDst->top >= lprcDst->bottom)
		{
			lprcDst->left	= 0;
			lprcDst->right	= 0;
			lprcDst->top	= 0;
			lprcDst->bottom	= 0;

			return	FALSE;
		}

		return	TRUE;
	}


	//
	//	RECTの正規化
	//
	void	NormalizeRect(LPRECT lprc)	const
	{
		if(lprc == NULL)
			return;

		LONG	tmp;

		if(lprc->left > lprc->right)
		{
			tmp = lprc->left;
			lprc->left = lprc->right;
			lprc->right = tmp;
		}

		if(lprc->top > lprc->bottom)
		{
			tmp = lprc->top;
			lprc->top = lprc->bottom;
			lprc->bottom = tmp;
		}
	}



	//
	//	1バイトのバイナリを16進数文字の2文字にする
	//
	bool	BinToHex(BYTE cb,TCHAR& cUpper,TCHAR& cLower)	const
	{
		BYTE	tmp;

		tmp = cb >> 4;
		if(tmp < 10)
			cUpper = _T('0') + tmp;
		else
			cUpper = _T('A') + tmp - 10;

		tmp = cb & 0x0F;
		if(tmp < 10)
			cLower = _T('0') + tmp;
		else
			cLower = _T('A') + tmp - 10;

		return	true;
	}


	//
	//	16進数文字の2文字を1バイトのバイナリにする
	//
	bool	HexToBin(BYTE& cb,const TCHAR& cUpper,const TCHAR& cLower)	const
	{
		BYTE	tmp;

		if(cUpper >= _T('0') && cUpper <= _T('9'))
			tmp = (BYTE)cUpper - _T('0');
		else if(cUpper >= _T('A') && cUpper <= _T('F'))
			tmp = (BYTE)cUpper - _T('A') + 10;
		else if(cUpper >= _T('a') && cUpper <= _T('f'))
			tmp = (BYTE)cUpper - _T('a') + 10;
		else
			return	false;
		cb = tmp << 4;

		if(cLower >= _T('0') && cLower <= _T('9'))
			tmp = (BYTE)cLower - _T('0');
		else if(cLower >= _T('A') && cLower <= _T('F'))
			tmp = (BYTE)cLower - _T('A') + 10;
		else if(cLower >= _T('a') && cLower <= _T('f'))
			tmp = (BYTE)cLower - _T('a') + 10;
		else
			return	false;
		cb += tmp;

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

DLLファイル名からCLSIDを取得する

詳細が公開されていないソフト内のインターフェースをホストしたい場合など、DLL内にあるCLSIDを取得したいことがごくまれにある。

LoadTypeLib でDLLを読み込みITypeLibを取得してから、ITypeIfo::GetTypeAttr()を使うことで取得できる。


下の使用例では、Windows標準のシェル関連DLL「shdocvw.dll」を読み込み、GUIDのCLSIDからプログラムIDを取得、出力した。
Shell.Explorer.1
Shell.Explorer.2
InternetExplorer.Application.1
Shell.UIHelper.1
ShellNameSpace.ShellNameSpace.1
ShellShellNameSpace.ShellShellNameSpace.1


bool	Test()
{
	bool	ret;
	size_t	i;
	size_t	nSize;
	HRESULT	hr;
	WCHAR*	pwszProgID;

	CAtlArray<GUID>		aguidCoClass;

	//shdocvw.dll内のCLSIDを取得
	ret = CLSIDFromDLLFile(_T("c:\\windows\\system32\\shdocvw.dll"),aguidCoClass);
	if(ret == false)
		return	false;

	nSize = aguidCoClass.GetCount();
	for(i = 0; i < nSize; i++)
	{
		//GUIDからプログラムIDの文字列に変換してATLTRACEで出力
		pwszProgID = NULL;
		hr =  ::ProgIDFromCLSID(aguidCoClass[i],&pwszProgID);
		if(SUCCEEDED(hr))
		{
			ATLTRACE("%s\n",(CAtlString)pwszProgID);
			::CoTaskMemFree(pwszProgID);
		}
	}

	return	true;
}






#include "atlstr.h"
#include "atlcoll.h"


/*!
 * \brief
 * DLL中のCLSIDを取得する
 * 
 * \param[in]	pszFile
 * タイプライブラリをチェックするDLLファイルのパス
 * 
 * \param[out]	aguidCoClass
 * COCLASSとして登録されているGUIDの配列
 * 
 * \retval	true	成功(1つ以上取得できた)
 * \retval	false	失敗(1つも取得できなかった)
 * 
 */
bool	CLSIDFromDLLFile(LPCTSTR pszFile,CAtlArray<GUID>& aguidCoClass)
{
	UINT		i;
	UINT		nCount;
	HRESULT		hr;
	TYPEATTR*	pTypeAttr;

	CComPtr<ITypeLib>	pITypeLib;
	CComPtr<ITypeInfo>	pITypeInfo;

	aguidCoClass.RemoveAll();

	hr = ::LoadTypeLib((CAtlStringW)pszFile,&pITypeLib);
	if(FAILED(hr))
		return	false;

	nCount = pITypeLib->GetTypeInfoCount();
	for(i = 0; i < nCount ; i++)
	{
		pITypeInfo = NULL;
		hr = pITypeLib->GetTypeInfo(i,&pITypeInfo);
		if(FAILED(hr))
			continue;

		pTypeAttr = NULL;
		hr = pITypeInfo->GetTypeAttr(&pTypeAttr);
		if(FAILED(hr))
			continue;

		if(pTypeAttr->typekind == TKIND_COCLASS)
		{
			aguidCoClass.Add(pTypeAttr->guid);
		}
		pITypeInfo->ReleaseTypeAttr(pTypeAttr);
	}

	return	aguidCoClass.GetCount() ? true : false;
}

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

IEコントロールからのイベントを簡単に受信する

ATLを利用すると比較的簡単にイベントの受信ができる。ここではBEGIN_SINK_MAPによりイベントを受信する方法を紹介する。

まずひな形となるプロジェクトを以下の手順で作成する。

1.Visual Studio 2008の「ファイル」メニューの「新規作成」から「プロジェクト」を選択する。

2.「新しいプロジェクト」ウインドウが開いたら、テンプレートから「ATL/WTLアプリケーションウイザード」を選択してプロジェクトを作成する。

3.「ATL/WTLアプリケーションウイザード」が開いたら「ユーザーインターフェース機能」タブにある「ビューウインドウの形式」で「HTMLページ」を選択して「完了」ボタンを押す。

以上の操作により、シングルドキュメントインターフェースでビューの形式がHTMLページのプロジェクトが自動生成される。



ウイザードにより自動生成されたソースコードのうち、Viewクラスのヘッダーファイルを以下ソースコードのコメントで示したように変更する。
#pragma once

//■以下の宣言を5行追加
#define SINKID_COMTESTEVENTS 0		//Viewとして使うだけならいちいちdefineしなくてもいいが。。。
#include "mshtml.h"
#include "mshtmdid.h"
#include "Exdisp.h"
#include "Exdispid.h"


class CMy20090318_htmleventView : public CWindowImpl<CMy20090318_htmleventView, CAxWindow>
	,public IDispEventImpl<SINKID_COMTESTEVENTS, CMy20090318_htmleventView, &DIID_DWebBrowserEvents2>	//■この行を追加
{
	CComPtr<IWebBrowser2>	_pIWebBrowser2;			//■この行を追加。このビューがホストしているIWebBrowser2を使いやすいようにメンバーとして用意する

public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
			return FALSE;

		// このメッセージを翻訳する機会をHTML ページに与える
		return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
	}

	//■以下の3行を追加(IEコントロールのイベントマップ。ここではDISPID_DOCUMENTCOMPLETEのみ処理している)
	BEGIN_SINK_MAP(CMy20090318_htmleventView)
		SINK_ENTRY_EX(SINKID_COMTESTEVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
	END_SINK_MAP()

	BEGIN_MSG_MAP(CMy20090318_htmleventView)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)		//■WM_CREATEのメッセージハンドラを追加
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)		//■WM_DESTROYのメッセージハンドラを追加
	END_MSG_MAP()


	///////////////////////////////////////////////////////
	//■これ以降の3つの関数を追加
	//


	//
	//	IEコントロールのイベントによりHTMLページの読み込みが終わった時に呼ばれる
	//
	STDMETHOD(OnDocumentComplete)(IDispatch* pDisp, VARIANT* URL)
	{
		ATLTRACE("■OnDocumentComplete Received.\n");

		return S_OK;
	}


	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		LRESULT	ret;

		//IWebBrowser2はOnCreate後でないと取得できないのでDefWindowProcで先に生成してしまう
		ret = DefWindowProc(uMsg,wParam,lParam);

		//IWebBroser2の取得
		_pIWebBrowser2 = NULL;
		QueryControl(&_pIWebBrowser2);
		if(_pIWebBrowser2 == NULL)
			return	ret;


		HRESULT		hr;
		CComVariant	vEmpty;

		//IEコントロールへの接続。これによりイベントが受信できるようになる。
		hr = Advise(_pIWebBrowser2);

		//URLの読み込み
		vEmpty.Clear();
		hr = _pIWebBrowser2->Navigate(L"http://www.usefullcode.net/",&vEmpty,&vEmpty,&vEmpty,&vEmpty);

		return	ret;
	}


	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;

		if(_pIWebBrowser2)
		{
			//IEコントロールからの切断
			Unadvise(_pIWebBrowser2);
			_pIWebBrowser2 = NULL;
		}

		return	0;
	}
};


たったこれだけの手順でIEコントロールからのDISPID_DOCUMENTCOMPLETEを処理できる。BEGIN_SINK_MAP内にDISPID_BEFORENAVIGATE2やDISPID_NAVIGATECOMPLETE2などなど、処理したいイベントのシンクマップを追加すればそれだけで処理できるようになる。

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

2009年03月20日

ワーカースレッドを生成するためのクラス

マルチスレッド化するとメインウインドウに与える影響を少なくしつつバックグラウンドで処理しやすい。ここではATLを利用した環境で簡単にスレッドを生成できるクラス「CDnpThreadImpl」を作成した。

■使用例
#include "DnpThreadImpl.h"

///
///\brief
///	テスト用のスレッドクラス
///
class CTestThread	: public CDnpThreadImpl
{
protected:

	///
	///\brief
	///	スレッドメイン処理
	///
	/// CDnpThreadImpl::ThreadMain_() から呼ばれる
	///ユーザーは呼び出さないこと
	///
	///\param	pParam
	///	CreateThread() で渡したユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam)
	{
		ATLTRACE("■スレッド開始\n");

		while(1)
		{
			//処理は無限ループ

			::Sleep(1000);

			// IsAbort() でスレッド中断を検出して関数から抜ける
			if(IsAbort())
				break;
		}

		ATLTRACE("■スレッド終了\n");
		return	0;
	}
};

以上のようにスレッド本体処理用のクラスをCDnpThreadImplから派生する形で作ったら、
CTestThread cThread;

cThread.CreateThread(NULL);
とするだけでワーカースレッドが生成して処理が実行される。処理を中断したい場合は
cThread.AbortThread();
のようにする。
スレッドが完全に終了するまで待ちたい場合は
_cThread.DestroyThread(5000);  //最大5秒待つ
のようにする。


■「DnpCriticalSection.h」の内容
#pragma	once

#include "atlexcept.h"


/*!
 * \brief
 * クリティカルセクションクラス
 * 
 * 例外を処理しているだけで、 CComAutoCriticalSection をそのまま利用。
 * 
 */
class CDnpCriticalSection
{
	CComAutoCriticalSection		_sec;		//!< クリティカルセクション本体
public:


	~CDnpCriticalSection()
	{
		HRESULT	hr;
	
		hr = Unlock();

		ATLASSERT(SUCCEEDED(hr));
	}


	/*!
	 * \brief
	 * ロック
	 * 
	 */
	HRESULT Lock(void)
	{
		HRESULT	hr;

		try
		{
			hr = _sec.Lock();
			if(FAILED(hr))
				return	hr;
		}
		catch(CAtlException& e)
		{
			ATLTRACE("■例外発生(HRESULT=%08X)\n",e.m_hr);
		}

		return	hr;
	}


	/*!
	 * \brief
	 * ロック解除
	 * 
	 */
	HRESULT	Unlock(void)
	{
		HRESULT	hr;

		try
		{
			hr = _sec.Unlock();
			if(FAILED(hr))
				return	hr;
		}
		catch(CAtlException& e)
		{
			ATLTRACE("■例外発生(HRESULT=%08X)\n",e.m_hr);
		}

		return	hr;
	}
};


■「DnpThreadImpl.h」の内容
#pragma	once

#include "DnpCriticalSection.h"

///
///\brief
///		スレッド実装用クラス
///
///■利用例
/*! \code
	//
	{
		CTestThread		_cThread;

		_cThread.CreateThread(NULL);
		_cThread.AbortThread();
		_cThread.DestroyThread(1000);
	}
	//
	class CTestThread	: public CDnpThreadImpl
	{
	protected:

		///
		///\brief
		///	スレッドメイン処理
		///
		/// CDnpThreadImpl::ThradMain_() から呼ばれる
		///ユーザーは呼び出さないこと
		///
		///\param	pParam
		///	CreateThread() で渡したユーザー定義のパラメータ
		///
		virtual	DWORD	ThradMain(void* pParam)
		{
			ATLTRACE("■スレッド開始\n");

			while(1)
			{
				::Sleep(1000);

				// IsAbort() でスレッド中断を検出して関数から抜ける
				if(IsAbort())
					break;
			}

			ATLTRACE("■スレッド終了\n");
			return	0;
		}
	};
///\endcode
*/
///
class CDnpThreadImpl
{
	///
	///\brief
	///		スレッドパラメーター
	///
	///内部利用するのみなのでprivate構造体
	///
	struct	THREADPARAM
	{
		CDnpThreadImpl*	pThis;		//!< このクラスのポインタ
		void*	pParam;				//!< ユーザーが CreateThread() を読んだ時に指定されたパラメータ
	};


	///
	///\attention
	///以下のスレッド中断用フラグとクリティカルセクションは
	///put_bAbort() と get_bAbort() 以外からはアクセスしないこと!
	///
	CDnpCriticalSection	_sec_bAbort;	//!< スレッド中断フラグ用クリティカルセクション
	bool	_bAbort;					//!< スレッド中断用フラグ


protected:
	DWORD	_dwThreadID;			//!< 生成したスレッドのID
	HANDLE	_hThread;				//!< 生成したスレッドのハンドル

private:

	///
	///\brief
	///	スレッド中断用フラグ設定用関数
	///
	///\param[in]	bNew
	///新しいフラグ。trueならスレッド中断開始、falseなら無視
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	bool	put_bAbort(bool bNew)
	{
		HRESULT	hr;

		hr = _sec_bAbort.Lock();
		if(FAILED(hr))
			return	false;

		_bAbort = bNew;
		_sec_bAbort.Unlock();

		return	true;
	}


	///
	///\brief
	///	スレッド中断用フラグ取得用関数
	///
	///\param[out]	bValue
	///取得した中断フラグ。trueならスレッド中断処理中もしくは中断終了
	///
	///\retval	true	取得成功
	///\retval	false	失敗
	///
	bool	get_bAbort(bool& bValue)
	{
		HRESULT	hr;

		hr = _sec_bAbort.Lock();
		if(FAILED(hr))
			return	false;

		bValue = _bAbort;
		_sec_bAbort.Unlock();

		return	true;
	}



protected:

	///
	///\brief
	///	スレッド中断中(もしくは中断終了した)かどうか
	///
	///\retval	true	中断中
	///\retval	false	それ以外
	///
	///生成したスレッド内で中断処理をする必要があるかどうかを
	///チェックするのに利用する。
	///
	virtual	bool	IsAbort(void)
	{
		bool	bValue;

		if(get_bAbort(bValue))
			return	bValue;

		//取得に失敗したときはabortフラグfalseとして処理
		return	false;
	}


public:

	CDnpThreadImpl()
	{
		_dwThreadID = 0;
		_hThread = 0;
		put_bAbort(false);
	}

	virtual	~CDnpThreadImpl()
	{
		////スレッドが破棄されていなければASSERT
		//ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		DestroyThread(2000);			//スレッド終了を最大2秒待つ
	}



	///
	///\brief
	///	スレッド中断処理開始用
	///
	///スレッドを中断したいときに呼び出す
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	virtual	bool	AbortThread(void)
	{
		return	put_bAbort(true);
	}


	///
	///\brief
	///	スレッドの終了を待つ
	///
	///生成したスレッドからは呼ばないこと
	///
	///\param[in]	dwWait
	///	終了を待つ最大時間(単位:ミリ秒)\n
	///	INFINITE なら無限に待つ
	///
	///\retval	true	スレッドは終了した
	///\retval	false	スレッド終了待ちに失敗
	///
	bool	WaitUntilThreadEnd(DWORD dwWait=INFINITE)
	{
		DWORD	dwRet;

		if(_hThread == NULL)
			return	true;

		dwRet = ::WaitForSingleObject(_hThread,dwWait);
		if(dwRet == WAIT_TIMEOUT)
			return	false;

		return	true;
	}


	///
	///\brief
	///		スレッド破壊処理
	///
	///スレッド中断フラグを立ててスレッドが終了するのを待つ
	///
	///生成したスレッドからは呼ばないこと
	///
	///\param[in]	dwWait
	///	終了を待つ最大時間(単位:ミリ秒)\n
	///	INFINITE なら無限に待つ
	///
	///\retval	true	スレッドは破壊された
	///\retval	false	失敗
	///
	bool	DestroyThread(DWORD dwWait=INFINITE)
	{
		if(_hThread == NULL)
			return	true;

		//スレッド強制終了フラグを立てる
		AbortThread();

		WaitUntilThreadEnd(dwWait);

		if(IsThreadRunning() == false)
		{
			::CloseHandle(_hThread);
			_hThread	= NULL;
			_dwThreadID	= 0;

			return	true;
		}

		return	false;
	}



	///
	///\brief
	///		スレッドが現在実行中かどうか
	///
	///生成したスレッドからは呼ばないこと
	///
	///\retval	true	実行中
	///\retval	false	実行していない
	///
	bool	IsThreadRunning(void)	const
	{
		BOOL	ret;
		DWORD	dwCode;

		if(_hThread == NULL)
			return	false;

		ret = ::GetExitCodeThread(_hThread,&dwCode);
		if(ret == FALSE)
			return	false;

		return	(dwCode == STILL_ACTIVE) ? true : false;
	}





	///
	///\brief
	///		スレッド生成処理
	///
	///\param[in]	pParam
	///	スレッドに渡したいユーザー定義パラメーター。自由に利用していい
	///
	///\return
	///		ゼロなら失敗。ゼロ以外なら成功
	///
	DWORD	CreateThread(void* pParam)
	{
		ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		THREADPARAM*	pData;

		pData = new THREADPARAM;
		pData->pParam	= pParam;
		pData->pThis	= this;

		put_bAbort(false);
		_hThread = ::CreateThread(NULL,0,ThreadMain_,pData,0,&_dwThreadID);
		if(_hThread == NULL)
		{
			delete	pData;
			return 0;
		}

		return _dwThreadID;
	}



private:

	///
	///\brief
	///		スレッドメイン関数
	///
	///APIの CreateThread から呼ばれる。それ以外からは利用されない。
	///
	///内部で仮想関数の ThreadMain() を呼びだしている
	///
	static DWORD WINAPI ThreadMain_(void* pParam)
	{
		THREADPARAM* pData = (THREADPARAM*) pParam;

		CDnpThreadImpl*	pThis = pData->pThis;
		void*	pUseParam = pData->pParam;

		delete	pParam;

		return	pThis->ThreadMain(pUseParam);
	}


	///
	///\brief
	///	スレッドメイン処理(仮想関数のため実装なし)
	///
	///\param	pParam
	///	CreateThread() の引数として渡されたユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam) = 0;
};

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

スレッドを作り、HTTPでファイルをダウンロードする

最近はネット上にさまざまな情報やファイルがある。ブロードバンドの普及により通信速度も向上したこともあり、自分のプログラムからネット上の情報を参照したい場面がよくある。
ここではATL環境でマルチスレッドを利用してHTTPによりファイルをダウンロード/保存するクラスを作成した。

ここでは簡単のためスレッド生成数は1つのみで、キューにURLリストをためておき順次ダウンロードしている。

スレッドの生成にはCDnpThreadImpl、キューはCDnpQueを利用した。




■使用イメージ
	CDnpDownloadThread2	_cThread;

	//スレッドの生成とダウンロード開始
	{
		CAtlString	strURL;
		CAtlString	strFile;

		strURL = _T("http://www.usefullcode.net/atom.xml");
		strFile = _T("_down.xml");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);

		strURL = _T("http://www.usefullcode.net/index.html");
		strFile = _T("_down.html");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);
	}

	//メッセージハンドラ設定
	BEGIN_MSG_MAP(CMainDlg)
		MESSAGE_HANDLER(WM_DNP_DOWNLOAD_COMPLETE, OnDnpDownloadComplete)
	END_MSG_MAP()


	//ダウンロード結果の受信
	LRESULT OnDnpDownloadComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bool	ret = lParam ? true : false;

		ATLTRACE("■ダウンロード%s\n",(ret ? _T("成功") : _T("失敗")));

		return	0;
	}


■「DnpDownloadThread2.h」の内容
#pragma	once


#include "wininet.h" 
#pragma comment(lib,"wininet.lib")

#include "atlstr.h"
#include "atlfile.h"

#include "DnpThreadImpl.h"
#include "DnpQue.h"

#define	WM_DNP_DOWNLOAD_COMPLETE	(WM_APP + 66)



///
///\brief
///	HTTPを利用したダウンロードスレッド
///
///
///
/** \code
	CDnpDownloadThread2	_cThread;

	//スレッドの生成とダウンロード開始
	{
		CAtlString	strURL;
		CAtlString	strFile;

		strURL = _T("http://www.usefullcode.net/atom.xml");
		strFile = _T("_down.xml");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);

		strURL = _T("http://www.usefullcode.net/index.html");
		strFile = _T("_down.html");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);
	}

	//メッセージハンドラ設定
	BEGIN_MSG_MAP(CMainDlg)
		MESSAGE_HANDLER(WM_DNP_DOWNLOAD_COMPLETE, OnDnpDownloadComplete)
	END_MSG_MAP()


	//ダウンロード結果の受信
	LRESULT OnDnpDownloadComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bool	ret = lParam ? true : false;

		ATLTRACE("■ダウンロード%s\n",(ret ? _T("成功") : _T("失敗")));

		return	0;
	}
\endcode
*/
///
class CDnpDownloadThread2	: protected CDnpThreadImpl
{

	HINTERNET	_hInternet;			//!< 接続用
	HINTERNET	_hConnect;			//!< 接続用

	HANDLE		_hEvent;			//!< ダウンロード情報追加検出イベント用


	///
	///\brief
	///	ダウンロード情報格納用
	///
	class	CItem
	{
	public:
		CAtlString	strURL;			//!< ダウンロードURL
		CAtlString	strFile;		//!< 保存先ファイル名
		HWND		hWnd;			//!< ダウンロード結果通知先
		WPARAM		wParam;			//!< 結果通知時のユーザー定義パラメーター

		CItem()
		{
			wParam	= NULL;
			hWnd	= NULL;
		}

		CItem(LPCTSTR pszURL,LPCTSTR pszFile,HWND hUserWnd,WPARAM wUserParam)
		{
			strURL	= pszURL;
			strFile	= pszFile;
			hWnd	= hUserWnd;
			wParam	= wUserParam;
		}
	};

	//
	//ダウンロード情報格納用データとそのクリティカルセクション
	//
	CDnpCriticalSection	_sec_queItems;		//!< put_queItems() get_queItems() get_queItemsSize() 以外からはアクセスしないこと
	CDnpQue<CItem>		_queItems;			//!< put_queItems() get_queItems() get_queItemsSize()  以外からはアクセスしないこと

protected:

	///
	///\brief
	///	ダウンロード情報の追加
	///
	///\param[in]	cItem
	///ダウンロード情報
	///
	///\retval	true	追加成功
	///\retval	false	追加失敗
	///
	bool	put_queItems(const CItem& cItem)
	{
		HRESULT	hr;

		hr = _sec_queItems.Lock();
		if(FAILED(hr))
			return	false;

		_queItems.AddData(cItem);
		_sec_queItems.Unlock();

		return	true;
	}


	///
	///\brief
	///	ダウンロード情報の取得
	///
	///\param[out]	cItem
	///ダウンロード情報
	///
	///\retval	true	取得成功
	///\retval	false	取得失敗
	///
	bool	get_queItems(CItem& cItem)
	{
		bool	ret;
		HRESULT	hr;

		hr = _sec_queItems.Lock();
		if(FAILED(hr))
			return	false;

		ret = _queItems.GetData(cItem);
		_sec_queItems.Unlock();

		return	ret;
	}



	///
	///\brief
	///	まだ残っているダウンロード情報数
	///
	///\param[out]	nSize
	///ダウンロード情報数
	///
	///\retval	true	取得成功
	///\retval	false	取得失敗
	///
	bool	get_queItemsSize(size_t& nSize)
	{
		HRESULT	hr;

		hr = _sec_queItems.Lock();
		if(FAILED(hr))
		{
			nSize = 0;
			return	false;
		}

		nSize = _queItems.GetSize();
		_sec_queItems.Unlock();

		return	true;
	}




public:

	CDnpDownloadThread2()
	{
		_hEvent		= NULL;
		_hInternet	= NULL;
		_hConnect	= NULL;
	}

	virtual	~CDnpDownloadThread2()
	{
		////スレッドが破棄されていなければASSERT
		//ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		DestroyThread(2000);			//スレッド終了を最大2秒待つ
	}




	///
	///\brief
	///	ダウンロード情報の追加およびダウンロード開始
	///
	///ほかにダウンロード中のファイルがある場合はそのダウンロードが終わるまで
	///ダウンロードは開始しない
	///
	///\param[in]	pszURL
	///ダウンロードしたいURL
	///
	///\param[in]	pszFile
	///保存先ファイル
	///
	///\param[in]	hWnd
	///結果の通知先HWND
	///
	///\param[in]	wParam
	///結果通知時のユーザー定義パラメーター
	///
	///\retval	true	成功(情報の追加成功であって、ダウンロード結果ではない)
	///\retval	false	失敗
	///
	bool	StartDownload(LPCTSTR pszURL,LPCTSTR pszFile,HWND hWnd,WPARAM wParam)
	{
		bool	ret;

		ret = put_queItems(CItem(pszURL,pszFile,hWnd,wParam));
		if(ret == false)
			return	false;

		if(_hEvent == NULL)
			_hEvent = ::CreateEvent(NULL,FALSE,TRUE,_T("CDnpDownloadThread2"));

		::SetEvent(_hEvent);

		if(IsThreadRunning())
			return	true;

		return	CreateThread(NULL) ? true : false;
	}



	///
	///\brief
	///	ダウンロードの中断
	///
	bool	AbortDownload(void)
	{
		return	AbortThread();
	}



protected:

	///
	///\brief
	///	スレッドメイン処理
	///
	/// CDnpThreadImpl::ThradMain_() から呼ばれる
	///ユーザーは呼び出さないこと
	///
	///\param	pParam
	///	CreateThread() で渡したユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam)
	{
		bool	ret;
		size_t	nSize;
		HANDLE	hEvent;

		hEvent = ::OpenEvent(EVENT_ALL_ACCESS,FALSE,_T("CDnpDownloadThread2"));
	
		while(1)
		{
			//500ミリ秒ごともしくはイベントセット時に処理する
			::WaitForSingleObject(hEvent,500);

			// IsAbort() でスレッド中断を検出して関数から抜ける
			if(IsAbort())
				break;

			ret = get_queItemsSize(nSize);
			if(ret && nSize == 0)
			{
				::ResetEvent(hEvent);
				continue;
			}


			{
				CItem		cItem;
				bool		ret;
				HANDLE		hFile;
				CAtlFile	cFile;

				ret = get_queItems(cItem);
				if(ret == false)
					continue;

				ret = false;
				cFile.Create(cItem.strFile,GENERIC_WRITE,0,CREATE_ALWAYS);
				hFile = cFile.Detach();
				if(hFile)
				{
					ret = Download(cItem.strURL,hFile);
					::CloseHandle(hFile);
				}
				if(cItem.hWnd && ::IsWindow(cItem.hWnd))
					::PostMessage(cItem.hWnd,WM_DNP_DOWNLOAD_COMPLETE,cItem.wParam,(LPARAM)ret);
			}
		}
		::CloseHandle(hEvent);

		return	0;
	}





	///
	///\brief
	///	スレッド中断処理開始用
	///
	///スレッドを中断したいときに呼び出す
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	bool	AbortThread(void)
	{
		if(_hEvent)
			::SetEvent(_hEvent);

		if(_hConnect)
			::InternetCloseHandle(_hConnect);
		if(_hInternet)
			::InternetCloseHandle(_hInternet);
		_hConnect = NULL;
		_hInternet = NULL;

		if(_hEvent)
			::CloseHandle(_hEvent);
		_hEvent = NULL;

		return	CDnpThreadImpl::AbortThread();
	}






	///
	///\brief
	///		HTTPを利用したダウンロード関数
	///
	///ダウンローが終了(もしくは失敗や中断)するまでreturnしない
	///
	///\param[in]	pszURL
	///ダウンロードするURL
	///
	///\param	hFile
	///保存先ファイルのハンドル
	///
	///\param[in]	dwBuffSize
	///受信バッファーサイズ
	///
	///\retval	true	ダウンロードは正常に終了
	///\retval	false	失敗
	///
	///
	virtual	bool	Download(LPCTSTR pszURL,HANDLE hFile,DWORD dwBuffSize=1024)
	{
		TCHAR		pszHeader[] = _T("Accept: */*");
		BOOL		ret;
		DWORD		dwReadSize;
		DWORD		dwWrittenSize;
		BYTE*		pcbBuff;

		pcbBuff = new BYTE[dwBuffSize];
		if(pcbBuff == NULL || IsAbort())
			return	false;

		_hInternet = ::InternetOpen(NULL,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
		if(_hInternet == NULL || IsAbort())
		{
			delete	pcbBuff;
			return	false;
		}

		_hConnect = ::InternetOpenUrl(_hInternet,pszURL,pszHeader,lstrlen(pszHeader),INTERNET_FLAG_DONT_CACHE,0);
		if(_hConnect == NULL || IsAbort())
		{
			::InternetCloseHandle(_hInternet);
			_hInternet = NULL;
			delete	pcbBuff;
			return	false;
		}

		while(1)
		{
			//if(IsAbort())
			//	break;

			::Sleep(0);
			ret = ::InternetReadFile(_hConnect,pcbBuff,dwBuffSize,&dwReadSize);
			if(ret == FALSE)
				break;

			if(dwReadSize == 0 || IsAbort())
				break;

			ret = ::WriteFile(hFile,pcbBuff,dwReadSize,&dwWrittenSize,NULL);
			if(ret == FALSE)
				break;
		}

		if(ret)
			ret = ::FlushFileBuffers(hFile);
		::InternetCloseHandle(_hConnect);
		::InternetCloseHandle(_hInternet);
		_hConnect = NULL;
		_hInternet = NULL;

		delete	pcbBuff;

		return	(ret == FALSE || IsAbort()) ? false : true;
	}

};


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

ディスプレイデバイスを列挙する

Windows 2000以上で用意されているEnumDisplayDevicesを利用すると、そのPCにインストールされているディスプレイデバイスを一覧できる。

■取得結果例(マルチディスプレイ環境なのでQuadroが2つ列挙されている模様)
NVIDIA Quadro FX 2500M
NVIDIA Quadro FX 2500M
RDPDD Chained DD
RDP Encoder Mirror Driver


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

///
///\brief
///	ディスプレイデバイス名取得
///
///仮にディスプレイデバイスが1つでも以下のように1つ以上のデバイス名が取得されることがある
///
///	Windows 2000以降対応
///
///\param	astrDeviceName
///取得したデバイス名の配列
///
///\retval	true	
///\retval	false	
///
///-取得結果例1
///		- NeoMagic MagicGraph256AV driver
///		- NetMeeting driver
///		- RDPDD Chained DD
///-取得結果例2
///		- NVIDIA Quadro FX 2500M
///		- NVIDIA Quadro FX 2500M
///		- RDPDD Chained DD
///		- RDP Encoder Mirror Driver

bool	GetDisplayDeviceName(CAtlArray<CAtlString>& astrDeviceName)
{
	HMODULE			hDLL;
	BOOL			ret;
	DWORD			dwDevice;
	DISPLAY_DEVICE	sDD;

	BOOL (CALLBACK* pfnEnumDisplayDevices)(PVOID Unused,DWORD iDevNum,PDISPLAY_DEVICE lpDisplayDevice,DWORD dwFlags);

	astrDeviceName.RemoveAll();

	hDLL = ::LoadLibrary(_T("user32.dll"));
	if(hDLL == NULL)
		return	false;

#ifdef _UNICODE
	(FARPROC&)pfnEnumDisplayDevices = ::GetProcAddress(hDLL,"EnumDisplayDevicesW");
#else
	(FARPROC&)pfnEnumDisplayDevices = ::GetProcAddress(hDLL,"EnumDisplayDevicesA");
#endif

	if(pfnEnumDisplayDevices == NULL)
	{
		::FreeLibrary(hDLL);
		return	false;
	}

	dwDevice = 0;
	while(1)
	{
		::ZeroMemory(&sDD,sizeof(DISPLAY_DEVICE));
		sDD.cb = sizeof(DISPLAY_DEVICE);

		//dwDevice番目のディスプレイデバイスを取得
		ret = pfnEnumDisplayDevices(NULL,dwDevice,&sDD,0);
		if(ret == FALSE)
			break;

		astrDeviceName.Add(sDD.DeviceString);
		dwDevice++;
	}

	::FreeLibrary(hDLL);

	return	true;
}



bool	Test()
{
	size_t		i;
	size_t		nSize;

	CAtlArray<CAtlString>	astrName;

	GetDisplayDeviceName(astrName);

	nSize = astrName.GetCount();
	for(i = 0; i < nSize; i++)
	{
		ATLTRACE("%s\n",astrName[i]);
	}

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

2009年03月21日

Windows SDK、DDK、WDK、MSDNライブラリのダウンロードリンク(2010年5月版)

Microsoftのホームページ上をさまよっていたらいくつかのリンクを見つけた。ここではそれらを紹介する。


Windows 7用SDKのダウンロード(2010/5追記)

「Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1 (ISO)」のダウンロードは http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=71deb800-c591-4f97-a900-bea146e4fae1で可能。x86用、AMD64、x64用別にISOファイルが1つずつ用意されていて、isoファイルは1つ1.5GBほど。x86用だけでいいのであれば、[GRMSDK_EN_DVD.iso ]を1つダウンロードすればいい。


DDK/WDKのダウンロード

Microsoftのホームページ内に「Download Kits and Tools」というページを発見した。

まず目についたのが
Windows Server 2003 DDK」のサイトへのリンク。たどると
Download the Windows Server 2003 SP1 DDK [236 MB ISO file]」がダウンロードできるようになっていた。このDDKを用いればWindows 2000/XP/VistaとWindows Server 2003に対応したドライバーを開発できるようだ。これは過去のDDKバージョンになる。ほかのDDKよりもダウンロードサイズが小さいのが気になるが、勝手な想像をするとおそらくMSDN Libraryが省略されているのだろう。


さらにドライバー開発用のリンクを探してみると「Windows Driver Kit on Microsoft Connect」がある。これはMicrosoftの無料登録制のホームページへのリンクになっていて、たどると「WDK for Windows Server 2008 - Build 6001_18002」がダウンロードできる。ファイル名は「6.1.6001.18002.081017-1400_wdksp-WDK18002SP_EN_DVD.iso」でファイルサイズは605MBほど。
このDDK(WDK)はWindows 2000/XP/VistaとWindows Server 2003/2008に対応したドライバーを開発できるようだ。

Windows 7用のベータ版WDKに関してはありますよというアナウンスだけで、Microsoftに電子メールで申請するかMSDNに入っていない限りダウンロードはできないようだ。



Windows SDKのダウンロード

DDKを探したついでにSDKの方も検索してみた。結果「Microsoft Windows ソフトウェア開発キット」というページを発見。

ここには「適切な SDK の検索 (英語)」へのリンクが用意されていて、どのバージョンのSDKが最新になっているのか、どのSDKがどのOSに対応しているのかがひとめでわかるようになっていた。

2009年3月21日現在では、2009年2月8日リリースの「6.0.6001.18000.367」(サイズ1.3GB、ファイル名6.0.6001.18000.367-KRMSDK_EN.iso)というバージョンが一番対応範囲が広く、Windows XP/VistaおよびWindows Server 2003/2008に対応するようだ。

2009年2月7日リリースでビルドバージョンが「6.1.6000.16384.10」(サイズ1.4GB、ファイル名)というのもある。isoでのダウンロードではなく、setup.exeから動的にダウンロードするバージョンになる。対象OSからWindows Server 2008が除外、.NET Frameworkも3.5に対応しないバージョンだが、基本的な内容は2009年2月8日版と同じなのだろう。

Windows 7用のSDKに関しては、「Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA」というベータ版が用意されている。サイズは1.3GB、ファイル名は7.0.7000.0.4011.WindowsSDK_Windows7_idw.WindowsSDK.DVD.Release.isoになる。OSはWindows 7のベータ版に加え、Windows XP/VistaとWindows Server 2008に対応するようだ。



MSDNライブラリのダウンロード

最後に開発で必須なヘルプとなるMSDNライブラリのダウンロード用リンク。

日本語版は「MSDN Library for Visual Studio 2008 SP1 (2008年12月更新版)」(サイズ2.6GB、ファイル名JPNMSDNX1530336.ISO)が、英語版は「MSDN Library for Visual Studio 2008 SP1」(サイズ2.2GB、ファイル名VS2008SP1MSDNENUX1506188.iso)が最新版のようだ。

連続接続時間が6時間となるイーモバイルでは2GBもあるMSDNライブラリを1度の接続でダウンロードすることは不可能。ちょっと調べてみたら「Irvine」が2008年7月15日にバージョンアップをしていて2GB超えのファイルのダウンロードにも対応していた。このダウンローダーを利用することで無事にイーモバイル環境でも落とすことができた。

2009年03月22日

IEコントロールの右クリックメニューを実装する

IEコントロールの右クリックメニューを自分で制御したいときにはIDocHostUIHandlerを実装して、SetExternalUIHandlerで設定すればいい。
ここでの処理はIE8のアクセラレーター項目には対応しない。

1.Visual Studio 2008の「ファイル」メニューにある「新規」メニューから「プロジェクト」を選択する。

2.「新しいプロジェクト」の画面で「ATL/WTLアプリケーションウイザード」を選択する。ここではプロジェクト名を「RightClickTest」とした。

3.アプリケーションウイザードが開いたら、「ユーザーインターフェース機能」のタブにある「ビューウインドウの形式」で「HTML」ページを選択して「完了」ボタンを押す。

4.(以上の操作でアプリケーションウイザードにより自動的にひな形となるソースコードが自動生成される)

5.生成したソースコードのViewクラスを開き、以下のように6箇所ソースコードを追加する。

#pragma once

#include "DnpDocHostUIHandlerDispatchImpl.h"	//■追加

class CRightClickTestView : public CWindowImpl<CRightClickTestView, CAxWindow>
	,public CComObjectRootEx<CComSingleThreadModel>		//■追加
	,public IDnpDocHostUIHandlerDispatchImpl			//■追加
{
public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
			return FALSE;

		// このメッセージを翻訳する機会をHTML ページに与える
		return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
	}

	BEGIN_MSG_MAP(CRightClickTestView)
	END_MSG_MAP()


	//■以下4行を追加
	BEGIN_COM_MAP(CRightClickTestView)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandlerDispatch)
	END_COM_MAP()


	//■右クリック表示用の仮想関数を追加
	STDMETHOD(ShowContextMenu)(DWORD dwID,DWORD x,DWORD y,IUnknown *pcmdTarget,IDispatch *pdispReserved,HRESULT *dwRetVal)
	{
		bool	ret;

		HMENU	hMenu;
		HWND	hWnd;
		bool	bUseShellExtention;
		bool	bUseLanguage;
		bool	bMenuUserCtrl;

		hMenu = NULL;
		hWnd = NULL;
		bUseShellExtention	= true;		//シェルエクステンションメニューを追加する
		bUseLanguage		= true;		//エンコードメニューを追加する
		bMenuUserCtrl		= true;		//メニュー項目の有効/無効を自分で決定する

		ret = GetDefaultContextMenu(hMenu,hWnd,dwID,pcmdTarget,bUseShellExtention,bUseLanguage);
		if(ret == false)
			return	E_NOTIMPL;		//メニュー取得失敗。IEデフォルト処理に任せる

		//メニューの表示とその処理
		{
			int		nSelection;

			if(bMenuUserCtrl)
			{
				//自分でメニューの有効/無効を決定する(WM_INITMENUPOPUPもしくはこの関数内で設定可能になる)
				nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,m_hWnd,(RECT*)NULL);
			}
			else
			{
				//IEにメニューの有効/無効を決定させる
				nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,hWnd,(RECT*)NULL);
			}
			::DestroyMenu(hMenu);

			//メニュー選択結果の処理
			if(nSelection)
				::SendMessage(hWnd,WM_COMMAND,nSelection,NULL);
		}

		*dwRetVal = S_OK;
		return S_OK;
	}


	//■生成時処理用に関数を追加
	HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
			DWORD dwStyle = 0, DWORD dwExStyle = 0,
			_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
	{
		HWND	hWnd;

		hWnd = CWindowImpl<CRightClickTestView, CAxWindow>::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
		if(hWnd == NULL)
			return	NULL;

		//右クリックメニュー処理をこのクラスで行う
		SetExternalUIHandler(static_cast<IDocHostUIHandlerDispatch*>(this));

		return	hWnd;
	}
};
6.「MainFrm.h」を開き、
	CRightClickTestView		m_view;
という部分を
	CComObject<CRightClickTestView> m_view;
に変更する。

7.最後に「DnpDocHostUIHandlerDispatchImpl.h」というファイルを作成し、その内容を以下のようにする。
#pragma	once

#include "Mshtmcid.h"

///
///\brief
///	IDocHostUIHandlerDispatch実装補助用クラス
///
///仮想関数はすべてE_NOTIMPLを返す
///
class	IDnpDocHostUIHandlerDispatchImpl :
	public IDispatchImpl<IDocHostUIHandlerDispatch, &IID_IDocHostUIHandlerDispatch, &LIBID_ATLLib>
{
public:
	virtual HRESULT STDMETHODCALLTYPE ShowContextMenu( 
		/* [in] */ DWORD dwID,
		/* [in] */ DWORD x,
		/* [in] */ DWORD y,
		/* [in] */ IUnknown *pcmdtReserved,
		/* [in] */ IDispatch *pdispReserved,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetHostInfo( 
		/* [out][in] */ DWORD *pdwFlags,
		/* [out][in] */ DWORD *pdwDoubleClick)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ShowUI( 
		/* [in] */ DWORD dwID,
		/* [in] */ IUnknown *pActiveObject,
		/* [in] */ IUnknown *pCommandTarget,
		/* [in] */ IUnknown *pFrame,
		/* [in] */ IUnknown *pDoc,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE HideUI( void)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE UpdateUI( void)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE EnableModeless( 
		/* [in] */ VARIANT_BOOL fEnable)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate( 
		/* [in] */ VARIANT_BOOL fActivate)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate( 
		/* [in] */ VARIANT_BOOL fActivate)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ResizeBorder( 
		/* [in] */ long left,
		/* [in] */ long top,
		/* [in] */ long right,
		/* [in] */ long bottom,
		/* [in] */ IUnknown *pUIWindow,
		/* [in] */ VARIANT_BOOL fFrameWindow)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( 
		/* [in] */ DWORD_PTR hWnd,
		/* [in] */ DWORD nMessage,
		/* [in] */ DWORD_PTR wParam,
		/* [in] */ DWORD_PTR lParam,
		/* [in] */ BSTR bstrGuidCmdGroup,
		/* [in] */ DWORD nCmdID,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath( 
		/* [out] */ BSTR *pbstrKey,
		/* [in] */ DWORD dw)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDropTarget( 
		/* [in] */ IUnknown *pDropTarget,
		/* [out] */ IUnknown **ppDropTarget)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetExternal( 
		/* [out] */ IDispatch **ppDispatch)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateUrl( 
		/* [in] */ DWORD dwTranslate,
		/* [in] */ BSTR bstrURLIn,
		/* [out] */ BSTR *pbstrURLOut)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE FilterDataObject( 
		/* [in] */ IUnknown *pDO,
		/* [out] */ IUnknown **ppDORet)
	{
		return	E_NOTIMPL;
	}





	///
	///\brief
	///	IEの右クリックメニューの取得
	///
	///\param[in,out]	hMenu
	///取得/生成した右クリックメニュー
	///必ずNULLを渡すこと!
	///
	///\param	hWnd
	///メニュー選択結果の送信先HWND。\n
	///必ずNULLを渡すこと!
	///
	///\param[in]	dwID
	///IDocHostUIHandler::ShowContextMenu() の第1引数
	///
	///\param	*pcmdTarget
	///IDocHostUIHandler::ShowContextMenu() の第3引数IOleCommandTargetのIUnknown
	///
	///\param[in]	bUseShellExtention
	///シェルエクステンションメニューを利用するかどうか
	///たとえばフリーソフトなどで追加される「○○でダウンロード」などのメニュー。trueなら追加する。
	///
	///\param[in]	bUseLanguage
	///エンコードメニューを利用するかどうか
	///ページの言語エンコード指定用のメニュー。trueなら追加する。
	///
	///\retval	true	取得成功
	///\retval	false	失敗
	///
	///取得方法は以下のHPを参照
	///「WebBrowser Customization (Part 2)」
	///http://msdn.microsoft.com/en-us/library/aa770042(VS.85).aspx
	///
	///- 利用例
	/** \code
		STDMETHOD(ShowContextMenu)(DWORD dwID,DWORD x,DWORD y,IUnknown *pcmdTarget,IDispatch *pdispReserved,HRESULT *dwRetVal)
		{
			bool	ret;

			HMENU	hMenu;
			HWND	hWnd;
			bool	bUseShellExtention;
			bool	bUseLanguage;
			bool	bMenuUserCtrl;

			hMenu = NULL;
			hWnd = NULL;
			bUseShellExtention	= true;		//シェルエクステンションメニューを追加する
			bUseLanguage		= true;		//エンコードメニューを追加する
			bMenuUserCtrl		= true;		//メニュー項目の有効/無効を自分で決定する

			ret = GetDefaultContextMenu(hMenu,hWnd,dwID,pcmdTarget,bUseShellExtention,bUseLanguage);
			if(ret == false)
				return	E_NOTIMPL;		//メニュー取得失敗。IEデフォルト処理に任せる

			//メニューの表示とその処理
			{
				int		nSelection;

				if(bMenuUserCtrl)
				{
					//自分でメニューの有効/無効を決定する(WM_INITMENUPOPUPもしくはこの関数内で設定可能になる)
					nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,m_hWnd,(RECT*)NULL);
				}
				else
				{
					//IEにメニューの有効/無効を決定させる
					nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,hWnd,(RECT*)NULL);
				}
				::DestroyMenu(hMenu);

				//メニュー選択結果の処理
				if(nSelection)
					::SendMessage(hWnd,WM_COMMAND,nSelection,NULL);
			}

			*dwRetVal = S_OK;
			return S_OK;
		}
	\endcode
	*/
	///
	///\todo
	///IE8では未来クリックメニューにアクセラレーターがあるが、SHDVIDが不明なため追加できない
	///
	bool	GetDefaultContextMenu(HMENU& hMenu,HWND& hWnd,DWORD dwID,IUnknown *pcmdTarget,bool bUseShellExtention,bool bUseLanguage)
	{
		#define IDR_BROWSE_CONTEXT_MENU  24641
		#define SHDVID_GETMIMECSETMENU   27
		#define SHDVID_ADDMENUEXTENSIONS 53

		HRESULT		hr;
		CComPtr<IOleCommandTarget>	pIOleCommandTarget;

		if(pcmdTarget == NULL)
			return	false;

		ATLASSERT(hMenu == NULL);
		ATLASSERT(hWnd == NULL);

		pcmdTarget->QueryInterface(IID_IOleCommandTarget,(void**)&pIOleCommandTarget);

		//IEのhWndの取得
		{
			CComPtr<IOleWindow>	pIOleWindow;

			hr = pcmdTarget->QueryInterface(IID_IOleWindow,(void**)&pIOleWindow);
			hr = pIOleWindow->GetWindow(&hWnd);
		}

		//DLLからのメニューリソース読み込み
		{
			HINSTANCE	hDll;

			hDll = ::LoadLibrary(TEXT("SHDOCLC.DLL"));			//XPまではこのDLL
			if(hDll == NULL)
				hDll = ::LoadLibrary(_T("ieframe.dll"));		//VistaではこのDLL

			if(hDll == NULL)
				return	false;

			hMenu = ::LoadMenu(hDll,MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));
			hMenu = ::GetSubMenu(hMenu,dwID);

			::FreeLibrary(hDll);
		}

		//言語サブメニューの追加
		if(bUseLanguage)
		{
			CComVariant		var;
			MENUITEMINFO	mii = {0};

			hr = pIOleCommandTarget->Exec(&CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &var);

			mii.cbSize = sizeof(mii);
			mii.fMask  = MIIM_SUBMENU;
			mii.hSubMenu = (HMENU) var.byref;

			::SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii);
		}


		//IEのExtentionのメニューを追加
		//たとえばフリーソフトなどで追加される「○○でダウンロード」などのメニュー
		if(bUseShellExtention)
		{
			CComVariant	var1;
			CComVariant	var2;

			V_VT(&var1) = VT_INT_PTR;
			V_BYREF(&var1) = hMenu;

			V_VT(&var2) = VT_I4;
			V_I4(&var2) = dwID;
			hr = pIOleCommandTarget->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);
		}



		//IEコントロールデフォルトの設定でメニュー項目の有効/無効/チェック設定をする
		{
			int	i;

			for (i = 0; i < GetMenuItemCount(hMenu); i++)
			{
				OLECMD olecmd;
				olecmd.cmdID = GetMenuItemID(hMenu, i);

				//■■通常のOLECMDFとは値が異なる。要調査
				if (olecmd.cmdID > 0)
				{
					UINT mf;
					pIOleCommandTarget->QueryStatus(&CGID_MSHTML,1,&olecmd,NULL);
					switch (olecmd.cmdf)
					{
					case OLECMDF_ENABLED:
					case 3:
						mf = MF_BYCOMMAND | MF_ENABLED | MF_UNCHECKED;
						break;

					case OLECMDF_LATCHED:
						mf = MF_BYCOMMAND | MF_ENABLED | MF_CHECKED;
						break;

					case 1:
					default:
						mf = MF_BYCOMMAND | MF_DISABLED | MF_GRAYED;
						break;
					}
					::CheckMenuItem(hMenu, olecmd.cmdID, mf);
					::EnableMenuItem(hMenu, olecmd.cmdID, mf);
				}
			}
		}

		return	true;
	}
};

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

1  2





usefullcode@gmail.com

About 2009年03月

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

前の記事は2007年04月です。

次の記事は2009年04月です。

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