|
|
#include "shellprv.h"
#pragma hdrstop
#include "copy.h"
UINT DefView_CopyHook(const COPYHOOKINFO *pchi); int PathCopyHookCallback(HWND hwnd, UINT wFunc, LPCTSTR pszSrc, LPCTSTR pszDest);
void _CopyHookTerminate(HDSA hdsaCopyHooks, BOOL fProcessDetach);
typedef struct { ICopyHook * pcphk; // Either ICopyHookA *or LPCOPYHOOK
BOOL fAnsiCrossOver; // TRUE for ICopyHookA *on UNICODE build
} CALLABLECOPYHOOK;
typedef struct { ICopyHook cphk; ICopyHookA cphkA; LONG cRef; } CCopyHook;
STDMETHODIMP_(ULONG) CCopyHook_AddRef(ICopyHook *pcphk); // forward
STDMETHODIMP CCopyHook_QueryInterface(ICopyHook *pcphk, REFIID riid, void **ppvObj) { CCopyHook *this = IToClass(CCopyHook, cphk, pcphk); if (IsEqualIID(riid, &IID_IShellCopyHook) || IsEqualIID(riid, &IID_IUnknown)) { *ppvObj = pcphk; } else if (IsEqualIID(riid, &IID_IShellCopyHookA)) { *ppvObj = &this->cphkA; } else { *ppvObj = NULL; return E_NOINTERFACE; } CCopyHook_AddRef(&this->cphk); return NOERROR; }
STDMETHODIMP_(ULONG) CCopyHook_AddRef(ICopyHook *pcphk) { CCopyHook *this = IToClass(CCopyHook, cphk, pcphk); return InterlockedIncrement(&this->cRef); }
STDMETHODIMP_(ULONG) CCopyHook_Release(ICopyHook *pcphk) { CCopyHook *this = IToClass(CCopyHook, cphk, pcphk);
if (InterlockedDecrement(&this->cRef)) return this->cRef;
LocalFree((HLOCAL)this); return 0; }
STDMETHODIMP_(UINT) CCopyHook_CopyCallback(ICopyHook *pcphk, HWND hwnd, UINT wFunc, UINT wFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs) { COPYHOOKINFO chi = { hwnd, wFunc, wFlags, pszSrcFile, dwSrcAttribs, pszDestFile, dwDestAttribs }; DebugMsg(DM_TRACE, TEXT("Event = %d, File = %s , %s"), wFunc, pszSrcFile, Dbg_SafeStr(pszDestFile)); // check Special Folders first...
if (PathCopyHookCallback(hwnd, wFunc, pszSrcFile, pszDestFile) == IDNO) { return IDNO; } if (wFunc != FO_COPY && !(wFlags & FOF_NOCONFIRMATION)) { TCHAR szShortName[MAX_PATH]; BOOL fInReg = (RLIsPathInList(pszSrcFile) != -1); BOOL fInBitBucket = IsFileInBitBucket(pszSrcFile); UINT iLength = GetShortPathName(pszSrcFile, szShortName, ARRAYSIZE(szShortName)); // Don't double search for names that are the same (or already found)
if (iLength != 0 && lstrcmpi(pszSrcFile, szShortName) != 0) { if (!fInReg) fInReg = (RLIsPathInList(szShortName) != -1); if (!fInBitBucket) fInBitBucket = IsFileInBitBucket(szShortName); } if (fInReg && !fInBitBucket) { LPCTSTR pszSpec = PathFindFileName(pszSrcFile); return ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_RENAMEFILESINREG), pszSpec, MB_YESNO | MB_ICONEXCLAMATION, pszSpec); } } return DefView_CopyHook(&chi); }
ICopyHookVtbl c_CCopyHookVtbl = { CCopyHook_QueryInterface, CCopyHook_AddRef, CCopyHook_Release, CCopyHook_CopyCallback, };
STDMETHODIMP CCopyHookA_QueryInterface(ICopyHookA *pcphkA, REFIID riid, void **ppvObj) { CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA); return CCopyHook_QueryInterface(&this->cphk,riid,ppvObj); }
STDMETHODIMP_(ULONG) CCopyHookA_AddRef(ICopyHookA *pcphkA) { CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA); return CCopyHook_AddRef(&this->cphk); }
STDMETHODIMP_(ULONG) CCopyHookA_Release(ICopyHookA *pcphkA) { CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA); return CCopyHook_Release(&this->cphk); }
STDMETHODIMP_(UINT) CCopyHookA_CopyCallback(ICopyHookA *pcphkA, HWND hwnd, UINT wFunc, UINT wFlags, LPCSTR pszSrcFile, DWORD dwSrcAttribs, LPCSTR pszDestFile, DWORD dwDestAttribs) { WCHAR szSrcFileW[MAX_PATH]; WCHAR szDestFileW[MAX_PATH]; LPWSTR pszSrcFileW = NULL; LPWSTR pszDestFileW = NULL; CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA);
if (pszSrcFile) { SHAnsiToUnicode(pszSrcFile, szSrcFileW, ARRAYSIZE(szSrcFileW)); pszSrcFileW = szSrcFileW; }
if (pszDestFile) { SHAnsiToUnicode(pszDestFile, szDestFileW, ARRAYSIZE(szDestFileW)); pszDestFileW = szDestFileW; }
return CCopyHook_CopyCallback(&this->cphk, hwnd, wFunc, wFlags, pszSrcFileW, dwSrcAttribs, pszDestFileW, dwDestAttribs); }
ICopyHookAVtbl c_CCopyHookAVtbl = { CCopyHookA_QueryInterface, CCopyHookA_AddRef, CCopyHookA_Release, CCopyHookA_CopyCallback, };
STDAPI SHCreateShellCopyHook(ICopyHook **pcphkOut, REFIID riid) { HRESULT hres = E_OUTOFMEMORY; // assume error;
CCopyHook *pcphk = (void*)LocalAlloc(LPTR, SIZEOF(CCopyHook)); if (pcphk) { pcphk->cphk.lpVtbl = &c_CCopyHookVtbl; pcphk->cphkA.lpVtbl = &c_CCopyHookAVtbl; pcphk->cRef = 1; hres = CCopyHook_QueryInterface(&pcphk->cphk, riid, pcphkOut); CCopyHook_Release(&pcphk->cphk); } return hres; }
HRESULT CCopyHook_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { return SHCreateShellCopyHook((ICopyHook **)ppv, riid); }
// create the HDSA of copyhook objects
HDSA CreateCopyHooks(LPCTSTR pszKey) { HDSA hdsaCopyHooks = DSA_Create(SIZEOF(CALLABLECOPYHOOK), 4); if (hdsaCopyHooks) { HKEY hk;
if (RegOpenKey(HKEY_CLASSES_ROOT, pszKey, &hk) == ERROR_SUCCESS) { int i; TCHAR szKey[128];
// iterate through the subkeys
for (i = 0; RegEnumKey(hk, i, szKey, ARRAYSIZE(szKey)) == ERROR_SUCCESS; ++i) { TCHAR szCLSID[128]; LONG cb = SIZEOF(szCLSID);
// for each subkey, get the class id and do a cocreateinstance
if (SHRegQueryValue(hk, szKey, szCLSID, &cb) == ERROR_SUCCESS) { IUnknown *punk; HRESULT hres = SHExtCoCreateInstance(szCLSID, NULL, NULL, &IID_IUnknown, &punk); if (SUCCEEDED(hres)) { CALLABLECOPYHOOK cc;
SHPinDllOfCLSIDStr(szCLSID);
cc.pcphk = NULL; cc.fAnsiCrossOver = FALSE; hres = punk->lpVtbl->QueryInterface(punk, &IID_IShellCopyHook, &cc.pcphk); if (SUCCEEDED(hres)) { DSA_AppendItem(hdsaCopyHooks, &cc); } else { hres = punk->lpVtbl->QueryInterface(punk, &IID_IShellCopyHookA, &cc.pcphk); if (SUCCEEDED(hres)) { cc.fAnsiCrossOver = TRUE; DSA_AppendItem(hdsaCopyHooks, &cc); } } punk->lpVtbl->Release(punk); } } } RegCloseKey(hk); } } return hdsaCopyHooks; }
int CallCopyHooks(HDSA *phdsaHooks, LPCTSTR pszKey, HWND hwnd, UINT wFunc, FILEOP_FLAGS fFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs) { int i;
if (!*phdsaHooks) { HDSA hdsaTemp = CreateCopyHooks(pszKey); if (hdsaTemp == NULL) return IDYES;
// we don't hold a CritSection when doing the above to avoid deadlocks,
// now we need to atomicaly store our results. if someone beat us to this
// we free the hdsa we created. SHInterlockedCompareExchange does this for us
// letting us know where there is a race condition so we can free the dup copy
if (SHInterlockedCompareExchange((void **)phdsaHooks, hdsaTemp, 0)) { // some other thread raced with us, blow this away now
_CopyHookTerminate(hdsaTemp, FALSE); } }
for (i = DSA_GetItemCount(*phdsaHooks) - 1; i >= 0; i--) { int iReturn; CALLABLECOPYHOOK *pcc = (CALLABLECOPYHOOK *)DSA_GetItemPtr(*phdsaHooks, i); if (!pcc->fAnsiCrossOver) { iReturn = pcc->pcphk->lpVtbl->CopyCallback(pcc->pcphk, hwnd, wFunc, fFlags, pszSrcFile, dwSrcAttribs, pszDestFile, dwDestAttribs); } else { CHAR szSrcFileA[MAX_PATH]; CHAR szDestFileA[MAX_PATH]; LPSTR pszSrcFileA = NULL; LPSTR pszDestFileA = NULL; ICopyHookA *pcphkA = (LPCOPYHOOKA)pcc->pcphk;
if (pszSrcFile) { SHUnicodeToAnsi(pszSrcFile, szSrcFileA, ARRAYSIZE(szSrcFileA)); pszSrcFileA = szSrcFileA; } if (pszDestFile) { SHUnicodeToAnsi(pszDestFile, szDestFileA, ARRAYSIZE(szDestFileA)); pszDestFileA = szDestFileA; } iReturn = pcphkA->lpVtbl->CopyCallback(pcphkA, hwnd, wFunc, fFlags, pszSrcFileA, dwSrcAttribs, pszDestFileA, dwDestAttribs); } if (iReturn != IDYES) return iReturn; } return IDYES; }
// These need to be per-instance since we are storing interfaces pointers
HDSA g_hdsaFileCopyHooks = NULL; HDSA g_hdsaPrinterCopyHooks = NULL;
int CallFileCopyHooks(HWND hwnd, UINT wFunc, FILEOP_FLAGS fFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs) { return CallCopyHooks(&g_hdsaFileCopyHooks, STRREG_SHEX_COPYHOOK, hwnd, wFunc, fFlags, pszSrcFile, dwSrcAttribs, pszDestFile, dwDestAttribs); }
int CallPrinterCopyHooks(HWND hwnd, UINT wFunc, PRINTEROP_FLAGS fFlags, LPCTSTR pszSrcPrinter, DWORD dwSrcAttribs, LPCTSTR pszDestPrinter, DWORD dwDestAttribs) { return CallCopyHooks(&g_hdsaPrinterCopyHooks, STRREG_SHEX_PRNCOPYHOOK, hwnd, wFunc, fFlags, pszSrcPrinter, dwSrcAttribs, pszDestPrinter, dwDestAttribs); }
//
// We will only call this on process detach, and these are per-process
// globals, so we do not need a critical section here
//
// This function is also called from CreateCopyHooks when the second
// thread is cleaning up its local hdsaCopyHoos, which does not require
// a critical section either.
//
void _CopyHookTerminate(HDSA hdsaCopyHooks, BOOL fProcessDetach) { // Note that we must no call any of virtual functions when we are
// processing PROCESS_DETACH signal, because the DLL might have been
// already unloaded before shell32. We just hope that they don't
// allocate any global thing to be cleaned. USER does the same thing
// with undestroyed window. It does not send call its window procedure
// when it is destroying an undestroyed window within its PROCESS_DETACH
// code. (SatoNa/DavidDS)
//
if (!fProcessDetach) { int i; for (i = DSA_GetItemCount(hdsaCopyHooks) - 1; i >= 0; i--) { CALLABLECOPYHOOK *pcc = (CALLABLECOPYHOOK *)DSA_GetItemPtr(hdsaCopyHooks, i); pcc->pcphk->lpVtbl->Release(pcc->pcphk); } }
DSA_Destroy(hdsaCopyHooks); }
// called from ProcessDetatch
// NOTE: we are seralized at this point, don't need critical sections
void CopyHooksTerminate(void) { ASSERTDLLENTRY; // does not require a critical section
if (g_hdsaFileCopyHooks) { _CopyHookTerminate(g_hdsaFileCopyHooks, TRUE); g_hdsaFileCopyHooks = NULL; }
if (g_hdsaPrinterCopyHooks) { _CopyHookTerminate(g_hdsaPrinterCopyHooks, TRUE); g_hdsaPrinterCopyHooks = NULL; } }
|