用拷贝钩子实现对文件夹的监控

原文网址: http://blog.sina.com.cn/s/blog_648d306d0100vsj5.html

 

ICopyHook是一个用于创建拷贝钩子处理程序COM接口,它决定一个文件夹或者打印机对象是否可以被移动,拷贝,重命名或删除。Shell在执行这些操作之前,会调用ICopyHook接口的CopyCallback方法对它们进行验证。CopyCallback返回一个int值指示Shell是否应该继续执行这个操作。返回值IDYES表示继续,而返回值IDNOIDCANCEL则表示终止。

一个文件夹对象可以安装多个拷贝钩子处理程序。如果出现这种情况,Shell会依次调用每个处理程序。只有当每个处理程序都返回IDYES时,Shell才真正执行用户请求的操作。

贝钩子处理程序的作用是在上述四种操作执行前对它们进行验证,但是Shell并不会把操作的结果通知给拷贝钩子处理程序。而windows提供的APIFindFirstChangeNotificationFindNextChangeNotification却可以实现这个功能。因此,只有把这种两种方法结合起来,才能对一个文件夹的状态进行完全的监控。

拷贝钩子处理程序实现并不困难,首先创建一个作为进程内组件的COM对象,它只需要暴露一个ICopyHook接口(当然还有IUnknown)。然后用regsrv32.exe注册这个COM组件。最后一步是向Shell注册你的这个拷贝钩子处理程序,方法是在注册表HKEY_CLASSES_ROOT\Directory\Shellex\CopyHookHandlers下创建一个名称任意的sub key,在此sub key中创建一个类型为REG_SZ的项并将你的COM对象的CLSID作为它的默认值就可以了。

下面就是一个拷贝钩子的实现程序(注:以下代码经老妖改动并添加了详细操作过程,在BCB6中成功编译并通过测试)

1. 
ICopyHook接口创建TCopyHook,从IClassFactory接口创建TClassFactory:

// TCopyHook.h
// TCopyHook类实现了ICopyHook接口,TClassFactory实现了IClassFactory接口
//---------------------------------------------------------------------------
#define NO_WIN32_LEAN_AND_MEAN
#include <shlobj.h>
//---------------------------------------------------------------------------
class TCopyHook: public ICopyHook
{
public:
    TCopyHook():m_refcnt(0) {}
    STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();
    STDMETHODIMP_(UINT) CopyCallback(HWND hwnd, UINT wFunc,UINT wFlags,
            LPCTSTRpszSrcFile, DWORD dwSrcAttribs,
            LPCTSTRpszDestFile, DWORD dwDestAttribs);
private:
    int m_refcnt;
};
//---------------------------------------------------------------------------
class TClassFactory : public IClassFactory
{
public:
    TClassFactory():m_refcnt(0) {}
    STDMETHODIMP QueryInterface(REFIID iid, void**ppvObject);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();
    STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIIDriid, void **ppvObject);
    STDMETHODIMP LockServer(BOOL fLock);
private:
    int m_refcnt;
};


// TCopyHook.cpp
// TCopyHook对象和TClassFactory对象的实现文件
#include <stdio.h>
#include "TCopyHook.h"
//---------------------------------------------------------------------------
extern LONGnLocks;          // 对象计数,用于DllCanUnloadNow
ULONG __stdcall TCopyHook::AddRef()
{
    if(m_refcnt == 0)
        nLocks++;
    m_refcnt++;
    return m_refcnt;
}
//---------------------------------------------------------------------------
ULONG __stdcall TCopyHook::Release()
{
    int nNewCnt = --m_refcnt;
    if(nNewCnt <= 0)
    {
        nLocks--;
        delete this;
    }
    return nNewCnt;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TCopyHook::QueryInterface(REFIID dwIID, void **ppvObject)
{
    if(dwIID == IID_IUnknown)
        *ppvObject =static_cast<IUnknown*>(this);
    else
        if(dwIID == IID_IShellCopyHook)
            *ppvObject= static_cast<ICopyHook*>(this);
        else
            returnE_NOINTERFACE;
    reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
    return S_OK;
}
//---------------------------------------------------------------------------
// 这就是CopyCallback方法,拷贝钩子的所有功能由它实现。参数的具体值参看MSDN
UINT __stdcall TCopyHook::CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags,
        LPCTSTR pszSrcFile, DWORDdwSrcAttribs,
        LPCTSTR pszDestFile, DWORDdwDestAttribs)
{
    char szMessage[MAX_PATH+14];
    sprintf(szMessage, "对%s进行的操作,是否继续?", pszSrcFile);
    return MessageBox(NULL, szMessage, "确认", MB_YESNO | MB_ICONEXCLAMATION);
}
//---------------------------------------------------------------------------
ULONG __stdcall TClassFactory::AddRef()
{
    if(m_refcnt==0)
        nLocks++;
    m_refcnt++;
    return m_refcnt;
}
//---------------------------------------------------------------------------
ULONG __stdcall TClassFactory::Release()
{

    int nNewCnt = --m_refcnt;

    if(nNewCnt <= 0)
    {
        nLocks--;
        delete this;
    }
    return nNewCnt;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TClassFactory::QueryInterface(REFIID dwIID, void **ppvObject)
{
    if(dwIID == IID_IUnknown)
        *ppvObject =static_cast<IUnknown*>(this);
    else
        if(dwIID == IID_IClassFactory)
            *ppvObject= static_cast<IClassFactory*>(this);
        else
            returnE_NOINTERFACE;
    reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
    return S_OK;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TClassFactory::CreateInstance(IUnknown* pUnkownOuter,
        REFIID riid, void** ppvObj)
{
    if(pUnkownOuter != NULL)
        return CLASS_E_NOAGGREGATION;
    TCopyHook *pObj = new TCopyHook;
    pObj->AddRef();
    HRESULT hr = pObj->QueryInterface(riid, ppvObj);
    pObj->Release();
    return hr;
}
//---------------------------------------------------------------------------
HRESULT __stdcall TClassFactory::LockServer(BOOL fLock)
{
    if(fLock)
        nLocks++;
    else
        nLocks--;
    return S_OK;
}
2.  BCB New-->ActiveX-->ActiveX Library ,然后添加相应代码。
   //----------------------------------------------------------------
   // 原作: webber84
   // 修改: ccrun(老妖)(www.ccrun.com)
   // 欢迎访问C++ Builder 研究:http://www.ccrun.com
   // 为防不负责任的转载者,此处加上原作者及修改者信息,请见谅。
   //----------------------------------------------------------------
   
   以下是修改后的Project1.cpp,大家可以直接copy过去。:
   
//$$---- axlib proj source ---- (stAXLibProjectSource)
#define NO_WIN32_LEAN_AND_MEAN
#include <vcl.h>
#pragma hdrstop
#include <atl\atlvcl.h>
#include <objbase.h>
#include <olectl.h>
#include "TCopyHook.h"

#pragma package(smart_init)
TComModule  Project1Module;
TComModule &_Module = Project1Module;

// 这是要添加到注册表中的项,注意如果你要使用这段代码,应该用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;
//---------------------------------------------------------------------------
// The ATL Object map holds an array of _ATL_OBJMAP_ENTRY structures that
// described the objects of your OLE server. The MAP is handed to your
// project's CComModule-derived _Module object via the Init method.
BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()
//---------------------------------------------------------------------------
// Entry point of your Server invoked by Windows for processes or threads are
// initialized or terminated.
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
    if(reason == DLL_PROCESS_ATTACH)
        hInstance = (HMODULE)hinst;
    return TRUE;
}
//---------------------------------------------------------------------------
// _Module.Term is typically invoked from the DLL_PROCESS_DETACH of your
// DllEntryPoint. However, this may result in an incorrect shutdown sequence.
// Instead an Exit routine is setup to invoke the cleanup routine
// CComModule::Term.
void ModuleTerm(void)
{
    _Module.Term();
}
#pragma exit ModuleTerm 63
//---------------------------------------------------------------------------
// Entry point of your Server invoked to inquire whether the DLL is no
// longer in use and should be unloaded.
STDAPI __export DllCanUnloadNow(void)
{
    return nLocks == 0? S_OK: S_FALSE;
}
//---------------------------------------------------------------------------
// Entry point of your Server allowing OLE to retrieve a class object from
// your Server
STDAPI __export DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    HRESULT hr = E_OUTOFMEMORY;
    *ppv = NULL;
    TClassFactory *pClassFactory = new TClassFactory;
    if(pClassFactory != NULL)
        hr =pClassFactory->QueryInterface(riid, ppv);
    return hr;
}
//---------------------------------------------------------------------------
// Entry point of your Server invoked to instruct the server to create
// registry entries for all classes supported by the module
STDAPI __export DllRegisterServer(void)
{
    HRESULT hr = S_OK;
    int nItems = sizeof(szRegTable) /sizeof(szRegTable[0]);
    char szDllPath[MAX_PATH];
    GetModuleFileName(hInstance, szDllPath, MAX_PATH);
    for(int i=0; i<nItems && SUCCEEDED(hr); i++)
    {
        const char *szKeyName =szRegTable[i][0];
        const char *szValueName =szRegTable[i][1];
        const char *szValue =szRegTable[i][2];
        if(szValue == (const char*) -1)
            szValue= szDllPath;
        HKEY hKey;
        LONG lReturn =RegCreateKey(HKEY_CLASSES_ROOT, szKeyName, &hKey);
        if(lReturn == ERROR_SUCCESS)
        {
            RegSetValueEx(hKey,szValueName, 0, REG_SZ,
                    (constBYTE*)szValue, strlen(szValue)+1);
            RegCloseKey(hKey);
        }
        if(lReturn != ERROR_SUCCESS)
        {
            hr =SELFREG_E_CLASS;
            DllUnregisterServer();
        }
    }
    return hr;
}
//---------------------------------------------------------------------------
// Entry point of your Server invoked to instruct the server to remove
// all registry entries created through DllRegisterServer.
STDAPI __export DllUnregisterServer(void)
{
    HRESULT hr = S_OK;
    LONG lReturn = 0;
    int nItems = sizeof(szRegTable) /sizeof(szRegTable[0]);
    for(int i=nItems-1; i>=0; i--)
    {
        const char *szKeyName =szRegTable[i][0];
        if((i == nItems-1) ||stricmp(szRegTable[i+1][0], szKeyName) != 0)
            lReturn= RegDeleteKey(HKEY_CLASSES_ROOT, szKeyName);
        if(lReturn != ERROR_SUCCESS)
            hr =SELFREG_E_CLASS;
    }
    return hr;
}
//---------------------------------------------------------------------------



3.  BCB IDE 环境中,选择菜单的 Project-->Addto Project--> 找到刚才创建的 TCopyHook.cpp-->OK
   
编译工程。如果没有错误,将生成 Project1.dll

4. 
修改注册表:
   
HKEY_CLASSES_ROOT\Directory\shellex\CopyHookHandlers\ 下新建一个项,命名为 Test ,更改其默认值为 {7e10a039-fe03-4f9c- b7e1-c5eeeaf53735} ,老妖注:这里的默认值应该和 project1.cpp 中的相同注册项目相同。
   
5. 
注册 COM 组件:
   
运行 regsvr32.exe  路径 \project.dll ,点击确定后不要理会再弹出的错误窗口。重新启动计算机,试着 copy 一个文件夹,当 paste 的时候,效果出来了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值