« 2006年11月 | メイン | 2007年01月 »

前の10件 1  2  3  4  5  6  7  8  9  10

2006年12月 記事一覧

第8回 画像の余白を塗りつぶす

ImageViewer08_01.gif
これまでに縦横比を保ったままウインドウの中央に画像を表示できるようになった。しかしウインドウのサイズを変更するとこのようになってしまう。これは以前に描画のちらつきをなくすためWM_ERASEBKGNDメッセージを無効化し、背景の描画がされないようにしたためだ。

ImageViewer08_02.gif
今回は画像以外の背景を塗りつぶすことでこの現象を抑える。

ImageViewer08_03.gif まずImageViewerView.hの先頭にatlmisc.hのインクルード宣言を追加する。このヘッダーはCRectというクラスを利用するためのものだ。
#include "atlmisc.h"

ImageViewer08_04.gif そして画像の余白部分を塗る処理をOnPaint()内に置く。背景はGetSysColorBrush()によりウインドウの背景色と同じブラシを取得してそれで塗りつぶす。塗りつぶすための命令はFillRectで、ここではCPaintDC内のメンバー関数を利用している。
		//余白描画
		HBRUSH	hBrush;
		hBrush = ::GetSysColorBrush(COLOR_WINDOW);
		if(nX == 0)
		{
			//上下の余白
			dc.FillRect(&CRect(0,0,nWindowWidth,nY),hBrush);
			dc.FillRect(&CRect(0,nY + nHeight,nWindowWidth,nWindowHeight),hBrush);
		}
		else
		{
			//左右の余白
			dc.FillRect(&CRect(0,0,nX,nWindowHeight),hBrush);
			dc.FillRect(&CRect(nX + nWidth,0,nWindowWidth,nWindowHeight),hBrush);
		}
		::DeleteObject(hBrush);

ImageViewer08_05.gif
これでウインドウサイズを変更しても余白部分が変な表示にならなくなった。

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

フォルダを作る

Windowsにはフォルダを作成するAPIなどが数多く実装されている。そのうち有名なのは以下の5つだ。

int _tmkdir(LPCTSTR dirname);

BOOL CreateDirectory(LPCTSTR lpPathName,LPSECURITY_ATTRIBUTES lpSecurityAttributes);

BOOL CreateDirectoryEx(LPCTSTR lpTemplateDirectory,LPCTSTR lpNewDirectory,LPSECURITY_ATTRIBUTES lpSecurityAttributes);

int SHCreateDirectory(HWND hwnd,LPCWSTR pszPath);

int SHCreateDirectoryEx(HWND hwnd,LPCTSTR pszPath,SECURITY_ATTRIBUTES *psa);

■_tmkdir
対応:全プラットフォーム
最大パス長:不明

■CreateDirectory
対応:Windows 95以降
最大パス長:248文字(CreateDirectoryWは「\\?\」の付加で3万2000文字)

■CreateDirectoryEx
対応:Windows 95以降
最大パス長:MAX_PATH(CreateDirectoryExWは「\\?\」の付加で3万2000文字)

■SHCreateDirectory
対応:Windows 2000以降
最大パス長:MAX_PATH
ユニコードのみ対応

■SHCreateDirectoryEx
対応:Windows Me以降
最大パス長:248


これらのAPIは作成結果の戻り方も対応OSもさまざま。とりあえず利用するには以下のようにする。

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

#include "direct.h"
bool	CreateFolderBy_tmkdir(LPCTSTR pszFolder)
{
	return	::_tmkdir(pszFolder) == 0 ? true : false;
}


bool	CreateFolderByCreateDirectory(LPCTSTR pszFolder)
{
	return	::CreateDirectory(pszFolder,NULL) ? true : false;
}

bool	CreateFolderByCreateDirectoryEx(LPCTSTR pszFolder)
{
	return	::CreateDirectoryEx(_T(""),pszFolder,NULL) ? true : false;
}

#include "shlobj.h"
bool	CreateFolderBySHCreateDirectory(LPCWSTR pszFolder)
{
	return	::SHCreateDirectory(NULL,pszFolder) == ERROR_SUCCESS ? true : false;
}

bool	CreateFolderBySHCreateDirectoryEx(LPCTSTR pszFolder)
{
	return	::SHCreateDirectoryEx(NULL,pszFolder,NULL) == ERROR_SUCCESS ? true : false;
}

bool	Test(void)
{
	bool	ret_tmkdir;
	bool	retCreateDirectory;
	bool	retCreateDirectoryEx;
	bool	retSHCreateDirectory;
	bool	retSHCreateDirectoryEx;
	CAtlString	strMessage;

	//フォルダを作成
	ret_tmkdir				= CreateFolderBy_tmkdir(_T("c:\\testA\\"));
	retCreateDirectory		= CreateFolderByCreateDirectory(_T("c:\\testB\\"));
	retCreateDirectoryEx	= CreateFolderByCreateDirectoryEx(_T("c:\\testC\\"));
	retSHCreateDirectory	= CreateFolderBySHCreateDirectory(L"c:\\testD\\");
	retSHCreateDirectoryEx	= CreateFolderBySHCreateDirectoryEx(_T("c:\\testE\\"));

	strMessage = _T("1階層のフォルダ作成結果\n");
	if(ret_tmkdir)
		strMessage += _T("_tmkdir成功\n");
	if(retCreateDirectory)
		strMessage += _T("CreateDirectory成功\n");
	if(retCreateDirectoryEx)
		strMessage += _T("CreateDirectoryEx成功\n");
	if(retSHCreateDirectory)
		strMessage += _T("SHCreateDirectory成功\n");
	if(retSHCreateDirectoryEx)
		strMessage += _T("SHCreateDirectoryEx成功\n");


	//一気に3階層のフォルダを作成
	ret_tmkdir				= CreateFolderBy_tmkdir(_T("c:\\test1\\test\\test\\"));
	retCreateDirectory		= CreateFolderByCreateDirectory(_T("c:\\test2\\test\\test\\"));
	retCreateDirectoryEx	= CreateFolderByCreateDirectoryEx(_T("c:\\test3\\test\\test\\"));
	retSHCreateDirectory	= CreateFolderBySHCreateDirectory(L"c:\\test4\\test\\test\\");
	retSHCreateDirectoryEx	= CreateFolderBySHCreateDirectoryEx(_T("c:\\test5\\test\\test\\"));

	strMessage += _T("\n3階層のフォルダ作成結果\n");
	if(ret_tmkdir)
		strMessage += _T("_tmkdir成功\n");
	if(retCreateDirectory)
		strMessage += _T("CreateDirectory成功\n");
	if(retCreateDirectoryEx)
		strMessage += _T("CreateDirectoryEx成功\n");
	if(retSHCreateDirectory)
		strMessage += _T("SHCreateDirectory成功\n");
	if(retSHCreateDirectoryEx)
		strMessage += _T("SHCreateDirectoryEx成功\n");

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

	return	true;
}

test83.gif
ここでは「C:\」にそれぞれのAPIを利用してフォルダを作成した。実行結果を見ると、新しいフォルダを1つだけ作る場合はどのAPIも成功する。しかし一気に数階層のフォルダを作ろうとした場合にはSHCreateDirectory系以外は失敗した。
つまりSHCreateDirectory系以外を利用する場合は、複数のフォルダを一気に作ろうと試みないようにフォルダの存在をチェックしつつ実行する必要がある。

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

Windowsの壁紙を変える

プログラム上からの壁紙設定はIActiveDesktopを利用する。
設定変更後にApplyChanges(AD_APPLY_ALL);を実行しなければいけないことに注意。また、IActiveDesktopはインクルードファイルとしてwininet.hを必要とする。

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

#include "wininet.h"
#include "shlobj.h"


//
//	壁紙の設定
//
//dwTypeは以下の値を設定
//	#define WPSTYLE_CENTER      0
//	#define WPSTYLE_TILE        1
//	#define WPSTYLE_STRETCH     2
//
bool	SetWallpaper(LPCTSTR pszFile,DWORD dwStyle)
{
	HRESULT	hr;
	IActiveDesktop*	pIActiveDesktop;

	pIActiveDesktop = NULL;
	hr = ::CoCreateInstance(CLSID_ActiveDesktop,NULL,CLSCTX_INPROC_SERVER,IID_IActiveDesktop,(void**)&pIActiveDesktop);
	if(FAILED(hr) || pIActiveDesktop == NULL)
		return	false;

	hr = pIActiveDesktop->SetWallpaper((CAtlStringW)pszFile,0);
	if(SUCCEEDED(hr))
	{
		WALLPAPEROPT	sInfo;

		sInfo.dwSize = sizeof(WALLPAPEROPT);
		sInfo.dwStyle = dwStyle;
		hr = pIActiveDesktop->SetWallpaperOptions(&sInfo,0);
	}
	if(SUCCEEDED(hr))
		hr = pIActiveDesktop->ApplyChanges(AD_APPLY_ALL);

	pIActiveDesktop->Release();

	return	SUCCEEDED(hr) ? true : false;
}


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

	SetWallpaper(_T("test.jpg"),WPSTYLE_CENTER);

	::CoUninitialize();
}

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

2006年12月24日

第9回 簡単なエラー処理を入れる

ImageViewer09_01.gif
次回は画像を読み込む処理を実装する予定だ。それの前準備として簡単なエラー処理の追加と変数名の変更をする。
まずは中途半端な位置で定義されている「Bitmap* pImage」の位置を動かすとともに変数名を変える。

ImageViewer09_02.gif 一番上に移動し、変数名の先頭に「_」を付加した。クラスのメンバー変数は頭に「_」もしくは「m_」をつけるとソースコードが読みやすい。またポインタ型は「p」、intは「n」、DWORDは「dw」...のように変数の命名規則を社内や開発チームなどの単位で統一しておくといい。 さらに変数の初期化をするためにコンストラクタも用意した。
	Bitmap*		_pImage;
	CImageViewerView()
	{
		_pImage = NULL;
	}

ImageViewer09_03.gif 変数名の宣言変更に伴ってその変数を利用している場所も順次変数名を変えておく。 また、OnDestroyとOnPaintではそれぞれ_pImage==NULLのときは処理しないようにしておく。これにより画像が読み込まれていないときはしてはOnPaintやOnDestroy内で_pImageにアクセスすることがなくなり(少しは)安全に処理できる。きちんとエラー処理を入れるのであれば、OnPaint内でゼロで除算することがないようにイメージサイズも見るべきだ。その手の細かいエラー処理は今後少しずつ入れていくことにする。
		//画像読み込み
		_pImage = Bitmap::FromFile(L"test.jpg",TRUE);
		//画像開放
		if(_pImage)
			delete	_pImage;
		_pImage = NULL;
		if(_pImage == NULL)
			return	0;
		//画像サイズ
		nImageWidth = _pImage->GetWidth();
		nImageHeight = _pImage->GetHeight();
		//画像表示
		cGraphics.DrawImage(_pImage,(REAL)nX,(REAL)nY,(REAL)nWidth,(REAL)nHeight);

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

第10回 「ファイルを開く」ダイアログを利用して画像を読み込む

ImageViewer10_01.gif
今回は自由に画像ファイルを読み込めるようにする。まず「ImageViewerView.h」を開く。

ImageViewer10_02.gif そしてインクルードファイルとして「atlstr.h」を追加する。
#include "atlstr.h"

ImageViewer10_03.gif 次に画像読み込み用の関数LoadImageを追加、それに合わせてOnEraseBkgndとOnCreateをそれぞれ変更する。 LoadImage内ではすでに画像が読み込まれていた場合はそれを解放してから新しい画像ファイルを読み込み、画面表示を更新する。 OnEraseBkgndは今までは何もしなかったが、画像が読み込まれていないときは背景を描画するように変更する。
	//画像の読み込み
	bool	LoadImage(LPCTSTR pszFile)
	{
		//画像が既に読み込まれているならそれを開放
		if(_pImage)
			delete	_pImage;

		//画像読み込み
		_pImage = Bitmap::FromFile((CAtlStringW)pszFile,TRUE);

		//ウインドウ再描画
		Invalidate();

		return	_pImage ? true : false;
	}

	//背景描画
	LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		//画像が読み込まれているときは背景を描画しない
		if(_pImage)
			return	0;

		//画像が読み込まれていないときは背景を描画する
		bHandled = FALSE;
		return	0;
	}

	//ウインドウ生成時処理
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		return	0;
	}

ImageViewer10_04.gif
次に新しくソースコードファイルを作る。ソリューションエクスプローラの「Header Files」を右クリックして現れたメニューの「追加」から「新しい項目」を選択する。

ImageViewer10_05.gif
ここではhファイルを追加したいので、カテゴリから「コード」、テンプレートから「ヘッダーファイル」を選択する。ファイル名は「ShowFileOpenSaveDialog」とした。

ImageViewer10_06.gif
これでプロジェクトにヘッダーファイルが新しく追加された。

ImageViewer10_07.gif このヘッダーファイル内に「ファイルを開く」ダイアログを利用するための関数を定義する。ここでは以前紹介した「ファイルを開く/保存するダイアログを表示する」をそのまま利用している。
#pragma	once


#include "commdlg.h"


//
//	ファイルを開く/保存するダイアログ表示
//
//bSaveDialog==trueで保存用、falseで開くためのダイアログを表示
//pszInitFolerで初期選択フォルダ指定(ex._T("d:\\"))
//pszFilterExt、pszFilterNameでファイルの拡張子指定(ex.pszFilterExt=_T("*.txt")、pszFilterName=_T("テキストファイル"))
//bUseAllFilter==trueで全てのファイル(*.*)を使うかどうかの指定
//pszTitleでダイアログタイトルの指定
//
//
//■使用例
//	#include "atlstr.h"
//	void	Test(void)
//	{
//		bool		ret;
//		CAtlString	strFile;
//		ret = ShowFileOpenSaveDialog(NULL,&strFile,true,_T("d:"),_T("*.txt"),_T("テキストファイル"));
//		if(ret)
//			::MessageBox(NULL,strFile,_T(""),MB_OK);
//	}
//
//
bool	ShowFileOpenSaveDialog(HWND hParentWnd,CAtlString* pstrFileName,bool bSaveDialog,LPCTSTR pszInitFoler=NULL,LPCTSTR pszFilterExt=NULL,LPCTSTR pszFilterName=NULL,bool bUseAllFilter=true,LPCTSTR pszTitle=NULL)
{
	BOOL			ret;
	TCHAR			pszName[2048];
	TCHAR			pszFilter[2048];
	OPENFILENAME	sOpenFileName;

	if(pstrFileName == NULL)
		return	false;
	*pstrFileName = _T("");

	/////////////////////////////////
	//フィルター作成
	//

	//\0の代わりに\1を使っていることに注意!
	pszFilter[0] = NULL;
	if(pszFilterExt && pszFilterName)
		::_stprintf_s(pszFilter,2048 * sizeof(TCHAR),_T("%s\1%s\1"),pszFilterName,pszFilterExt);
	else if(pszFilterExt)
		::_stprintf_s(pszFilter,2048 * sizeof(TCHAR),_T("%s\1%s\1"),pszFilterExt,pszFilterExt);
	else if(bUseAllFilter == false)
		return	false;

	if(bUseAllFilter)
	{
		if(pszFilter[0])
			::_tcscat_s(pszFilter,2048 * sizeof(TCHAR),_T("All file\1*.*\1"));
		else
			::_stprintf_s(pszFilter,2048 * sizeof(TCHAR),_T("All file\1*.*\1"));
	}
	else
		::_tcscat_s(pszFilter,2048 * sizeof(TCHAR),_T("\1"));

	//\1を\0に置換
	{
		size_t	i;
		size_t	nSize;

		nSize = ::_tcslen(pszFilter);
		for(i = 0; i < nSize; i++)
		{
			if(pszFilter[i] == '\1')
				pszFilter[i] = '\0';
		}
	}

	/////////////////////////////////
	//パラメータ初期化
	//
	pszName[0] = NULL;
	::ZeroMemory(&sOpenFileName,sizeof(OPENFILENAME));
	sOpenFileName.lStructSize	= sizeof(OPENFILENAME);
	sOpenFileName.hwndOwner		= hParentWnd;
	sOpenFileName.lpstrFile		= pszName;
	sOpenFileName.nMaxFile		= 2048;
	sOpenFileName.lpstrFilter	= pszFilter;
	sOpenFileName.nFilterIndex	= 1;
	sOpenFileName.lpstrTitle	= pszTitle;
	sOpenFileName.nMaxFileTitle	= pszTitle ? (DWORD)::_tcslen(pszTitle) : 0;
	sOpenFileName.lpstrInitialDir = pszInitFoler;
	sOpenFileName.Flags			= OFN_PATHMUSTEXIST | OFN_DONTADDTORECENT | OFN_EXPLORER | (bSaveDialog ? OFN_OVERWRITEPROMPT : OFN_FILEMUSTEXIST);


	/////////////////////////////////
	//ダイアログ表示
	//
	if(bSaveDialog)
		ret = ::GetSaveFileName(&sOpenFileName);
	else
		ret = ::GetOpenFileName(&sOpenFileName);

	if(ret == FALSE)
		return	false;

	*pstrFileName = pszName;

	return	true;
}

ImageViewer10_08.gif
次に「ファイルを開く」ダイアログを利用するコードを追加するため、「MainFrm.h」を開く。

ImageViewer10_09.gif そして先ほど作成したヘッダーファイルのインクルード宣言を追加する。
#include "ShowFileOpenSaveDialog.h"

ImageViewer10_10.gif 「ファイルを開く」ダイアログが「ファイル」メニューの「開く」を選択したときに表示されるようにメッセージハンドラを追加する。ファイルを開くではWM_COMMANDがID_FILE_OPENで呼ばれる。 今回の画像表示で利用してるGDI+は画像ファイルとしてJPEG以外にPNGやGIFなどにも対応している。しかしここでは拡張子JPGのみ指定した。
		COMMAND_ID_HANDLER(ID_FILE_OPEN,OnFileOpen)
	//ファイルを開くダイアログ表示
	LRESULT OnFileOpen(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		bool		ret;
		CAtlString	strFile;

		ret = ShowFileOpenSaveDialog(NULL,&strFile,false,_T(""),_T("*.jpg"),_T("JPEGファイル"));
		if(ret)
			m_view.LoadImage(strFile);

		return	0;
	}

ImageViewer10_11.gif
これで実行した。
起動時には画像は表示されず背景は真白。

ImageViewer10_12.gif
「ファイル」メニューの「開く」を選択する。

ImageViewer10_13.gif
するとファイルを選択するためのダイアログが開く。ここで画像ファイルを指定して「開く」ボタンを押す。

ImageViewer10_14.gif
すると画像が表示された。

ImageViewer10_15.gif
さらにほかの画像に切り替えることもできる。ここまで実装を進めるとよやく画像表示ソフトっぽくなってきた。

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

第11回 ドラッグ・アンド・ドロップで画像を開く

ImageViewer11_01.gif 前回は「ファイルを開く」ダイアログが利用できるようにした。今回はさらにドラッグ・アンド・ドロップで画像ファイルを開けるようにする。 まず「ImageViewerView.h」にドラッグアンドドロップ用のメッセージWM_DROPFILESのメッセージハンドラを用意する。
		MESSAGE_HANDLER(WM_DROPFILES, OnDropFiles)

ImageViewer11_02.gif そしてドラッグ・アンド・ドロップされたファイルの取得とウインドウへの利用登録を行う。 複数項目がドラッグ・アンド・ドロップされる可能性があり、さらにフォルダなど画像ファイル以外がドロップされる可能性もある。そのためフォルダは処理せず、LoadImageの戻り値を見て成功したらそれ以上読み込み処理をしないようにした。
	//ドラッグアンドドロップで開く
	LRESULT OnDropFiles(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bool	bRet;
		UINT	i;
		UINT	nSize;
		UINT	ret;
		HDROP	hDrop;
		TCHAR	pszBuff[MAX_PATH*10];

		hDrop = (HDROP)wParam;
		nSize = ::DragQueryFile(hDrop,-1,NULL,0);
		for(i = 0; i < nSize; i++)
		{
			ret = ::DragQueryFile(hDrop,i,pszBuff,MAX_PATH*10);
			if(ret == -1)
				continue;

			if(::PathIsDirectory(pszBuff))
				continue;

			bRet = LoadImage(pszBuff);
			if(bRet)
				break;
		}
		::DragFinish(hDrop);

		return	0;
	}
		//ドラッグアンドドロップを受け入れる
		::DragAcceptFiles(m_hWnd,TRUE);

ImageViewer11_03.gif
これで自由にドラッグ・アンド・ドロップしてファイルを読み込めるようになった。
また、「ファイルを開く」ダイアログではJPEGファイルしか選択できなかった。しかしドラッグ・アンド・ドロップ処理では拡張子のチェックを入れていないので、PNGやGIFなどJPEGファイル以外の様々な画像ファイルを開ける。

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

第12回 タイトルバーに画像ファイルのパスを表示する

ImageViewer12_01.gif タイトルバーに表示される文字列の設定はSetWindowTextで行なえる。今回はViewウインドウ内から設定するため、GetParentで親ウインドウ(フレームウインドウ)を取得して、それに対してSetWindowTextを使っている。 今回はさらに読み込んだ画像の縦横サイズを調べてゼロだったら不正な画像と判断するエラー処理も追加した。
		//不正な画像かどうかチェック
		if(_pImage && (_pImage->GetWidth() == 0 || _pImage->GetHeight() == 0))
		{
			delete	_pImage;
			_pImage = NULL;
		}

		//ウインドウタイトル設定
		CAtlString	strTitle;
		strTitle.LoadString(IDR_MAINFRAME);
		if(_pImage)
		{
			strTitle += _T(" - ");
			strTitle += pszFile;
		}
		GetParent().SetWindowText(strTitle);

ImageViewer12_02.gif
これで実行して画像を読み込むとタイトルバーに画像ファイルのパスが表示された。

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

レジストリを読み書きする

test85.gif ATLライブラリにはレジストリを扱うクラスとしてCRegKeyが用意されている。このクラスを利用すると簡単にレジストリへ値を書き込んだり値を読み込める。
#include "atlstr.h"


bool	WriteTest(DWORD dwData)
{
	LONG	nRet;
	CRegKey	cRegistry;

	//レジストリキーがなければ作成(複数階層でも作成される)
	nRet = cRegistry.Create(HKEY_CURRENT_USER,_T("software\\usefullcode\\test85"),REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE);
	if(nRet != ERROR_SUCCESS)
		return	false;

	//DWORD値を書き込む
	nRet = cRegistry.SetDWORDValue(_T("test_dword"),dwData);

	cRegistry.Close();

	return	(nRet == ERROR_SUCCESS) ? true : false;
}



bool	ReadTest(DWORD* pdwData)
{
	LONG	nRet;
	CRegKey	cRegistry;

	if(pdwData == NULL)
		return	false;

	//レジストリキーがなければ失敗
	nRet = cRegistry.Open(HKEY_CURRENT_USER,_T("software\\usefullcode\\test85"),KEY_READ);
	if(nRet != ERROR_SUCCESS)
		return	false;

	//DWORD値を読み込む
	nRet = cRegistry.QueryDWORDValue(_T("test_dword"),*pdwData);

	cRegistry.Close();

	return	(nRet == ERROR_SUCCESS) ? true : false;
}


void	Test(void)
{
	bool		ret;
	DWORD		dwData;
	CAtlString	strMessage;

	ret = WriteTest(10);
	if(ret)
		ret = ReadTest(&dwData);

	if(ret == false && dwData != 10)
		::MessageBox(NULL,_T("失敗しました!"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("成功しました"),_T(""),MB_OK);
}

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

第13回 ウインドウサイズを前回利用時と同じにする

ImageViewer13_01.gif
起動時のウインドウサイズを前回利用したときと同じサイズで開くようにする。
ソフト終了時にレジストリにウインドウサイズを保存、ソフト起動時にレジストリからウインドウサイズを読み込んで表示する。

まずはソフト終了時のウインドウサイズをレジストリへ保存するため取得する。MainFrm.hを開き、終了時のウインドウサイズを保存するための変数_rectWindowとWM_DESTROYのメッセージハンドラを追加する。
	CRect	_rectWindow;
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

ImageViewer13_02.gif そしてウインドウ破棄(ソフト終了)時に実行されるOnDestroyを追加する。ここではソフトを終了するときに最大化もしくは最小化していない場合にウインドウサイズを取得している。つまり最大化もしくは最小化しているときはウインドウサイズを取得しない。
	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;

		//最小化・最大化されていなければウインドウ破壊前にウインドウサイズを保存しておく
		if(IsIconic() == FALSE && IsZoomed() == FALSE)
			GetWindowRect(&_rectWindow);

		return	0;
	}
ImageViewer13_03.gif 次にImageViewer.cppでRun関数の前にレジストリにデータを読み書きする処理を追加する。 レジストリへの書き込みはATLライブラリのCRegKeyを利用する。
//レジストリからのデータ読み込み
bool	LoadRegistry(LPCTSTR pszValueName,void* pData,ULONG nSize)
{
	LONG	nRet;
	ULONG	nSizeRead;
	CRegKey	cRegistry;

	if(pData == NULL)
		return	false;

	//レジストリキーがなければ失敗
	nRet = cRegistry.Open(HKEY_CURRENT_USER,_T("software\\usefullcode\\ImageViewer"),KEY_READ);
	if(nRet != ERROR_SUCCESS)
		return	false;

	nSizeRead = nSize;
	nRet = cRegistry.QueryBinaryValue(pszValueName,pData,&nSizeRead);
	if(nRet == ERROR_SUCCESS && nSizeRead != nSize)
		nRet = ERROR_INVALID_DATA;

	cRegistry.Close();

	return	(nRet == ERROR_SUCCESS) ? true : false;
}


//レジストリへのデータ書き込み
bool	SaveRegistry(LPCTSTR pszValueName,const void* pData,ULONG nSize)
{
	LONG	nRet;
	CRegKey	cRegistry;

	if(pData == NULL)
		return	false;

	//レジストリキーがなければ作成(複数階層でも作成される)
	nRet = cRegistry.Create(HKEY_CURRENT_USER,_T("software\\usefullcode\\ImageViewer"),REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE);
	if(nRet != ERROR_SUCCESS)
		return	false;

	nRet = cRegistry.SetBinaryValue(pszValueName,pData,nSize);

	cRegistry.Close();

	return	(nRet == ERROR_SUCCESS) ? true : false;
}

ImageViewer13_04.gif
次にRun関数内を変更する。この関数はウインドウの生成から終了までを担っている。


ImageViewer13_05.gif
Run関数をこのように修正する。ウインドウ生成処理前にレジストリからウインドウサイズを読み込む。その際、読み込んだウインドウサイズをそのまま表示に利用するのではなく、現在の画面解像度と比較してウインドウが画面の中に納まる場合のみ利用している。

これで実行すると終了時にウインドウサイズがレジストリに書き込まれ、次回から同じサイズで起動するようになる。
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
	CMessageLoop theLoop;
	_Module.AddMessageLoop(&theLoop);

	CMainFrame wndMain;

	bool	ret;
	RECT	rect;

	//ウインドウサイズの読み込み
	ret = LoadRegistry(_T("window_rect"),&rect,sizeof(RECT));
	if(ret)
	{
		//現在のスクリーン内にrectが完全に収まるかチェック。はみ出すならfalse
		if(rect.left < 0 || rect.right > ::GetSystemMetrics(SM_CXSCREEN))
			ret = false;
		if(rect.top < 0 || rect.bottom > ::GetSystemMetrics(SM_CYSCREEN))
			ret = false;
	}

	HWND	hWndRet;

	if(ret)
		hWndRet = wndMain.CreateEx(0,rect);
	else
		hWndRet = wndMain.CreateEx();
	if(hWndRet == NULL)
	{
		ATLTRACE(_T("メイン ウィンドウの作成に失敗しました!\n"));
		return 0;
	}

	wndMain.ShowWindow(nCmdShow);

	int nRet = theLoop.Run();

	//ウインドウサイズの保存
	if(wndMain._rectWindow.Width() > 0 && wndMain._rectWindow.Height() > 0)
		SaveRegistry(_T("window_rect"),&wndMain._rectWindow,sizeof(RECT));

	_Module.RemoveMessageLoop();
	return nRet;
}

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

フォルダ内のファイルを列挙する

twst86.gif
フォルダの中に含まれるファイルはFindFirstFile、FindNextFile、FindCloseで検索できる。ここでは指定したフォルダに含まれるファイルをCAtlArrayに格納して返す。CAtlArrayはCStringArrayと同じでCStringの配列になる。

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

//
//	フォルダ内のファイル検索
//
//見つかったファイルをpastrFilesに追加する
//nRecursiveは子フォルダの探索数(デフォルトは探索しない)
//
bool	GetFilesInFolder(LPCTSTR pszFolder,CAtlArray<CAtlString>* pastrFiles,int nRecursive=0)
{
	BOOL			bFind;
	HANDLE			hFind;
	WIN32_FIND_DATA	FindFileData;
	CAtlString		strFolder;

	if(pszFolder == NULL || _tcslen(pszFolder) == 0 || pastrFiles == NULL)
		return	false;

	//astrFiles.RemoveAll();

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

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

	bFind = TRUE;
	while(bFind)
	{
		if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			//フォルダなら...
			if(nRecursive > 0 && _tcsncmp(FindFileData.cFileName,_T("."),1) != 0 && _tcsncmp(FindFileData.cFileName,_T(".."),2) != 0 )
			{
				//再起で子フォルダ内を調べる
				GetFilesInFolder(strFolder + FindFileData.cFileName,pastrFiles,nRecursive - 1);
			}
		}
		else
		{
			//ファイルなら...
			pastrFiles->Add(strFolder + FindFileData.cFileName);
		}

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

	return	true;
}




void	Test(void)
{
	CAtlArray<CAtlString>	astrFiles;

	GetFilesInFolder(_T("c:\\"),&astrFiles);

	size_t		i;
	size_t		nSize;
	CAtlString	strMessage;

	nSize = astrFiles.GetCount();
	for(i = 0; i < nSize; i++)
	{
		strMessage += astrFiles[i];
		strMessage += _T("\n");
	}

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


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

前の10件 1  2  3  4  5  6  7  8  9  10





usefullcode@gmail.com

About 2006年12月

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

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

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

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