« 2007年01月 | メイン | 2007年03月 »

前の10件 1  2  3

2007年02月 記事一覧

数値の平方根を計算する

test112.gif
ここでは平方根を計算したい数値を素因数分解し、素因数が2つ以上ある場合にルートの外に出す方法を利用した。

計算の過程で用いた素因数分解処理用のクラスと関数は「数値を素因数分解する」と同じものを利用している。

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



class	CElementInfo
{
public:
	long	_nElement;
	ULONG	_nCount;

	CElementInfo()
	{
		_nElement = 0;
		_nCount = 0;
	}

	CElementInfo(long nElement,ULONG nCount)
	{
		_nElement = nElement;
		_nCount = nCount;
	}

	CElementInfo&	operator++()
	{
		_nCount++;
		return	*this;
	}
	CElementInfo&	operator++(int)
	{
		_nCount++;
		return	*this;
	}

	CElementInfo&	operator--()
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
	CElementInfo&	operator--(int)
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
};



//
//	素因数分解
//
bool	Digest(long nData,CAtlArray<CElementInfo>* pacElements)
{
	long	nElement;
	ULONG	nCount;
	long	nLoopMax;

	if(pacElements == NULL)
		return	false;
	pacElements->RemoveAll();

	//負の数なら素因数として-1を与える
	if(nData < 0)
	{
		pacElements->Add(CElementInfo(-1,1));
		nData *= -1;
	}
	if(nData == 0 || nData == 1)
		return	true;

	nLoopMax = (long)sqrt((double)nData) + 1;
	for(nElement = 2; nElement <= nLoopMax; nElement += 2)
	{
		if(nElement == 4)	//2で素因数分解を1回行った後は3,5,7...2n+1で処理を
			nElement--;	//行なうように4のときに1を減算

		nCount = 0;
		while(1)
		{
			if(nData % nElement)
				break;

			nCount++;
			nData /= nElement;
		}
		if(nCount > 0)
			pacElements->Add(CElementInfo(nElement,nCount));
		if(nData == 1)
			break;
	}
	if(nData > 1)
		pacElements->Add(CElementInfo(nData,1));

	return	true;
}





//
//	平方根
//
//pacInnerElementsはルートの中側
//pacOuterElementsはルートの外側
//
bool	SquareRoot(long nData,CAtlArray<CElementInfo>* pacInnerElements,CAtlArray<CElementInfo>* pacOuterElements)
{
	bool	ret;
	CAtlArray<CElementInfo>	acDigest;

	if(pacInnerElements)
		pacInnerElements->RemoveAll();
	if(pacOuterElements)
		pacOuterElements->RemoveAll();
	if(pacInnerElements == NULL || pacOuterElements == NULL)
		return	false;

	if(nData == 0 || nData == 1 || nData == -1)
		return	true;

	//素因数分解
	ret = Digest(nData,&acDigest);
	if(ret == false)
		return	false;

	size_t	i;
	size_t	nSize;

	nSize = acDigest.GetCount();
	for(i = 0; i < nSize; i++)
	{
		if(acDigest[i]._nCount == 1)
			pacInnerElements->Add(CElementInfo(acDigest[i]._nElement,1));
		else if(acDigest[i]._nCount % 2 == 0)
			pacOuterElements->Add(CElementInfo(acDigest[i]._nElement,acDigest[i]._nCount / 2));
		else
		{
			if(acDigest[i]._nCount != 0)
			{
				pacInnerElements->Add(CElementInfo(acDigest[i]._nElement,1));
				pacOuterElements->Add(CElementInfo(acDigest[i]._nElement,acDigest[i]._nCount / 2));
			}
		}
	}

	return	true;
}



//
//	平方根
//
//pacInnerElementsはルートの中側
//pacOuterElementsはルートの外側
//
bool	SquareRoot(long nData,long* pnInner,long* pnOuter)
{
	bool	ret;
	CAtlArray<CElementInfo>	acInnerElements;
	CAtlArray<CElementInfo>	acOuterElements;

	if(pnInner)
		*pnInner = 0;
	if(pnOuter)
		*pnOuter = 0;
	if(pnInner == NULL || pnOuter == NULL)
		return	false;
	if(nData == 0)
		return	true;


	////////////////////////////////////
	//平方根の計算
	//
	ret = SquareRoot(nData,&acInnerElements,&acOuterElements);
	if(ret == false)
		return	false;


	////////////////////////////////////
	//結果値の計算
	//

	size_t	i;
	size_t	j;
	size_t	nSizeI;
	size_t	nSizeJ;

	*pnInner = 1;
	nSizeI = acInnerElements.GetCount();
	for(i = 0; i < nSizeI; i++)
	{
		nSizeJ = acInnerElements[i]._nCount;
		for(j = 0; j < nSizeJ; j++)
			*pnInner *= acInnerElements[i]._nElement;
	}

	*pnOuter = 1;
	nSizeI = acOuterElements.GetCount();
	for(i = 0; i < nSizeI; i++)
	{
		nSizeJ = acOuterElements[i]._nCount;
		for(j = 0; j < nSizeJ; j++)
			*pnOuter *= acOuterElements[i]._nElement;
	}

	return	true;
}






bool	Test()
{
	bool	ret;
	long	nData;
	long	nInner;
	long	nOuter;
	CAtlString	strBuff;
	CAtlString	strMessage;

	nData = 200;

	ret = SquareRoot(nData,&nInner,&nOuter);
	if(ret == false)
		return	false;

	strMessage.Format(_T("√%d = %d√%d"),nData,nOuter,nInner < 0 ? nInner*-1 : nInner);
	if(nInner < 0)
		strMessage += _T("i");

	::MessageBox(NULL,strMessage,_T("結果"),MB_OK);

	return	true;
}

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

2007年02月12日

二次方程式の解を求める

test113.gif
二次方程式の解は公式「(-b±√(b*b-4ac))/2a」を使えば簡単に計算できる。とは言うものの実際にソースコードとして実装しようと思うと結構大変。
このプログラムではヒトが計算するのと同じように平方根の展開や分数の約分を行って解を求めた。

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



class	CElementInfo
{
public:
	long	_nElement;
	ULONG	_nCount;

	CElementInfo()
	{
		_nElement = 0;
		_nCount = 0;
	}

	CElementInfo(long nElement,ULONG nCount)
	{
		_nElement = nElement;
		_nCount = nCount;
	}

	CElementInfo&	operator++()
	{
		_nCount++;
		return	*this;
	}
	CElementInfo&	operator++(int)
	{
		_nCount++;
		return	*this;
	}

	CElementInfo&	operator--()
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
	CElementInfo&	operator--(int)
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
};



//
//	素因数分解
//
bool	Digest(long nData,CAtlArray<CElementInfo>* pacElements)
{
	long	nElement;
	ULONG	nCount;
	long	nLoopMax;

	if(pacElements == NULL)
		return	false;
	pacElements->RemoveAll();

	//負の数なら素因数として-1を与える
	if(nData < 0)
	{
		pacElements->Add(CElementInfo(-1,1));
		nData *= -1;
	}
	if(nData == 0 || nData == 1)
		return	true;

	nLoopMax = (long)sqrt((double)nData) + 1;
	for(nElement = 2; nElement <= nLoopMax; nElement += 2)
	{
		if(nElement == 4)	//2で素因数分解を1回行った後は3,5,7...2n+1で処理を
			nElement--;	//行なうように4のときに1を減算

		nCount = 0;
		while(1)
		{
			if(nData % nElement)
				break;

			nCount++;
			nData /= nElement;
		}
		if(nCount > 0)
			pacElements->Add(CElementInfo(nElement,nCount));
		if(nData == 1)
			break;
	}
	if(nData > 1)
		pacElements->Add(CElementInfo(nData,1));

	return	true;
}





//
//	平方根
//
//pacInnerElementsはルートの中側
//pacOuterElementsはルートの外側
//
bool	SquareRoot(long nData,CAtlArray<CElementInfo>* pacInnerElements,CAtlArray<CElementInfo>* pacOuterElements)
{
	bool	ret;
	CAtlArray<CElementInfo>	acDigest;

	if(pacInnerElements)
		pacInnerElements->RemoveAll();
	if(pacOuterElements)
		pacOuterElements->RemoveAll();
	if(pacInnerElements == NULL || pacOuterElements == NULL)
		return	false;

	if(nData == 0)
		return	true;

	pacInnerElements->Add(CElementInfo((nData < 0) ? -1 : 1,1));
	pacOuterElements->Add(CElementInfo(1,1));
	if(nData == 1 || nData == -1)
		return	true;

	if(nData < 0)
		nData *= -1;

	//素因数分解
	ret = Digest(nData,&acDigest);
	if(ret == false)
		return	false;

	size_t	i;
	size_t	nSize;

	nSize = acDigest.GetCount();
	for(i = 0; i < nSize; i++)
	{
		if(acDigest[i]._nCount == 1)
			pacInnerElements->Add(CElementInfo(acDigest[i]._nElement,1));
		else if(acDigest[i]._nCount % 2 == 0)
			pacOuterElements->Add(CElementInfo(acDigest[i]._nElement,acDigest[i]._nCount / 2));
		else
		{
			if(acDigest[i]._nCount != 0)
			{
				pacInnerElements->Add(CElementInfo(acDigest[i]._nElement,1));
				pacOuterElements->Add(CElementInfo(acDigest[i]._nElement,acDigest[i]._nCount / 2));
			}
		}
	}

	return	true;
}





long	GetValue(const CAtlArray<CElementInfo>& acElements)
{
	long	ret;
	size_t	i;
	size_t	j;
	size_t	nSizeI;
	size_t	nSizeJ;

	nSizeI = acElements.GetCount();
	if(nSizeI == 0)
		return	0;

	ret = 1;
	for(i = 0; i < nSizeI; i++)
	{
		nSizeJ = acElements[i]._nCount;
		for(j = 0; j < nSizeJ; j++)
			ret *= acElements[i]._nElement;
	}

	return	ret;
}



//
//	平方根
//
//pacInnerElementsはルートの中側
//pacOuterElementsはルートの外側
//
bool	SquareRoot(long nData,long* pnInner,long* pnOuter)
{
	bool	ret;
	CAtlArray<CElementInfo>	acInnerElements;
	CAtlArray<CElementInfo>	acOuterElements;

	if(pnInner)
		*pnInner = 0;
	if(pnOuter)
		*pnOuter = 0;
	if(pnInner == NULL || pnOuter == NULL)
		return	false;
	if(nData == 0)
		return	true;

	////////////////////////////////////
	//平方根の計算
	//
	ret = SquareRoot(nData,&acInnerElements,&acOuterElements);
	if(ret == false)
		return	false;


	////////////////////////////////////
	//結果値の計算
	//

	*pnInner = GetValue(acInnerElements);
	*pnOuter = GetValue(acOuterElements);

	if(*pnInner == 0)
		*pnInner = 1;

	return	true;
}




//
//	約分
//
//nDenominator	:分母
//nNumerator	:分子
//
bool	Reduction(long nDenominator,long nNumerator,CAtlArray<CElementInfo>* pacDenominatorElements,CAtlArray<CElementInfo>* pacNumeratorElements)
{
	CAtlArray<CElementInfo>*	pA;
	CAtlArray<CElementInfo>*	pB;

	if(pacDenominatorElements)
		pacDenominatorElements->RemoveAll();
	if(pacNumeratorElements)
		pacNumeratorElements->RemoveAll();
	if(pacNumeratorElements == NULL || pacDenominatorElements == NULL)
		return	false;

	if(nDenominator == 0 || nNumerator == 0)
		return	true;
	if(nDenominator == -1)
	{
		//分母が-1だったら、分子に-1をかけて終わり
		nDenominator = 1;
		nNumerator *= -1;
		return	true;
	}
	if(nDenominator == 1 || nNumerator == 1)
		return	true;


	///////////////////////////////
	//約分前処理
	//

	if(nDenominator < 0)
	{
		//分母がマイナスだったら分子にマイナス記号を移す
		nDenominator *= -1;
		nNumerator *= -1;
	}

	//分母・分子ともに素因数分解
	Digest(nDenominator,pacDenominatorElements);
	Digest(nNumerator,pacNumeratorElements);

	//素因数の数が少ない方をpA、多い方をpBにする
	if(pacDenominatorElements->GetCount() < pacNumeratorElements->GetCount())
	{
		pA = pacDenominatorElements;
		pB = pacNumeratorElements;
	}
	else
	{
		pA = pacNumeratorElements;
		pB = pacDenominatorElements;
	}


	///////////////////////////////
	//約分処理
	//
	size_t	i;
	size_t	j;
	size_t	nSizeA;
	size_t	nSizeB;

	i = 0;
	nSizeA = pA->GetCount();
	nSizeB = pB->GetCount();
	for(j = 0; j < nSizeB; j++)
	{
		for(; i < nSizeA; i++)
		{
			if((*pA)[i]._nElement < (*pB)[j]._nElement)
				continue;
			if((*pA)[i]._nElement > (*pB)[j]._nElement)
				break;

			//共通する素因数の数だけnCountを減らす
			if((*pA)[i]._nCount < (*pB)[j]._nCount)
			{
				(*pB)[j]._nCount -= (*pA)[i]._nCount;
				(*pA)[i]._nCount = 0;
			}
			else if((*pA)[i]._nCount > (*pB)[j]._nCount)
			{
				(*pA)[i]._nCount -= (*pB)[j]._nCount;
				(*pB)[j]._nCount = 0;
			}
			else
			{
				(*pA)[i]._nCount = 0;
				(*pB)[j]._nCount = 0;
			}
			break;
		}
	}

	return	true;
}


//
//	約分
//
//nDenominator	:分母
//nNumerator	:分子
//
bool	Reduction(long& nDenominator,long& nNumerator)
{
	bool	ret;
	CAtlArray<CElementInfo>		acElementsA;
	CAtlArray<CElementInfo>		acElementsB;

	///////////////////////////////
	//約分
	//
	ret = Reduction(nDenominator,nNumerator,&acElementsA,&acElementsB);
	if(ret == false)
		return	false;


	///////////////////////////////
	//約分結果の数値計算
	//
	nDenominator = GetValue(acElementsA);
	nNumerator = GetValue(acElementsB);

	return	true;
}





//
//	二次方程式の解
//
//
//入力:	ax^2 + bx + c = 0
//出力:	(nA1 + nB1√nC1)/nD1	、	(nA2 + nB2√nC2)/nD2
//
bool	QuadricEquation(long a,long b,long c,long& nA1,long& nB1,long& nC1,long& nD1,long& nA2,long& nB2,long& nC2,long& nD2)
{
	bool	ret;
	long	b24ac;
	CAtlArray<CElementInfo>		acBElements;
	CAtlArray<CElementInfo>		ac2AElements;
	CAtlArray<CElementInfo>		acInnerElements;
	CAtlArray<CElementInfo>		acOuterElements;

	if(a == 0)
		return	false;

	b24ac = b * b - 4 * a * c;		//√内を計算
	if(b24ac == 0)
	{
		//重解
		//√内がゼロなら重解

		a *= 2;
		b *= -1;
		ret = Reduction(a,b);
		if(ret == false)
			return	false;

		//ATLTRACE("重解:%d/%d",b,a);

		nA1 = b;
		nB1 = 0;
		nC1 = 1;
		nD1 = a;

		//重解なので値を同じにする
		nA2 = nA1;
		nB2 = nB1;
		nC2 = nC1;
		nD2 = nD1;

		return	true;
	}

	//√を計算して素因数分解
	ret = SquareRoot(b24ac,&acInnerElements,&acOuterElements);
	if(ret == false)
		return	false;

	//(-b)を素因数分解
	ret = Digest(-1 * b,&acBElements);
	if(ret == false)
		return	false;

	//(2a)を素因数分解
	ret = Digest(2 * a,&ac2AElements);
	if(ret == false)
		return	false;

	bool	bFound;
	size_t	i;
	size_t	j;
	size_t	k;
	size_t	nSizeI;
	size_t	nSizeJ;
	size_t	nSizeK;

	//素因数分解した値を元に解の公式に当てはめて約分
	nSizeI = ac2AElements.GetCount();
	for(i = 0; i < nSizeI; i++)
	{
		//分母分子に含まれる共通因子を探す

		bFound = false;
		nSizeJ = acBElements.GetCount();
		for(j = 0; j < nSizeJ; j++)
		{
			if(acBElements[j]._nElement != ac2AElements[i]._nElement)
				continue;
			bFound = true;
			break;
		}
		if(bFound == false || acBElements[j]._nCount == 0)
			continue;

		bFound = false;
		nSizeK = acOuterElements.GetCount();
		for(k = 0; k < nSizeK; k++)
		{
			if(acOuterElements[k]._nElement != ac2AElements[i]._nElement)
				continue;
			bFound = true;
			break;
		}
		if(bFound == false || acOuterElements[k]._nCount == 0)
			continue;


		//共通因子が見つかった!

		ULONG	nMin;

		//約分できる共通因子の数を求める
		nMin = min(ac2AElements[i]._nCount,min(acBElements[j]._nCount,acOuterElements[k]._nCount));

		//見つかった共通因子で約分
		ac2AElements[i]._nCount		-= nMin;
		acBElements[j]._nCount		-= nMin;
		acOuterElements[k]._nCount	-= nMin;
	}


	////////////////////////////
	//求まった解の整形
	//
	//(nA ± nB√nC)/nD という形の解を√内が1のときはnA±nBを計算して整形する
	//

	long	nA;
	long	nB;
	long	nC;
	long	nD;

	nA = GetValue(acBElements);
	nB = GetValue(acOuterElements);
	nC = GetValue(acInnerElements);
	nD = GetValue(ac2AElements);
	if(nC == 0 && nB)
		nC = 1;			//√の中がゼロはおかしいので1にしておく

	if(nC != 1)
	{
		nA1 = nA;
		nB1 = nB;
		nC1 = nC;
		nD1 = nD;

		nA2 = nA;
		nB2 = -1 * nB;
		nC2 = nC;
		nD2 = nD;

		return	true;
	}

	nA1 = nA + nB;
	nB1 = 0;
	nC1 = 1;
	nD1 = nD;
	ret = Reduction(nD1,nA1);	//約分
	if(ret == false)
		return	false;

	nA2 = nA - nB;
	nB2 = 0;
	nC2 = 1;
	nD2 = nD;
	ret = Reduction(nD2,nA2);	//約分
	if(ret == false)
		return	false;

	return	true;
}





bool	Test()
{
	CAtlString	strBuff;
	CAtlString	strMessage;

	bool	ret;
	long	a;
	long	b;
	long	c;
	long	nA1;
	long	nB1;
	long	nC1;
	long	nD1;
	long	nA2;
	long	nB2;
	long	nC2;
	long	nD2;

	//////////////////////////
	//二次方程式の解を計算
	//

	a = 2;
	b = 6;
	c = 2;

	ret = QuadricEquation(a,b,c,nA1,nB1,nC1,nD1,nA2,nB2,nC2,nD2);
	if(ret == false)
		return	false;


	//////////////////////////
	//結果を整形して表示
	//

	strMessage.Format(_T("二次方程式:(%d)x*x + (%d)x + (%d)=0の解は\n\n"),a,b,c);

	if(nA1 == nA2 && nB1 == nB2 && nC1 == nC2 && nD1 == nD2)
	{
		//重解

		if(nD1 == 1)
			strBuff.Format(_T("%d"),nA1);
		else
			strBuff.Format(_T("%d/%d"),nA1,nD1);

		//重解なのでnBとnCは無視してOK

		strMessage += strBuff;
		::MessageBox(NULL,strMessage,_T(""),MB_OK);
		return	true;
	}

	if(nA1 == nA2 && nB1 == -1 * nB2 && nC1 == nC2 && nD1 == nD2)
	{
		//通常の±形式の解

		if(nD1 != 1)
		{
			if(nB1)
				strMessage += _T("(");
		}

		if(nA1)
		{
			strBuff.Format(_T("%d"),nA1);
			strMessage += strBuff;
		}

		if(nB1)
		{
			strMessage += _T("±");
			if(nB1 < 0)
				nB1 *= -1;
			if(nB1 != 1)
			{
				strBuff.Format(_T("%d"),nB1);
				strMessage += strBuff;
			}

			if(nC1 != 1 && nC1 != -1)
			{
				strBuff.Format(_T("√%d"),(nC1 > 0) ? nC1 : -1 * nC1);
				strMessage += strBuff;
			}
			if(nC1 < 0)
				strMessage += _T("i");
		}

		if(nD1 != 1)
		{
			if(nB1)
				strBuff.Format(_T("%s)/%d"),strMessage,nD1);
			else
				strBuff.Format(_T("%s/%d"),strMessage,nD1);
			strMessage = strBuff;
		}

		::MessageBox(NULL,strMessage,_T(""),MB_OK);
		return	true;
	}

	//ここに来る場合は以下のATLASSERT条件を満たすはず
	ATLASSERT(nB1 == 0);
	ATLASSERT(nC1 == 1);
	ATLASSERT(nB2 == 0);
	ATLASSERT(nC2 == 1);


	if(nD1 == 1)
		strBuff.Format(_T("%d"),nA1);
	else
		strBuff.Format(_T("%d/%d"),nA1,nD1);
	strMessage += strBuff;

	strMessage += _T("\n");

	if(nD2 == 1)
		strBuff.Format(_T("%d"),nA2);
	else
		strBuff.Format(_T("%d/%d"),nA2,nD2);
	strMessage += strBuff;

	::MessageBox(NULL,strMessage,_T(""),MB_OK);
	return	true;
}

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

2007年02月13日

フォルダをツリーコントロールに一覧する

test114.gif
ここでは特定のフォルダ内に存在するサブフォルダを一覧する簡易的なツリーコントロールクラスを作成した。

このクラスはかなり制限が多く、実装に癖があるので"実用"する場合はゼロから作り直した方が早いだろう。
トップレベルのフォルダは1つのみで、シェルフォルダやアイコンの表示機能などはない。
またフォルダがサブフォルダを持つ場合に、ツリーコントロール上で「+」マークが表示されるように、ツリーを開く際にその都度、孫フォルダまで取得している。これは本来スレッドを立ててマルチタスクに読み込むべき処理だが、そのまま簡単に読み込んでいる。



■ATL/WTLアプリケーションウイザードでダイアログベースのプロジェクトを作成する。

■リソースエディタでメインダイアログの大きさを適当に大きくする。

■MainDlg.hの先頭にクラス定義などを追加

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


class	CDnpFolderTreeCtrl	: public CTreeViewCtrl
{
protected:
	CAtlString	_strRootName;
	CAtlString	_strRootPath;
public:

	BEGIN_MSG_MAP(CDnpFolderTreeCtrl)
		NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDING,OnItemExpanding)
	END_MSG_MAP()
 

	bool	Initialize(LPCTSTR pszRootName,LPCTSTR pszRootPath)
	{
		bool		ret;
		HTREEITEM	hRoot;
		CAtlString	strPath;
		CAtlArray<CAtlString>	astrFolder;

		if(pszRootName == NULL || pszRootPath == NULL)
			return	false;
		if(*pszRootName == NULL || *pszRootPath == NULL)
			return	false;

		DeleteAllItems();

		strPath = pszRootPath;
		if(strPath.Right(1) != _T("\\") && strPath.Right(1) != _T("/"))
			strPath += _T("\\");

		_strRootName = pszRootName;
		_strRootPath = strPath;

		hRoot = InsertItem(pszRootName,TVI_ROOT,TVI_LAST);
		if(hRoot == NULL)
			return	false;

		ret = GetFolderList(_strRootPath,astrFolder);
		if(ret)
			ret = AddTreeFolder(hRoot,astrFolder,_strRootPath,0);
		if(ret == false)
			return	false;

		Expand(hRoot);

		return	ret;
	}


	//
	//	ツリーアイテムの示すフォルダへのパス取得
	//
	bool	GetPath(HTREEITEM hItem,CAtlString& strPath)
	{
		BOOL	ret;
		TCHAR	pszBuff[MAX_PATH];
		CAtlString	strBuff;

		//本来ならばITEMDATAを利用してフォルダパスを保存しておくべきだが
		//ここではツリーに登録したツリーアイテム名からパスを復元している
		strPath = _T("");
		while(1)
		{
			ret = GetItemText(hItem,pszBuff,MAX_PATH);
			if(ret == FALSE)
				return	false;
			strBuff = pszBuff;
			strBuff += _T("\\");
			strPath = strBuff + strPath;

			hItem = GetParentItem(hItem);
			if(hItem == NULL)
				break;
		}

		//先頭がルート名になっているはず
		if(strPath.Find(_strRootName) != 0)
		{
			strPath = _T("");
			return	false;
		}

		//先頭のルート名部分をルートパスへ置き換える
		strPath = strPath.Right(strPath.GetLength() - _strRootName.GetLength() - 1);
		strPath = _strRootPath + strPath;

		return	true;
	}



protected:

	//
	//ツリーアイテムを開いているときの処理
	//
	//孫アイテムが登録されていない場合は孫を登録する
	//
	LRESULT OnItemExpanding(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
	{
		LPNMTREEVIEW	pNmTreeView = (LPNMTREEVIEW)pnmh;

		if(pNmTreeView == NULL)
			return	0;

		//ツリーが閉じるだけなら処理はなし
		if(pNmTreeView->action == TVE_COLLAPSE)
			return	0;


		bool		ret;
		HTREEITEM	hItem;
		CAtlString	strPath;

		//開かれようとしているフォルダのパスを取得
		hItem = pNmTreeView->itemNew.hItem;
		ret = GetPath(hItem,strPath);
		if(ret == false)
			return	0;


		//孫フォルダー情報までツリーに登録されているかどうか
		if(IsLoadedFolder(hItem))
			return	0;


		BOOL		bRet;
		HTREEITEM	hChildItem;
		TCHAR		pszBuff[MAX_PATH];

		//これから開くツリーの子を列挙。孫フォルダーがツリーに内場合は追加
		hChildItem = GetChildItem(pNmTreeView->itemNew.hItem);
		while(GetParentItem(hChildItem) == hItem)
		{
			if(GetChildItem(hChildItem))
				break;

			bRet = GetItemText(hChildItem,pszBuff,MAX_PATH);
			if(bRet == FALSE)
				return	0;

			//フォルダ情報をツリーに追加
			{
				CAtlString	strBuff;
				CAtlArray<CAtlString>	astrChildFolder;

				//孫フォルダーリスト取得
				strBuff = strPath + pszBuff;
				ret = GetFolderList(strBuff,astrChildFolder);

				//ツリーに追加
				if(ret)
					AddTreeFolder(hChildItem,astrChildFolder);
			}

			hChildItem = GetNextItem(hChildItem,TVGN_NEXTVISIBLE);
			if(hChildItem == NULL)
				break;
		}

		return	0;
	}








	//
	//	hParentの子フォルダーはその子フォルダー情報を持っているかどうか
	//
	bool	IsLoadedFolder(HTREEITEM hParent)
	{
		HTREEITEM	hChildItem;

		hChildItem = GetChildItem(hParent);
		while(GetParentItem(hChildItem) == hParent)
		{
			if(GetChildItem(hChildItem))
				return	true;

			hChildItem = GetNextItem(hChildItem,TVGN_NEXTVISIBLE);
			if(hChildItem == NULL)
				break;
		}

		return	false;
	}









	bool	AddTreeFolder(HTREEITEM hParent,const CAtlArray<CAtlString>& astrFolderName,LPCTSTR pszPath=NULL,int nAddChild=0)
	{
		size_t		i;
		size_t		nSize;
		HTREEITEM	hChild;

		if(pszPath == NULL && nAddChild)
			return	false;		//pszPath==NULLのときはnAddChild==0でないとダメ

		nSize = astrFolderName.GetCount();
		for(i = 0; i < nSize; i++)
		{
			hChild = InsertItem(astrFolderName[i],hParent,TVI_LAST);
			if(hChild == NULL)
				continue;

			if(nAddChild == 0)
				continue;

			bool		ret;
			CAtlString	strPath;
			CAtlArray<CAtlString>	astrChildFolder;

			strPath = pszPath;
			strPath += astrFolderName[i];
			strPath += _T("\\");
			ret = GetFolderList(strPath,astrChildFolder);
			if(ret == false)
				continue;

			AddTreeFolder(hChild,astrChildFolder,strPath,nAddChild - 1);
		}

		return	true;
	}


	//
	//	ソート用仮想関数
	//
	//派生クラスでastrFolderName[nFirstIndex]からnCountの数だけ任意の方法で並べ替える
	//
	virtual	bool	SortData(CAtlArray<CAtlString>& astrFolderName,size_t nFirstIndex,size_t nCount)
	{
		return	true;
	}


	//
	//	フォルダの列挙
	//
	//指定したフォルダ内のフォルダを列挙します。
	//	pszPath内のフォルダを列挙してastrFolderName配列へ追加する
	//
	bool	GetFolderList(LPCTSTR pszPath,CAtlArray<CAtlString>& astrFolderName)
	{
		BOOL			bFind;
		HANDLE			hFind;
		WIN32_FIND_DATA	FindFileData;
		CAtlString		strFolder;

		if(pszPath == NULL || _tcslen(pszPath) == 0)
			return	false;

		strFolder = pszPath;
		if(strFolder.Right(1) != _T('\\') && strFolder.Right(1) != _T('/'))
			strFolder += _T("\\");

		hFind = ::FindFirstFile(strFolder + _T("*.*"),&FindFileData);
		if(hFind == INVALID_HANDLE_VALUE)
			return	false;

		size_t	nBefore;
		size_t	nAfter;

		nBefore = astrFolderName.GetCount();

		bFind = TRUE;
		while(bFind)
		{
			if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				//リンクフォルダや隠しフォルダは表示しない
				if((FindFileData.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_HIDDEN)) == 0)
					if(_tcsncmp(FindFileData.cFileName,_T("."),1) != 0 && _tcsncmp(FindFileData.cFileName,_T(".."),2) != 0 )
						astrFolderName.Add(FindFileData.cFileName);
			}

			bFind = ::FindNextFile(hFind,&FindFileData);
		}
		::FindClose(hFind);

		nAfter = astrFolderName.GetCount();

		if(nAfter == nBefore)
			return	true;

		return	SortData(astrFolderName,nBefore + 1,nAfter - nBefore);
	}
};
■MainDlg.hのCMainDlgにメンバー変数を追加
	CDnpFolderTreeCtrl	_wndTree;
■MainDlg.hのCMainDlgのメッセージマップとOnInitDialogに追加
	BEGIN_MSG_MAP(CMainDlg)
(。。。省略。。。)
		CHAIN_MSG_MAP_MEMBER(_wndTree)
	END_MSG_MAP()

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{

(。。。省略。。。)
		RECT	rect;		

		GetClientRect(&rect);
		rect.top += 10;
		rect.bottom -= 10;
		rect.left += 10;
		rect.right -=100;

		_wndTree.Create(m_hWnd,rect,NULL,WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TVS_HASBUTTONS | TVS_HASLINES,WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT); 
		_wndTree.Initialize(_T("cドライブ"),_T("c:\\"));

		return TRUE;
	}


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

2007年02月24日

CAtlFileでテキストファイルを読み込む

test115.gif
ここではATLに備わるファイル操作用のクラス「CAtlFile」を利用してテキストファイルを読み込み、メッセージボックスに表示した。

テキストファイルを読み込むときに一番注意するべき点は「文字コード」。Windows 9x時代にはほとんどのテキストファイルが通常のシフトJIS(SHIFT-JIS)で書かれていたためあまり注意する必要はなかった。

しかしインターネットやWindows XP/Vistaが普及するに連れてテキストファイルがシフトJISではなく、(まだまだ少ないものの)ユニコードやUTF-8で書かれるケースが増えてきた。また最近ではVisual Studioでアプリケーションをビルドするときに「ユニコードビルド」することも多い。

特にユニコードビルドをしている場合には意図せずにシフトJISのテキストファイルをユニコード形式で扱ってしまい、読み込んだファイルが文字化けしてしまうトラブルも起こりえるので注意が必要だ。


依存環境:ATL
#include <atlfile.h>


bool	Test()
{
	bool		ret;
	HRESULT		hr;
	ULONGLONG	nLen;
	CAtlFile	cFile;

	//ファイルを読み込みモードで開く
	hr = cFile.Create(_T("テスト.txt"),GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING);
	if(FAILED(hr))
		return	false;	//ファイルを開くのに失敗

	//ファイルサイズを取得
	hr = cFile.GetSize(nLen);

	ret = false;
	if(SUCCEEDED(hr))
	{
		//SHIFT-JISモードで読み込みたいのでcharでバッファーを確保
		char*	pszBuff;

		pszBuff = new char[(size_t)nLen + 1];
		if(pszBuff)
		{
			//ファイルにはNULL文字がないので付加しておく
			pszBuff[nLen] = NULL;

			//ファイルを読み込む
			hr = cFile.Read(pszBuff,(DWORD)nLen);

			//読み込んだ内容をメッセージボックスで表示
			if(SUCCEEDED(hr))
			{
				ret = true;
				::MessageBoxA(NULL,pszBuff,"",MB_OK);
			}

			delete	pszBuff;
		}
	}


	//ファイルを閉じる
	cFile.Close();

	return	ret;
}

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

CAtlFileでユニコード形式のテキストファイルを読み込む

test116.gif
ここではATLに備わるファイル操作用のクラス「CAtlFile」を利用してユニコード形式のテキストファイルを読み込み、メッセージボックスに表示した。

ユニコード文字列を読み込む際に注意すべき点はファイルサイズ=文字長という関係が成り立たないという点だ。

依存環境:ATL
#include <atlfile.h>


bool	Test()
{
	bool		ret;
	HRESULT		hr;
	ULONGLONG	nLen;
	CAtlFile	cFile;

	//ファイルを読み込みモードで開く
	hr = cFile.Create(_T("テスト.txt"),GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING);
	if(FAILED(hr))
		return	false;	//ファイルを開くのに失敗

	//ファイルサイズを取得
	hr = cFile.GetSize(nLen);

	ret = false;
	if(SUCCEEDED(hr))
	{
		//ユニコードモードで読み込みたいのでWCHARでバッファーを確保
		WCHAR*	pszBuff;

		//ファイルサイズ=WCHARの数というわけではなないので、BYTEでメモリを確保してキャスト
		pszBuff = (WCHAR*)new BYTE[(size_t)nLen + sizeof(WCHAR)];
		if(pszBuff)
		{
			//ファイルにはNULL文字がないので付加しておく
			((BYTE*)pszBuff)[nLen+0] = NULL;
			((BYTE*)pszBuff)[nLen+1] = NULL;

			//ファイルを読み込む
			hr = cFile.Read(pszBuff,(DWORD)nLen);

			//読み込んだ内容をメッセージボックスで表示
			if(SUCCEEDED(hr))
			{
				//このメッセージボックス関数は基本的にWindows 9xでは使えないので注意!
				::MessageBoxW(NULL,pszBuff,L"",MB_OK);
				ret = true;
			}

			delete	pszBuff;
		}
	}

	//ファイルを閉じる
	cFile.Close();

	return	ret;
}

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

CAtlFileでUTF-8形式のテキストファイルを読み込む

test117.gif
ここではATLに備わるファイル操作用のクラス「CAtlFile」を利用してUTF-8形式のテキストファイルを読み込み、メッセージボックスに表示した。

UTF-8からユニコードへの変換にはMultiByteToWideCharを利用する。

依存環境:ATL
#include <atlfile.h>


//
//	UTF8文字列を普通のユニコード文字列に変換する
//
//CP_UTF8はWindows98では利用できない?
//
WCHAR*	ConvertUTF8ToUnicode(const char* pszUTF8)
{
	int		nLen;
	WCHAR*	pszWchar;

	//Unicodeに必要な文字数の取得
	nLen = ::MultiByteToWideChar(CP_UTF8,0,pszUTF8,-1,NULL,0);
	pszWchar = new WCHAR[nLen];
	if(pszWchar == NULL)
		return	NULL;

	//変換
	nLen = ::MultiByteToWideChar(CP_UTF8,0,pszUTF8,(int)::strlen(pszUTF8)+1,pszWchar,nLen);
	if(nLen)
		return	pszWchar;

	delete	pszWchar;

	return	NULL;
}







bool	Test()
{
	bool		ret;
	HRESULT		hr;
	ULONGLONG	nLen;
	CAtlFile	cFile;

	//ファイルを読み込みモードで開く
	hr = cFile.Create(_T("テスト.txt"),GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING);
	if(FAILED(hr))
		return	false;	//ファイルを開くのに失敗

	//ファイルサイズを取得
	hr = cFile.GetSize(nLen);

	ret = false;
	if(SUCCEEDED(hr))
	{
		char*	pszBuff;

		pszBuff = new char[(size_t)nLen + 1];
		if(pszBuff)
		{
			//ファイルにはNULL文字がないので付加しておく
			pszBuff[nLen] = NULL;

			//ファイルを読み込む
			hr = cFile.Read(pszBuff,(DWORD)nLen);

			//読み込んだ内容をメッセージボックスで表示
			if(SUCCEEDED(hr))
			{
				WCHAR*	pwszBuff;

				//UTF8をUnicodeに変換
				pwszBuff = ConvertUTF8ToUnicode(pszBuff);

				if(pwszBuff)
				{
					//このメッセージボックス関数は基本的にWindows 9xでは使えないので注意!
					::MessageBoxW(NULL,pwszBuff,L"",MB_OK);
					ret = true;

					delete	pwszBuff;
				}
			}

			delete	pszBuff;
		}
	}

	//ファイルを閉じる
	cFile.Close();

	return	ret;
}


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

Vistaのバグ(その1) ファイル名を変更できない!

vista_bug1_01.gif
Windows Vistaを使い始めてすぐに気が付いたバグ。

再現するには...まず適当なフォルダを作り、その中に長いファイル名のファイルを作成する。ファイル名の長さはウインドウのサイズに依存するので一概に言えない。

vista_bug1_02.gif
エクスプローラーでの表示モードを「一覧」に切り替える。

vista_bug1_03.gif
長いファイル名のファイルを右クリックして現れたメニューから「名前の変更」を選択する。キーボードの[F2]キーを利用するなどの別の方法でも構わない。

vista_bug1_04.gif
...すると通常ならファイル名が変更できるはずなのだが、画像のような状態になってしまう。左側にちょっとだけ入力欄になっているのが見えるだけで、ほかは全然見えない。スクロールバーも表示されないのでどうしようもない。

vista_bug1_05.gif
さらにその状態でウインドウ内をクリックすると何も表示されなくなってしまう。表示を元に戻すには[F5]キーを押すなどして画面表示を更新する。

ちなみにこの現象はファイルを削除したときなど様々なファイル操作時に同じように起こる。私はエクスプローラーを常に一覧表示モードにしているので本当に不便。


Vistaのバグ(その2) 残り時間が表示されない!

vista_bug2.gif
これは重要ではない問題だが、ファイルのコピーや移動時に残り時間が表示されないというもの。

図は5.44GBのファイルを移動しているところ。残り330MBなので5GB以上移動し終わったところだが、残り時間が「計算中...」となっていて表示されていない。

ファイルの移動やコピーに関するバグとして致命的なのは、ファイルをエクスプローラー間でドラッグ・アンド・ドロップした直後にエクスプローラーのウインドウを閉じたときに、移動やコピー操作が勝手にキャンセルされてしまうというもの。
この現象はタイミングの関係なのか、いつも起きるというわけではないため再現するのが難しい。


Intel VTに対応したVirtual PC 2007が公開されている

vpc2007_01.gif
ちょっと前にVirtual PC 2007(日本語版)が公開された。Windows Vistaに正式対応、さらにIntel VTなどのハードウエア仮想に対応した。

vpc2007_02.gif
もちろん無料でダウンロードして使える。使用制限などは一切ない。

vpc2007_03.gif
プロダクトキーが最初から入力されていてマスクされている。これは最近のマイクロソフトの体験版や無償化されたソフトに多い。

vpc2007_04.gif

vpc2007_05.gif

vpc2007_06.gif
インストールが終わって設定を見てみると...「ハードウエアの仮想化」という項目があった。ここで「ハードウエア依存の仮想化技術を有効にする」にチェックを入れるとIntel VT(Intel Virtualization Technology)やAMD-Vが使えるようになる。もちろん利用しているPCのCPUやBIOSが対応していないと利用できないが。

ちなみにWindows Vista上でIntel VTを有効にしてVirtual PCでWindows Vistaを起動したが...対して変わったようには思えなかった。劇的な効果があるわけではない模様。


2007年02月26日

IEなどのアプリケーションからドラッグアンドドロップを受け入れる

test118.gif
Microsoft WordやInternet Explorerなど多くのアプリケーションではドラッグ・アンド・ドロップによってほかのアプリケーションとデータのやり取りができるように作られている。自分で作成したアプリケーションでドラッグ・アンド・ドロップによりデータを受け取るにはIDropTargetを利用した処理を実装する。

ここではIDropTargetの実装はが簡単にできるようにCDnpDropTargetImplという補助クラスを作成した。図の例ではInternet Explorerからドラッグ・アンド・ドロップされたデータのうち、テキストデータをメッセージボックスに表示している。

自分の好みに応じた処理を実装したい場合はCDropTarget内を変更する。そのほかの部分は多くの場合、共通に利用できる。

■ATL/WTLアプリケーションうインザードでダイアログベースのプロジェクトを作成する

■自動生成したソースコードのうち「プロジェクト名.cpp」のファイルを編集して、OleInitializeとOleUninitializeが実行されるようにする

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
(...省略...)

	//ドラッグアンドドロップ用
	::OleInitialize(NULL);

	int nRet = Run(lpstrCmdLine, nCmdShow);

	//ドラッグアンドドロップ用
	::OleUninitialize();

	_Module.Term();
	::CoUninitialize();

	return nRet;
}


■MainDlg.hの先頭に以下のようなドラッグアンドドロップのメイン処理の書かれたクラス宣言を追加する

#include "DnpDropTargetImpl.h"

class CDropTarget : public CDnpDropTargetImpl
{
protected:
	virtual	bool	OnDrop(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
	{
		//テキストのみ処理
		if(IsSupport(CF_TEXT))
		{
			bool		ret;
			CAtlString	strData;

			//テキストデータを取得
			ret = GetTextData(strData);
			if(ret)
			{
				//キー状態に応じてドラッグ・アンド・ドロップ元の動作を変えれる。
				//移動(DROPEFFECT_MOVE)にすると、ドロップ元のデータは消える。
				if(grfKeyState & MK_SHIFT)
					*pdwEffect = DROPEFFECT_MOVE;
				else
					*pdwEffect = DROPEFFECT_COPY;

				//取得文字列をメッセージボックスに表示
				::MessageBox(NULL,strData,_T(""),MB_OK);


				return	true;
			}
		}

		return	false;
	}

	virtual	bool	OnDragOver(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
	{
		//ドラッグ元がテキスト形式(CF_TEXT)をサポートしている場合のみ処理
		if(IsSupport(CF_TEXT))
		{
			//マウスカーソルの形を指定する。キー状態に応じて設定可能
			//使用可能なキーは以下の通り
			//MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON

			//ここではシフトキーのみ監視
			if(grfKeyState & MK_SHIFT)
				*pdwEffect = DROPEFFECT_MOVE;
			else
				*pdwEffect = DROPEFFECT_COPY;

			return	true;
		}

		return	false;
	}
};


■MainDlg.hのクラスCMainDlg内にメンバ変数を追加する

	//メンバ変数追加
	CComPtr<IDropTarget>	_pIDropTarget;

■MainDlg.hのクラスCMainDlgにあるOnInitDialog関数内にドラッグアンドドロップの登録処理を追加する

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		(...省略...)

		HRESULT	hr;
		CComObject<CDropTarget>*	pCDropTarget;

		pCDropTarget = NULL;
		hr = CComObject<CDropTarget>::CreateInstance(&pCDropTarget);

		if(SUCCEEDED(hr))
		{
			_pIDropTarget = NULL;
			hr = pCDropTarget->QueryInterface(IID_IDropTarget,(void**)&_pIDropTarget);
		}

		//ドラッグ・アンド・ドロップの受け入れ登録
		if(SUCCEEDED(hr))
			hr = ::RegisterDragDrop(m_hWnd,_pIDropTarget);

		ATLASSERT(SUCCEEDED(hr));		//失敗した場合は起動時にOleInitializeかCoInitializeを実行していない可能性大

		return TRUE;
	}

■MainDlg.hのクラスCMainDlgにあるCloseDialog関数内でRevokeDragDropを実行してドラッグアンドドロップの登録解除処理をする

	void CloseDialog(int nVal)
	{
		//ドラッグ・アンド・ドロップの受け入れ登録解除
		::RevokeDragDrop(m_hWnd);

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


■ドラッグ・アンド・ドロップ用のクラス定義をファイル名「DnpDropTargetImpl.h」として保存

#pragma	once

#include "atlstr.h"


//
//
//■使用例
//
//#include "DnpDropTargetImpl.h"
//
//
/////
/////プログラム起動時にCoInitializeとOleInitializeが実行されるようにする
/////プログラム終了時にOleUninitializeとCoUninitializeが実行されるようにする
/////
//int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
//{
//	HRESULT hRes = ::CoInitialize(NULL);
//
//	(...省略...)
//
//	//ドラッグアンドドロップ用
//	::OleInitialize(NULL);
//
//	(メイン処理)
//
//	//ドラッグアンドドロップ用
//	::OleUninitialize();
//
//	::CoUninitialize();
//
//	return nRet;
//}
//
//
////
////ドラッグアンドドロップの挙動を実装したクラスを作成
////
//class CDropTarget : public CDnpDropTargetImpl
//{
//protected:
//	virtual	bool	OnDrop(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
//	{
//		//テキストのみ処理
//		if(IsSupport(CF_TEXT))
//		{
//			bool		ret;
//			CAtlString	strData;
//
//			//テキストデータを取得
//			ret = GetTextData(strData);
//			if(ret)
//			{
//				//キー状態に応じてドラッグ・アンド・ドロップ元の動作を変えれる。
//				//移動(DROPEFFECT_MOVE)にすると、ドロップ元のデータは消える。
//				if(grfKeyState & MK_SHIFT)
//					*pdwEffect = DROPEFFECT_MOVE;
//				else
//					*pdwEffect = DROPEFFECT_COPY;
//
//				//取得文字列をメッセージボックスに表示
//				::MessageBox(NULL,strData,_T(""),MB_OK);
//
//
//				return	true;
//			}
//		}
//
//		return	false;
//	}
//
//	virtual	bool	OnDragOver(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
//	{
//		//ドラッグ元がテキスト形式(CF_TEXT)をサポートしている場合のみ処理
//		if(IsSupport(CF_TEXT))
//		{
//			//マウスカーソルの形を指定する。キー状態に応じて設定可能
//			//使用可能なキーは以下の通り
//			//MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON
//
//			//ここではシフトキーのみ監視
//			if(grfKeyState & MK_SHIFT)
//				*pdwEffect = DROPEFFECT_MOVE;
//			else
//				*pdwEffect = DROPEFFECT_COPY;
//
//			return	true;
//		}
//
//		return	false;
//	}
//};
//
//
////
////ドラッグアンドドロップを受け入れたいウインドウに処理を追加
////
//class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
//		public CMessageFilter, public CIdleHandler
//{
//
//	//メンバ変数追加
//	CComPtr<IDropTarget>	_pIDropTarget;
//
//public:
//	(...省略...)
//
//	//ウインドウ初期化時に登録処理を追加
//	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	{
//		(...省略...)
//
//
//		HRESULT	hr;
//		CComObject<CDropTarget>*	pCDropTarget;
//
//		pCDropTarget = NULL;
//		hr = CComObject<CDropTarget>::CreateInstance(&pCDropTarget);
//
//		if(SUCCEEDED(hr))
//		{
//			_pIDropTarget = NULL;
//			hr = pCDropTarget->QueryInterface(IID_IDropTarget,(void**)&_pIDropTarget);
//		}
//
//		//ドラッグ・アンド・ドロップの受け入れ登録
//		if(SUCCEEDED(hr))
//			hr = ::RegisterDragDrop(m_hWnd,_pIDropTarget);
//
//		ATLASSERT(SUCCEEDED(hr));		//失敗した場合は起動時にOleInitializeかCoInitializeを実行していない可能性大
//
//		return TRUE;
//	}
//
//	//ウインドウ終了時に登録解除処理を追加
//	void CloseDialog(int nVal)
//	{
//		//ドラッグ・アンド・ドロップの受け入れ登録解除
//		::RevokeDragDrop(m_hWnd);
//
//		DestroyWindow();
//		::PostQuitMessage(nVal);
//	}
//};
//
//
class CDnpDropTargetImpl : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CDnpDropTargetImpl>,
	public IDropTarget
{
protected:

	IDataObject*	_pIDataObject;
public:

	BEGIN_COM_MAP(CDnpDropTargetImpl)
		COM_INTERFACE_ENTRY(IDropTarget)
	END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

    CDnpDropTargetImpl()
	{
		_pIDataObject = NULL;
	}


	///////////////////////////////////////
	//	IDropTargetの実装
	//


	//
	//ドラッグ・アンド・ドロップの開始
	//
	STDMETHOD(DragEnter)(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
	{
		//_pIDataObjectにIDataObjectを保存しておく
		if(_pIDataObject)
			_pIDataObject->Release();
		_pIDataObject = NULL;
		pDataObject->QueryInterface(IID_IDataObject,(void**)&_pIDataObject);

		//以下共通処理
		return DragOver(grfKeyState,pt,pdwEffect);
	}


	//
	//ドラッグ・アンド・ドロップ中
	//
	STDMETHOD(DragOver)(DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
	{
		if(_pIDataObject == NULL || pdwEffect == NULL)
			return	E_FAIL;

		*pdwEffect = DROPEFFECT_NONE;

		return OnDragOver(_pIDataObject,grfKeyState,pt,pdwEffect) ? S_OK : E_FAIL;
	}


	//
	//ドラッグ・アンド・ドロップ終了
	//
	STDMETHOD(DragLeave)(void)
	{
		//_pIDataObjectが必要なくなったので解放
		if(_pIDataObject)
			_pIDataObject->Release();
		_pIDataObject = NULL;
		return	S_OK;
	}


	//
	//ドラッグ・アンド・ドロップ終了
	//
	STDMETHOD(Drop)(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect)
	{
		bool	ret;

		//_pIDataObjectを終了時のものに変更
		if(_pIDataObject)
			_pIDataObject->Release();
		_pIDataObject = NULL;

		//GetTextData()などをOnDrop内で使えるようにQueryInterfaceが必要
		pDataObject->QueryInterface(IID_IDataObject,(void**)&_pIDataObject);

		ret = OnDrop(_pIDataObject,grfKeyState,pt,pdwEffect);

		if(_pIDataObject)
			_pIDataObject->Release();
		_pIDataObject = NULL;

		return	ret ? S_OK : E_FAIL;
	}

	//
	//	IDropTargetの実装ここまで
	///////////////////////////////////////


protected:

	virtual	bool	OnDrop(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect) = 0;
	virtual	bool	OnDragOver(IDataObject* pDataObject,DWORD grfKeyState,POINTL pt,DWORD* pdwEffect) = 0;



public:


	//
	//テキストデータの取得
	//
	bool	GetTextData(CAtlString& strData)
	{
		HRESULT		hr;
		FORMATETC	sFormatEtc;
		STGMEDIUM	sStgMedium;

		strData = _T("");
		if(_pIDataObject == NULL)
			return	false;

		::ZeroMemory(&sFormatEtc,sizeof(FORMATETC));
		sFormatEtc.cfFormat	= CF_TEXT;
		sFormatEtc.dwAspect	= DVASPECT_CONTENT;
		sFormatEtc.lindex	= -1;
		sFormatEtc.tymed	= TYMED_HGLOBAL;

		hr = _pIDataObject->GetData(&sFormatEtc,&sStgMedium);
		if(FAILED(hr))
			return	false;

		strData = (char*)::GlobalLock(sStgMedium.hGlobal);
		::GlobalUnlock(sStgMedium.hGlobal);
		::ReleaseStgMedium(&sStgMedium);

		return	true;
	}



	//
	//対応形式のチェック
	//
	bool	IsSupport(CLIPFORMAT nFormat)
	{
		ULONG	i;
		ULONG	nCount;
		HRESULT	hr;
		FORMATETC		pFormatEtc[256];
		IEnumFORMATETC*	pIEnumFORMATETC;

		pIEnumFORMATETC = NULL;
		hr = _pIDataObject->EnumFormatEtc(DATADIR_GET,&pIEnumFORMATETC);
		if(FAILED(hr) || pIEnumFORMATETC == NULL)
			return	false;

		hr = pIEnumFORMATETC->Next(256,pFormatEtc,&nCount);
		if(FAILED(hr))
			return	false;

		for(i = 0; i < nCount; i++)
		{
			if(pFormatEtc[i].cfFormat == nFormat)
				return	true;
		}

		return	false;
	}
};  


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

前の10件 1  2  3





usefullcode@gmail.com

About 2007年02月

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

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

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

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