EternalWindows
シェル拡張 / コピーフック ハンドラ
サンプルはこちら

コピーフック ハンドラは、システム上に存在する全てのフォルダへの操作を検出します。 検出できる操作は、コピー、移動、削除、名前変更の4つであり、検出した操作の実行を拒否することもできます、 たとえば次のように、特定のフォルダの削除を拒否することができます。

コピーフック ハンドラは、ICopyHookを実装することになります。 フォルダ(ファイルは対象外)に対して何らかの操作が行われた場合はCopyCallbackが呼ばれます。

class CCopyHook : public ICopyHook
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP_(UINT) CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs);

	CCopyHook();
	~CCopyHook();

private:
	LONG m_cRef;
};

ICopyHookのメソッドはCopyCallbackただ1つだけです。 hwndは、ダイアログを表示する際に使用する親ウインドウのハンドルが格納されます。 wFuncは、フォルダに対して発生した操作を表す定数が格納されます。 wFlagsは、操作に関するフラグが格納されます。 pszSrcFileは、元のフォルダのフルパスが格納されます。 dwSrcAttribsは、元のフォルダの属性が格納されます。 pszDestFileは、先のフォルダのフルパスが格納されます。 dwDestAttribsは、先のフォルダの属性が格納されます。 戻り値は、操作を許可する場合にIDYESを返し、 許可しない場合にIDNOまたはIDCANCELを返します。 IDNOはこの操作のみを許可しないことを意味しますが、 IDCANCELは後続の操作も許可しないことを意味します。

wFuncに格納される定数を次に示します。

定数 意味
FO_COPY フォルダがコピーされようとしていることを意味する。 pszSrcFileがコピー元のフォルダになり、pszDestFileがコピー先のフォルダになる。
FO_DELETE フォルダが削除されようとしていることを意味する。 pszSrcFileが削除されるフォルダになる。
FO_MOVE フォルダが移動されようとしていることを意味する。 pszSrcFileが移動元のフォルダになり、pszDestFileが移動先のフォルダになる。
FO_RENAME フォルダの名前が変更されようとしていることを意味する。 pszSrcFileが変更前のフォルダになり、pszDestFileが変更後のフォルダになる。

CopyCallbackは、上記操作をシステム上の何らかのフォルダに行った場合に呼ばれます。

wFlagsに格納される定数の一部を次に示します。

定数 意味
FOF_SILENT 処理の進行を示すUIを表示しない。
FOF_NOCONFIRMATION 確認のためのUIを表示しない。全てYESで応答したことになる。
FOF_NOERRORUI エラーが発生してもUIを表示しない。
FOF_NOCONFIRMMKDIR 新しいフォルダを作成すべき時でも、その確認をUIで行わない。
FOF_NO_UI 上記した4つの定数の組み合わせ。どのようなUIも表示しないことを意味する。

FOF_NO_UIは、古いSDKのヘッダーファイルに定義されていないことがあります。 このような場合は、各定数を明示的に組み合わせて定義することになります。

今回のCopyCallbackでは、特定のフォルダの削除を許可しないようにしています。 また、特定のフォルダのコピーを実行する際にはユーザーへの確認を行います。

STDMETHODIMP_(UINT) CCopyHook::CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs)
{
	TCHAR szFolderName[] = TEXT("C:\\sample");

	if (StrCmp(pszSrcFile, szFolderName) != 0)
		return IDYES;

	if (wFunc == FO_DELETE) {
		if ((wFlags & FOF_NO_UI) != FOF_NO_UI)
			MessageBox(hwnd, TEXT("このフォルダは削除できません。"), pszSrcFile, MB_ICONWARNING);
		return IDNO;
	}
	else if (wFunc == FO_COPY) {
		if ((wFlags & FOF_NO_UI) != FOF_NO_UI)
			return MessageBox(hwnd, TEXT("コピーを続行しますか?"), pszSrcFile, MB_YESNO);
		return IDYES;
	}
	else
		;
	
	return IDYES;
}

特定のフォルダのパスはszFolderNameに格納しているため、 これが元のフォルダであるpszSrcFileと一致するかを調べます。 一致しない場合は操作を制限する必要がありませんから、IDYESを返すようにしています。 wFuncがFO_DELETEである場合は、フォルダの削除が行われようとしていることを意味します。 この際には、削除を許可しないためにIDNOを返し、 さらに削除できない旨を示すダイアログを表示するのですが、 wFlagsにFOF_NO_UIが含まれる場合はUIの表示を行ってはいけません。 同じように、FO_COPYでもFOF_NO_UIが含まれていないかを確認し、 含まれていない場合に処理の続行を確認するようにしています。 MessageBoxにMB_YESNOを指定した場合はIDYESかIDNOが返るため、 これをそのまま戻り値にすることができます。 なお、FO_COPYで行っていることは確認処理に相当すると思われるため、 wFlagsにFOF_NOCONFIRMATIONが含まれているかを確認すべきかもしれません。

コピーフック ハンドラを登録するには、次に示すレジストリキーを作成することになります。

HKEY_CLASSES_ROOT
  Directory
    shellex
      CopyHookHandlers
        <独自のハンドラ名> (既定) = オブジェクトのCLSID

コピーフック ハンドラのDLLはシェルの初期化時にロードされ、自動でアンロードされることはありません。 DLLが認識されるためには、登録後にシェルを再起動する必要があります。

コピーフック ハンドラの問題点

コピーフック ハンドラには、シェル上で発生したフォルダ操作しか検出できないという欠点があります。 たとえば、何らかのアプリケーションが次のコードを実行したとします。

RemoveDirectory(TEXT("C:\\sample"));

RemoveDirectoryはフォルダを削除する関数ですが、この関数を呼び出してもICopyHook::CopyCallbackが呼ばれることはありません。 つまり、第1引数のフォルダが存在する場合は普通に削除することができてしまいます。 それでは次のコードはどうでしょうか。

MoveFile(TEXT("C:\\sample"), TEXT("C:\\sample2"));

MoveFileは、ファイル及びフォルダの移動や名前変更を行う関数です。 上記ではフォルダの名前を変更する処理になりますが、やはりこれもICopyHook::CopyCallbackが呼ばれることはありません。

ファイルシステム関数によるフォルダ操作ではICopyHook::CopyCallbackが呼ばれませんが、 シェル関数によるフォルダ操作ならばICopyHook::CopyCallbackは呼ばれることになります。 たとえば、次に示すSHFileOperationがそれに当たります。

TCHAR          szPathSrc[] = TEXT("C:\\sample\0");
TCHAR          szPathDest[] = TEXT("C:\\sample_copy\0");
SHFILEOPSTRUCT fileOp;

ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
fileOp.wFunc  = FO_COPY;
fileOp.pFrom  = szPathSrc;
fileOp.pTo    = szPathDest;
fileOp.fFlags = 0;

SHFileOperation(&fileOp);

SHFILEOPSTRUCT構造体のwFuncにFO_COPYを指定した場合は、pFromのフォルダがpToにコピーされることになります。 SHFileOperationではパスが2つの\0文字で終了している必要があるため、明示的に文字列の中に\0を含めています。 fFlagsにFOF_NO_UIを指定した場合はUIが表示されないはずです。



戻る