|
用拷贝钩子实现对文件夹的监控
作者: webber84 ,如转载请保证本文档的完整性,并注明出处。 欢迎光临 C++ Builder 研究,http://www.ccrun.com/doc/go.asp?id=601 本文原出处已不知。原作:webber84, 经ccrun(老妖)修改并在BCB在调试通过。 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作为它的默认值就可以了。
下面就是一个拷贝钩子的实现程序(注:以下代码经老妖改动并添加了详细操作过程,在BCB6中成功编译并通过测试)
1. 从ICopyHook接口创建TCopyHook,从IClassFactory接口创建TClassFactory:
// TCopyHook.h // TCopyHook类实现了ICopyHook接口,TClassFactory实现了IClassFactory接口 //--------------------------------------------------------------------------- #define NO_WIN32_LEAN_AND_MEAN #include //--------------------------------------------------------------------------- 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, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, 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, REFIID riid, void **ppvObject); STDMETHODIMP LockServer(BOOL fLock); private: int m_refcnt; };
// TCopyHook.cpp // TCopyHook对象和TClassFactory对象的实现文件 #include #include "TCopyHook.h" //--------------------------------------------------------------------------- extern LONG nLocks; // 对象计数,用于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(this); else if(dwIID == IID_IShellCopyHook) *ppvObject = static_cast(this); else return E_NOINTERFACE; reinterpret_cast(*ppvObject)->AddRef(); return S_OK; } //--------------------------------------------------------------------------- // 这就是CopyCallback方法,拷贝钩子的所有功能由它实现。参数的具体值参看MSDN UINT __stdcall TCopyHook::CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs) { 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(this); else if(dwIID == IID_IClassFactory) *ppvObject = static_cast(this); else return E_NOINTERFACE; reinterpret_cast(*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 #pragma hdrstop #include #include #include #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 { 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, (const BYTE*)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-->Add to 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的时候,效果出来了,嘿嘿。自己试试吧。
|