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

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);
	}
};

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


カテゴリー「ファイル・フォルダ」 のエントリー