前の10件 14  15  16  17  18  19  20  21  22  23  24

記事一覧

2007年01月02日

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

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

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


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

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

#pragma once


#include "atlstr.h"


#define	WM_CHANGENOTIFY	WM_APP + 1





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

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

	virtual BOOL OnIdle()
	{
		return FALSE;
	}

	BEGIN_UPDATE_UI_MAP(CMainDlg)
	END_UPDATE_UI_MAP()

	BEGIN_MSG_MAP(CMainDlg)

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

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

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


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

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

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

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

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

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

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

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

		return	false;
	}


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

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

			if(pszPath == NULL)
				return	false;

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

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

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

			::ILFree(pidl);

			return	*pnID ? true : false;
		}

		if(*pnID == 0)
			return	false;

		//登録解除
		BOOL	ret;

		ret = ::SHChangeNotifyDeregister(*pnID);

		return	ret ? true : false;

	}




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

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




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

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

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

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

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

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

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

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

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

		MessageBox(strMessage);

		return	0;
	}



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

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

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

		UIAddChildWindowContainer(m_hWnd);

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

		return TRUE;
	}

	ULONG	_nID;

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

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

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

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

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

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

パスからPIDLを取得する

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

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


#include "shlobj.h"


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

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

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

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

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

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

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

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

	return	false;
}



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

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

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

	if(ret)
		::ILFree(pidl);

}


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

2007年01月01日

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

test93.gif

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

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

#include "atlstr.h"

#include "shlobj.h"


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

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

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

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

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

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

	if(SUCCEEDED(hr))
	{

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

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

	pIShellFolder->Release();

	if(pItemIdList)
		::ILFree(pItemIdList);

	return	SUCCEEDED(hr) ? true : false;
}




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

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

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

	::CoUninitialize();
}

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

2006年12月31日

「今後、このダイアログボックスを表示しない」があるメッセージボックスを使う

test92.gif
Windows2000以降には「今後、このダイアログボックスを表示しない」を備えたメッセージボックスとしてSHMessageBoxCheckが用意されている。

このAPIはチェックを入れてボタンを押したとき、レジストリに押されたボタンの情報を保存している。

依存環境:ATL
#include "shlwapi.h"
#pragma	comment(lib,"shlwapi.lib")


void	Test(void)
{
	int		ret;

	//初回はメッセージボックスが表示される
	ret = ::SHMessageBoxCheck(NULL,_T("今後表示しないにチェックを入れてボタンを押してください"),_T("test"),MB_YESNO | MB_ICONINFORMATION,IDNO,_T("{D438034E-E6C2-4dae-BF16-12CEE68E201E}"));

	//メッセージボックスが表示されない
	ret = ::SHMessageBoxCheck(NULL,_T("チェックを入れたらこのダイアログは表示されません"),_T("test"),MB_YESNO | MB_ICONINFORMATION,IDNO,_T("{D438034E-E6C2-4dae-BF16-12CEE68E201E}"));

	//レジストリを削除
	{
		LONG	nRet;
		CRegKey	cRegistry;

		nRet = cRegistry.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DontShowMeThisDialogAgain"));
		if(nRet == ERROR_SUCCESS)
		{
			cRegistry.DeleteValue(_T("{D438034E-E6C2-4dae-BF16-12CEE68E201E}"));
			cRegistry.Close();
		}
	}

	//メッセージボックスが表示される
	ret = ::SHMessageBoxCheck(NULL,_T("レジストリを削除したので表示されます"),_T("test"),MB_YESNO | MB_ICONINFORMATION,IDNO,_T("{D438034E-E6C2-4dae-BF16-12CEE68E201E}"));
}

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

タブを指定してドライブなどのプロパティを表示する

test91.gif
SHObjectPropertiesを利用するとドライブなどのプロパティを表示する際にデフォルトで開くタブを設定できる。

例えば「ツール」タブを開きたい場合は L"ツール" のように指定する。このときに指定する文字列はタブ名になる。そのため日本語OSでは L"ツール" でいいが、その他の言語ではほかの文字列になる。きちんと利用したい場合はshell32.dllかどこかに格納されているリソース文字列を参照する必要があるだろう。

#include "shlobj.h"


void	Test(void)
{
	::SHObjectProperties(NULL,SHOP_FILEPATH,L"c:\\",L"ツール");

	::MessageBox(NULL,_T("ダミー"),_T(""),MB_OK);
}

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

インターネット上からファイルをダウンロードする

インターネット上からファイルをダウンロードする方法はいくつかある。ここではwininetのInternetOpen、InternetOpenUrl、InternetReadFileを利用した。

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


//
//	ファイルのダウンロード
//
//	FTP/HTTP両方に対応する。
//	FTPの場合は「ret = DownloadFile(_T("ftp://userid:password@usefullcode.net/home/user/htdocs/index.html"),_T("index.html"));」
//	のようにしてユーザーアカウントとパスワードを設定する
//
bool	DownloadFile(LPCTSTR pszURL,LPCTSTR pszLocalFile,DWORD dwBuffSize=1024)
{
	TCHAR		pszHeader[] = _T("Accept: */*\r\n\r\n");
	BOOL		ret;
	DWORD		dwReadSize;
	DWORD		dwWrittenSize;
	BYTE*		pcbBuff;
	HINTERNET	hInternet;
	HINTERNET	hConnect;
	HANDLE		hFile;

	hInternet = ::InternetOpen(NULL,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
	if(hInternet == NULL)
		return	false;

	hConnect = ::InternetOpenUrl(hInternet,pszURL,pszHeader,-1,INTERNET_FLAG_DONT_CACHE,0);
	pcbBuff = new BYTE[dwBuffSize];
	if(hConnect == NULL || pcbBuff == NULL)
	{
		if(hConnect)
			::InternetCloseHandle(hConnect);
		::InternetCloseHandle(hInternet);
		if(pcbBuff)
			delete	pcbBuff;
		return	false;
	}

	//保存先のファイルを作る。ファイルがあった場合は失敗
	hFile = ::CreateFile(pszLocalFile,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
	ret = (hFile == INVALID_HANDLE_VALUE) ? FALSE : TRUE;

	while(ret)
	{
		::Sleep(0);
		ret = ::InternetReadFile(hConnect,pcbBuff,dwBuffSize,&dwReadSize);
		if(ret == FALSE || dwReadSize == 0)
			break;

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

	if(hFile != INVALID_HANDLE_VALUE)
	{
		::CloseHandle(hFile);

		//失敗時は作成したファイルを削除
		if(ret == FALSE)
			::DeleteFile(pszLocalFile);
	}

	::InternetCloseHandle(hConnect);
	::InternetCloseHandle(hInternet);

	delete	pcbBuff;

	return	ret ? true : false;
}




void	Test(void)
{
	bool	ret;

	ret = DownloadFile(_T("http://www.google.co.jp/"),_T("google.html"));
	if(ret)
		::MessageBox(NULL,_T("ダウンロードしました"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("失敗しました"),_T(""),MB_OK);
}

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

大文字と小文字を区別せずに文字列を比較する

CompareStringを利用すると大文字と小文字を区別しない文字列比較や全角文字と半角文字を区別しない文字列比較などができる。

このAPIは戻り値がstrcmpなどの通常の比較関数とは異なることに注意。戻り値から2を引いた値がstrcmpの戻り値と同じように扱えるようになる。つまりCompareStringによって文字列が等しいと判断された時の戻り値は2となる。

void	Test(void)
{
	int		nRet;

	//"あいうABCDEF"と"あいうabcdef"を比較(結果は「同じ」と判断)
	nRet = ::CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE,_T("あいうABCDEF"),-1,_T("あいうabcdef"),-1);
	if(nRet - 2 == 0)
		::MessageBox(NULL,_T("同じです"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("違います"),_T(""),MB_OK);


	//"ABC"と"abc"を比較(結果は「同じ」と判断)
	nRet = ::CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE,_T("ABC"),-1,_T("abc"),-1);
	if(nRet - 2 == 0)
		::MessageBox(NULL,_T("同じです"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("違います"),_T(""),MB_OK);


	//"abc"と"abc"を比較(結果は「違う」と判断)
	nRet = ::CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE,_T("abc"),-1,_T("abc"),-1);
	if(nRet - 2 == 0)
		::MessageBox(NULL,_T("同じです"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("違います"),_T(""),MB_OK);


	//"abc"と"abc"を比較(結果は「同じ」と判断)
	nRet = ::CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE | NORM_IGNOREWIDTH,_T("abc"),-1,_T("abc"),-1);
	if(nRet - 2 == 0)
		::MessageBox(NULL,_T("同じです"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("違います"),_T(""),MB_OK);
}

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

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

test88_01.gif
ここではMFCを利用して簡単にテキストファイルを読み込む。
「ファイル」メニューの「新規作成」から「プロジェクト」を選択し、現れたウインドウで「MFCアプリケーション」を使う。


test88_02.gif
MFCアプリケーションウイザードが開いたら「アプリケーションの種類」から「ダイアログベース」を選択する。


test88_03.gif
これでプロジェクトが生成された。
次にダイアログにボタンを配置するため「表示」メニューから「リソースビュー」を選択する。


test88_04.gif
左側にリソースビューが開いたら「Dialog」にあるメインウインドウ用のフォームを開く。


test88_05.gif
そしてボタンを配置するためツールボックスの「Button」をフォームへドラッグアンドドロップする。


test88_06.gif
これでボタンが配置された。次にボタンを押したときの動作を実装するため、ボタンをダブルクリックする。


test88_07.gif
すると自動的にメッセージハンドラなどが作成されると同時にソースコードの該当部分が開く。

test88_08.gif ここにボタンを押したときのソースコードを入力する。
ここではCStdioFileを利用してファイルを開き、1行ずつCStringへ読み込み、最後にAfxMessageBoxにてメッセージボックスへ表示している。
	BOOL		ret;
	CString		strLine;
	CString		strMessage;
	CStdioFile	cFile;

	ret = cFile.Open(_T("stdafx.h"),CFile::modeRead | CFile::shareDenyNone);
	if(ret == FALSE)
		return;

	while(ret)
	{
		ret = cFile.ReadString(strLine);
		strMessage += strLine;
		strMessage += _T("\n");
	}

	cFile.Close();

	AfxMessageBox(strMessage);

test88_09.gif
そして実行する。


test88_10.gif
ボタンを押すとファイルが読み込まれ、ファイル内容がメッセージボックスとして表示される。しかしここでは日本語部分が完全に文字化けしてしまった。

これはMFCアプリケーションウイザードではデフォルトでユニコードビルドとなっているためと、MFCのバグ(設計ミス)からCStdioFile::ReadStringAのようなメンバー関数が用意されていないためだ。


test88_11.gif
この場合、簡単に文字化けを直すには非ユニコードビルドにする。設定するにはまず「プロジェクト」メニューから「プロパティ」を選択する。

test88_12.gif
そして開いたウインドウの文字セットを「マルチバイト文字セットを使用する」に変更する。

test88_13.gif
これで実行すると文字化けせずにファイル内容をメッセージボックスに表示できた。

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

2006年12月24日

第15回 「送る」メニューから画像を開く

ImageViewer15_01.gif 起動時にコマンドラインで指定されたファイルを開くようにする。これによりEXEファイルやEXEファイルへのショートカットへ画像ファイルをドラッグ・アンド・ドロップしても画像が開くようになる。
コマンドラインの取得および解析にはCommandLineToArgvWとGetCommandLineWを利用した。GetCommandLineはユニコードおよび非ユニコードバージョンがAPIとして用意されているが、CommandLineToArgvWは非ユニコード版がない(対応OSもWindows 2000以降)。そのため取得したファイル名はCStringに一度変換してから処理している。
GDI+へ渡すファイル名はもともとユニコードなので本来変換の必要はないのだが...
		LPWSTR*	ppszArglist;
		int		nArgs;

		//コマンドライン引数で指定された画像を読み込む
		ppszArglist = ::CommandLineToArgvW(::GetCommandLineW(),&nArgs);
		if(ppszArglist && nArgs > 1)
			LoadImage((CAtlString)ppszArglist[1]);
		::LocalFree(ppszArglist);

ImageViewer15_02.gif
コマンドラインから引数の対応により、「送る」メニューからの画像表示にも対応する。

当然のことながら利用するためにはあらかじめSendToフォルダへショートカットを作っておく必要がある。Windows Vistaの場合のフォルダ位置は

C:\ Users\ ○○\ AppData\ Roaming\ Microsoft\ Windows\ SendTo\

になる。


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

第14回 カーソルキーで次の画像を表示する

ImageViewer14_01.gif 今回は画像を表示している状態で[↑][↓][←][→]キーを押したら、次の画像を表示できるようにする。 まずは現在読み込んでいる画像ファイルのパスを保存するように変更する。「ImageViewerView.h」を開いてメンバ変数を1つ増やす。
	CAtlString	_strImageFile;
			_strImageFile = pszFile;
		}
		else
			_strImageFile = _T("");

ImageViewer14_02.gif 「次の画像」をとは「現在読み込んでいる画像があるフォルダー内のファイルを名前順に並べたときの次の画像」ということになる。 実現するにはフォルダ内のファイルを列挙する処理、列挙したファイルを名前順にソートする処理が必要になるのでそれらを追加する。
#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;
}



class	CDnpSortString
{
	//比較関数
	static	int	SortStringArrayCompare(void* pContext,const void* pData1,const void* pData2)
	{
		if(pContext == NULL)
			return	0;
		return	((CDnpSortString*)pContext)->SortStringArrayCompare_(pData1,pData2);
	}

	//昇順か降順か
	bool		_bABC;

	//ソート対象
	CAtlArray<CAtlString>*	_pastrText;

public:

	CDnpSortString()
	{
		_bABC = true;
		_pastrText = NULL;
	}

	//比較関数の実体
	//外部からは呼ばないこと!
	int	SortStringArrayCompare_(const void* pData1,const void* pData2)
	{
		size_t	nSize;

		if(_pastrText == NULL)
			return	0;

		nSize = _pastrText->GetCount();
		if(*(size_t*)pData1 >= nSize || *(size_t*)pData2 >= nSize)
			return	0;

		if(_bABC)
			return	::_tcscmp((*_pastrText)[*(size_t*)pData1],(*_pastrText)[*(size_t*)pData2]);
		else
			return	::_tcscmp((*_pastrText)[*(size_t*)pData2],(*_pastrText)[*(size_t*)pData1]);
	}


	//
	//	CStringArrayのソート
	//
	bool	SortStringArray(CAtlArray<CAtlString>& astrText,bool bABC)
	{
		size_t	i;
		size_t	nSize;
		size_t*	pnIndex;
		CAtlArray<CAtlString>	astrTmp;

		nSize = astrText.GetCount();
		if(nSize == 0)
			return	false;

		pnIndex = new size_t[nSize];
		if(pnIndex == NULL)
			return	false;

		astrTmp.SetCount(nSize);
		for(i = 0; i < nSize; i++)
		{
			pnIndex[i] = i;
			astrTmp[i] = astrText[i];
		}

		_bABC = bABC;
		_pastrText = &astrText;
		::qsort_s(pnIndex,nSize,sizeof(size_t),SortStringArrayCompare,this);
		_pastrText = NULL;

		for(i = 0; i < nSize; i++)
			astrText[i] = astrTmp[pnIndex[i]];

		delete	pnIndex;

		return	true;
	}

};

ImageViewer14_03.gif さらにキーが押されたときの処理を追加する。キーが押されるとWM_KEYDOWNが送られるのでそれを利用する。 これで実行し、画像ファイルを読み込んだ後にカーソルキーで次のファイルを表示できるようになった。
		MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
	LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;
		if(wParam != VK_UP && wParam != VK_DOWN && wParam != VK_LEFT && wParam != VK_RIGHT)
			return	0;
		if(_pImage == NULL)
			return	0;

		CAtlString				strFolder;
		CDnpSortString			cSort;
		CAtlArray<CAtlString>	astrFiles;

		//現在読み込んでいる画像の存在するフォルダ名を取得
		strFolder = _strImageFile;
		{
			int		nFind;

			strFolder.Replace(_T('/'),_T('\\'));
			nFind = strFolder.ReverseFind(_T('\\'));
			if(nFind < 0)
				return	0;
			strFolder = strFolder.Left(nFind);
		}

		//画像のあるフォルダに存在するファイルを全て取得
		GetFilesInFolder(strFolder,&astrFiles);

		//取得したファイル一覧を名前順にソート
		//[←][↑]の場合は降順、[→][↓]の場合は昇順
		cSort.SortStringArray(astrFiles,(wParam == VK_UP || wParam == VK_LEFT) ? false : true);


		//取得したファイル一覧の中から現在表示している画像のインデックスを取得
		size_t	nIndex;
		size_t	nSize;
		nSize = astrFiles.GetCount();
		{
			CAtlString	strFile;

			strFile = _strImageFile;
			strFile.MakeLower();

			for(nIndex = 0; nIndex < nSize; nIndex++)
			{
				astrFiles[nIndex].MakeLower();
				if(strFile == astrFiles[nIndex])
					break;
			}
		}

		//次のファイルを表示
		{
			bool	ret;
			size_t	nTryCount;

			nTryCount = 0;
			while(1)
			{
				nIndex++;
				if(nIndex >= nSize)
					nIndex = 0;

				ret = LoadImage(astrFiles[nIndex]);
				if(ret)
					break;

				nTryCount++;
				if(nTryCount >= nSize)
					break;
			}
		}

		return	0;
	}

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

前の10件 14  15  16  17  18  19  20  21  22  23  24