ICopyHook是一個用于建立拷貝鈎子處理程式COM接口,它決定一個檔案夾或者列印機對象是否可以被移動,拷貝,重命名或删除。Shell在執行這些操作之前,會調用ICopyHook接口的CopyCallback方法對它們進行驗證。CopyCallback傳回一個int值訓示Shell是否應該繼續執行這個操作。傳回值IDYES表示繼續,而傳回值IDNO和IDCANCEL則表示終止。
一個檔案夾對象可以安裝多個拷貝鈎子處理程式。如果出現這種情況,Shell會依次調用每個處理程式。隻有當每個處理程式都傳回IDYES時,Shell才真正執行使用者請求的操作。
拷貝鈎子處理程式的作用是在上述四種操作執行前對它們進行驗證,但是Shell并不會把操作的結果通知給拷貝鈎子處理程式。而windows提供的API函數FindFirstChangeNotification和FindNextChangeNotification卻可以實作這個功能。是以,隻有把這種兩種方法結合起來,才能對一個檔案夾的狀态進行完全的監控。
拷貝鈎子處理程式實作并不困難,首先建立一個作為程序内元件的COM對象,它隻需要暴露一個ICopyHook接口(當然還有IUnknown)。然後用regsrv32.exe注冊這個COM元件。最後一步是向Shell注冊你的這個拷貝鈎子處理程式,方法是在系統資料庫HKEY_CLASSES_ROOT/Directory/Shellex/CopyHookHandlers下建立一個名稱任意的sub key,在此sub key中建立一個類型為REG_SZ的項并将你的COM對象的CLSID作為它的預設值就可以了。
下面就是一個拷貝鈎子的實作程式
// CCopyHook.h
//CCopyHook類實作了ICopyHook接口,CClassFactory實作了IClassFactory接口
#include <shlobj.h>
class CCopyHook: public ICopyHook
{
public:
CCopyHook():m_refcnt(0) { }
STDMETHODIMP QueryInterface(REFIID iid,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);
private:
int m_refcnt;
};
class CClassFactory:public IClassFactory
CClassFactory():m_refcnt(0) { }
STDMETHODIMP CreateInstance(IUnknown * pUnkOuter,REFIID riid,void ** ppvObject);
STDMETHODIMP LockServer(BOOL fLock);
// CCopyHook.cpp
//CCopyHook對象和CClassFactory對象的實作檔案
#include <stdio.h>
#include "CCopyHook.h"
extern LONG nLocks; //對象計數,用于DllCanUnloadNow
ULONG __stdcall CCopyHook::AddRef(){
if(m_refcnt==0)
nLocks++;
m_refcnt++;
return m_refcnt;
}
ULONG __stdcall CCopyHook::Release(){
int nNewCnt=--m_refcnt;
if(nNewCnt<=0){
nLocks--;
delete this;
}
return nNewCnt;
HRESULT __stdcall CCopyHook::QueryInterface(REFIID iid,void** ppvObject){
if(iid==IID_IUnknown)
*ppvObject=static_cast<IUnknown*>(this);
else
if(iid==IID_IShellCopyHook)
*ppvObject=static_cast<ICopyHook*>(this);
return E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
return S_OK;
//這就是CopyCallback方法,拷貝鈎子的所有功能由它實作。參數的具體值參看MSDN
UINT __stdcall CCopyHook::CopyCallback(HWND hwnd,UINT wFunc,UINT wFlags,
LPCTSTR pszDestFile,DWORD dwDestAttribs){
char szMessage[MAX_PATH+14];
sprintf(szMessage,"對%s進行的操作,是否繼續?",pszSrcFile);
return MessageBox(NULL,szMessage,"确認",MB_YESNO|MB_ICONEXCLAMATION);
ULONG __stdcall CClassFactory::AddRef(){
ULONG __stdcall CClassFactory::Release(){
HRESULT __stdcall CClassFactory::QueryInterface(REFIID iid,void** ppvObject){
if(iid==IID_IClassFactory)
*ppvObject=static_cast<IClassFactory*>(this);
HRESULT __stdcall CClassFactory::CreateInstance(IUnknown* pUnkownOuter,REFIID riid,void** ppvObj){
if(pUnkownOuter!=NULL)
return CLASS_E_NOAGGREGATION;
CCopyHook* pObj=new CCopyHook;
pObj->AddRef();
HRESULT hr=pObj->QueryInterface(riid,ppvObj);
pObj->Release();
return hr;
HRESULT __stdcall CClassFactory::LockServer(BOOL fLock){
if(fLock)
nLocks++;
// main.cpp
//主要實作了幾個COM對象标準的導出函數。
#include <objbase.h>
#include <olectl.h>
//這是要添加到系統資料庫中的項,注意如果你要使用這段代碼,應該用UUIDGEN.exe生成一
//個新的CLSID。
const char* szRegTable[][3]={
{"CLSID//{7e10a039-fe03-4f9c-b7e1-c5eeeaf53735}",0,"CopyHook"},
{"CLSID//{7e10a039-fe03-4f9c-b7e1-c5eeeaf53735}//InProcServer32",0,(const char*)-1},
{"CLSID//{7e10a039-fe03-4f9c-b7e1-c5eeeaf53735}//InProcServer32","ThreadingModel","Apartment"},
{"CLSID//{7e10a039-fe03-4f9c-b7e1-c5eeeaf53735}//ProgID",0,"webber84.CopyHook.1"},
{"webber84.CopyHook.1",0,"CopyHook"},
{"webber84.CopyHook.1//CLSID",0,"{7e10a039-fe03-4f9c-b7e1-c5eeeaf53735}"}};
HMODULE hInstance=NULL;
LONG nLocks=0;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
){
if(ul_reason_for_call==DLL_PROCESS_ATTACH)
hInstance=(HMODULE)hModule;
return TRUE;
STDAPI DllUnregisterServer(){
HRESULT hr=S_OK; LONG ret=0;
int items=sizeof(szRegTable)/sizeof(szRegTable[0]);
for(int i=items-1;i>=0;i--){
const char* szKeyName=szRegTable[i][0];
if( (i==items-1) || stricmp(szRegTable[i+1][0],szKeyName)!=0)
ret=RegDeleteKey(HKEY_CLASSES_ROOT,szKeyName);
if(ret!=ERROR_SUCCESS)
hr=SELFREG_E_CLASS;
//可重用的DllRegisterServer函數,隻要照上面的格式把系統資料庫項放到一個數組中,就可以//用這段代碼完成對任意元件的注冊。
STDAPI DllRegisterServer(void){
HRESULT hr=S_OK;
char szDllPath[MAX_PATH];
GetModuleFileName(hInstance,szDllPath,MAX_PATH);
for(int i=0;i<items && SUCCEEDED(hr);i++){
const char* szValueName=szRegTable[i][1];
const char* szValue=szRegTable[i][2];
if(szValue==(const char*)-1)
szValue=szDllPath;
HKEY hKey;
LONG ret=RegCreateKey(HKEY_CLASSES_ROOT,szKeyName,&hKey);
if(ret==ERROR_SUCCESS){
RegSetValueEx(hKey,szValueName,0,REG_SZ,(const BYTE*)szValue,
strlen(szValue)+1);
RegCloseKey(hKey);
}
if(ret!=ERROR_SUCCESS){
DllUnregisterServer();
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, void** ppvObj) {
HRESULT hr = E_OUTOFMEMORY;
*ppvObj = NULL;
CClassFactory *pClassFactory = new CClassFactory;
if (pClassFactory != NULL)
hr = pClassFactory->QueryInterface(riid, ppvObj);
return hr;
STDAPI DllCanUnloadNow(){
return nLocks==0 ? S_OK : S_FALSE;