mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
506 lines
17 KiB
506 lines
17 KiB
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "copy.h"
|
|
|
|
int PathCopyHookCallback(HWND hwnd, UINT wFunc, LPCTSTR pszSrc, LPCTSTR pszDest);
|
|
void _CopyHookTerminate(HDSA *phdsaCopyHooks, BOOL fProcessDetach);
|
|
|
|
typedef struct _CALLABLECOPYHOOK {
|
|
LPCOPYHOOK pcphk; // Either LPCOPYHOOKA or LPCOPYHOOK
|
|
BOOL fAnsiCrossOver; // TRUE for LPCOPYHOOKA on UNICODE build
|
|
} CALLABLECOPYHOOK, *LPCALLABLECOPYHOOK;
|
|
|
|
|
|
//========================================================================
|
|
// CCopyHook Class definition
|
|
//========================================================================
|
|
typedef struct _CCopyHook // dxi
|
|
{
|
|
ICopyHook cphk;
|
|
#ifdef UNICODE
|
|
ICopyHookA cphkA;
|
|
#endif
|
|
UINT cRef;
|
|
} CShellCopyHook, FAR* LPSHELLCOPYHOOK;
|
|
|
|
//========================================================================
|
|
// CShellCopyHook Member function prototypes
|
|
//========================================================================
|
|
STDMETHODIMP CShellCopyHook_QueryInterface(LPCOPYHOOK pcphk, REFIID riid, LPVOID FAR* ppvObj);
|
|
STDMETHODIMP_(ULONG) CShellCopyHook_AddRef(LPCOPYHOOK pcphk);
|
|
STDMETHODIMP_(ULONG) CShellCopyHook_Release(LPCOPYHOOK pcphk);
|
|
STDMETHODIMP_(UINT) CShellCopyHook_CopyCallback(LPCOPYHOOK pcphk, HWND hwnd, UINT wFunc, UINT wFlags, LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
|
|
LPCTSTR pszDestFile, DWORD dwDestAttribs);
|
|
|
|
//========================================================================
|
|
// CShellCopyHook Vtable
|
|
//========================================================================
|
|
ICopyHookVtbl c_CShellCopyHookVtbl = {
|
|
CShellCopyHook_QueryInterface,
|
|
CShellCopyHook_AddRef,
|
|
CShellCopyHook_Release,
|
|
CShellCopyHook_CopyCallback,
|
|
};
|
|
|
|
#ifdef UNICODE
|
|
//========================================================================
|
|
// CShellCopyHook Member function prototypes
|
|
//========================================================================
|
|
STDMETHODIMP CShellCopyHookA_QueryInterface(LPCOPYHOOKA pcphk, REFIID riid, LPVOID FAR* ppvObj);
|
|
STDMETHODIMP_(ULONG) CShellCopyHookA_AddRef(LPCOPYHOOKA pcphk);
|
|
STDMETHODIMP_(ULONG) CShellCopyHookA_Release(LPCOPYHOOKA pcphk);
|
|
STDMETHODIMP_(UINT) CShellCopyHookA_CopyCallback(LPCOPYHOOKA pcphk,
|
|
HWND hwnd, UINT wFunc, UINT wFlags,
|
|
LPCSTR pszSrcFile, DWORD dwSrcAttribs,
|
|
LPCSTR pszDestFile, DWORD dwDestAttribs);
|
|
|
|
//========================================================================
|
|
// CShellCopyHook Vtable
|
|
//========================================================================
|
|
ICopyHookAVtbl c_CShellCopyHookAVtbl = {
|
|
CShellCopyHookA_QueryInterface,
|
|
CShellCopyHookA_AddRef,
|
|
CShellCopyHookA_Release,
|
|
CShellCopyHookA_CopyCallback,
|
|
};
|
|
#endif
|
|
|
|
//========================================================================
|
|
// CShellCopyHook constructor
|
|
//========================================================================
|
|
|
|
STDAPI SHCreateShellCopyHook(LPCOPYHOOK FAR* pcphkOut, REFIID riid)
|
|
{
|
|
HRESULT hres = ResultFromScode(E_OUTOFMEMORY); // assume error;
|
|
LPSHELLCOPYHOOK pcphk;
|
|
|
|
pcphk = (void*)LocalAlloc(LPTR, SIZEOF(CShellCopyHook));
|
|
if (pcphk)
|
|
{
|
|
pcphk->cphk.lpVtbl = &c_CShellCopyHookVtbl;
|
|
#ifdef UNICODE
|
|
pcphk->cphkA.lpVtbl = &c_CShellCopyHookAVtbl;
|
|
#endif
|
|
pcphk->cRef = 1;
|
|
hres = CShellCopyHook_QueryInterface(&pcphk->cphk, riid, pcphkOut);
|
|
CShellCopyHook_Release(&pcphk->cphk);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CALLBACK CShellCopyHook_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID * ppvOut)
|
|
{
|
|
Assert(punkOuter == NULL);
|
|
return(SHCreateShellCopyHook((LPCOPYHOOK *)ppvOut, riid));
|
|
}
|
|
|
|
|
|
//========================================================================
|
|
// CShellCopyHook members
|
|
//========================================================================
|
|
STDMETHODIMP CShellCopyHook_QueryInterface(LPCOPYHOOK pcphk, REFIID riid,
|
|
LPVOID FAR* ppvObj)
|
|
{
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphk, pcphk);
|
|
if (IsEqualIID(riid, &IID_IShellCopyHook)
|
|
|| IsEqualIID(riid, &IID_IUnknown))
|
|
{
|
|
*ppvObj = pcphk;
|
|
this->cRef++;
|
|
return NOERROR;
|
|
}
|
|
#ifdef UNICODE
|
|
else if (IsEqualIID(riid, &IID_IShellCopyHookA))
|
|
{
|
|
*ppvObj = &this->cphkA;
|
|
this->cRef++;
|
|
return NOERROR;
|
|
}
|
|
#endif
|
|
|
|
*ppvObj = NULL;
|
|
return(ResultFromScode(E_NOINTERFACE));
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CShellCopyHook_AddRef(LPCOPYHOOK pcphk)
|
|
{
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphk, pcphk);
|
|
this->cRef++;
|
|
return this->cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CShellCopyHook_Release(LPCOPYHOOK pcphk)
|
|
{
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphk, pcphk);
|
|
this->cRef--;
|
|
if (this->cRef > 0)
|
|
{
|
|
return this->cRef;
|
|
}
|
|
|
|
LocalFree((HLOCAL)this);
|
|
return 0;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(UINT) CShellCopyHook_CopyCallback(LPCOPYHOOK pcphk, HWND hwnd,
|
|
UINT wFunc, UINT wFlags,
|
|
LPCTSTR pszSrcFile,
|
|
DWORD dwSrcAttribs,
|
|
LPCTSTR pszDestFile,
|
|
DWORD dwDestAttribs)
|
|
{
|
|
extern UINT DefView_CopyHook(const COPYHOOKINFO *pchi);
|
|
UINT idRet;
|
|
COPYHOOKINFO chi = { hwnd, wFunc, wFlags,
|
|
pszSrcFile, dwSrcAttribs,
|
|
pszDestFile, dwDestAttribs };
|
|
|
|
DebugMsg(DM_TRACE, TEXT("Event = %d, File = %s , %s"), wFunc, pszSrcFile,
|
|
pszDestFile ? pszDestFile : 0);
|
|
|
|
// First try see source path is in the list...
|
|
|
|
if (wFunc != FO_COPY && !(wFlags & FOF_NOCONFIRMATION))
|
|
{
|
|
TCHAR szShortName[MAX_PATH];
|
|
BOOL fInReg;
|
|
BOOL fInBitBucket;
|
|
UINT iLength;
|
|
|
|
fInReg = (RLIsPathInList(pszSrcFile) != -1);
|
|
fInBitBucket = IsFileInBitBucket(pszSrcFile);
|
|
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)
|
|
{
|
|
// Move to resource
|
|
return ShellMessageBox(HINST_THISDLL, hwnd,
|
|
MAKEINTRESOURCE(IDS_RENAMEFILESINREG),
|
|
pszSrcFile, MB_YESNO | MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
idRet = DefView_CopyHook(&chi);
|
|
if (idRet!=IDYES) {
|
|
return idRet;
|
|
}
|
|
|
|
// REVIEW: this should be moved to the network dll.
|
|
return PathCopyHookCallback(hwnd, wFunc, pszSrcFile, pszDestFile);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
//========================================================================
|
|
// CShellCopyHookA members
|
|
//========================================================================
|
|
STDMETHODIMP CShellCopyHookA_QueryInterface(LPCOPYHOOKA pcphkA, REFIID riid,
|
|
LPVOID FAR* ppvObj)
|
|
{
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphkA, pcphkA);
|
|
|
|
return CShellCopyHook_QueryInterface(&this->cphk,riid,ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CShellCopyHookA_AddRef(LPCOPYHOOKA pcphkA)
|
|
{
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphkA, pcphkA);
|
|
|
|
return CShellCopyHook_AddRef(&this->cphk);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CShellCopyHookA_Release(LPCOPYHOOKA pcphkA)
|
|
{
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphkA, pcphkA);
|
|
|
|
return CShellCopyHook_Release(&this->cphk);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(UINT) CShellCopyHookA_CopyCallback(LPCOPYHOOKA 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;
|
|
LPSHELLCOPYHOOK this = IToClass(CShellCopyHook, cphkA, pcphkA);
|
|
|
|
if (pszSrcFile)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
pszSrcFile, -1,
|
|
szSrcFileW, ARRAYSIZE(szSrcFileW));
|
|
pszSrcFileW = szSrcFileW;
|
|
}
|
|
|
|
if (pszDestFile)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
pszDestFile, -1,
|
|
szDestFileW, ARRAYSIZE(szDestFileW));
|
|
pszDestFileW = szDestFileW;
|
|
}
|
|
|
|
return CShellCopyHook_CopyCallback(&this->cphk, hwnd, wFunc, wFlags,
|
|
pszSrcFileW, dwSrcAttribs,
|
|
pszDestFileW, dwDestAttribs);
|
|
}
|
|
#endif
|
|
|
|
|
|
///// here we actually use the hook...
|
|
|
|
|
|
BOOL CopyHookInitialize(HDSA *phdsaCopyHooks, LPCTSTR lpszSubKey)
|
|
{
|
|
HKEY hk;
|
|
TCHAR szKey[128];
|
|
TCHAR szValue[128];
|
|
int i;
|
|
LONG cb;
|
|
BOOL bRet = TRUE;
|
|
HDSA hdsaCopyHooks;
|
|
|
|
// Note that we check *phdsaCopyHooks again in case we were in the middle
|
|
// of initializing it when we checked it before entering this function
|
|
if (*phdsaCopyHooks)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
hdsaCopyHooks = DSA_Create(SIZEOF(CALLABLECOPYHOOK), 4);
|
|
if (!hdsaCopyHooks)
|
|
{
|
|
bRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (RegOpenKey(HKEY_CLASSES_ROOT, lpszSubKey, &hk) == ERROR_SUCCESS) {
|
|
HRESULT hres;
|
|
CALLABLECOPYHOOK cc;
|
|
IUnknown *punk;
|
|
|
|
// iterate through the subkeys
|
|
for (i = 0; RegEnumKey(hk, i, szKey, ARRAYSIZE(szKey)) == ERROR_SUCCESS; ++i) {
|
|
cb = SIZEOF(szValue);
|
|
|
|
// for each subkey, get the class id and do a cocreateinstance
|
|
if (RegQueryValue(hk, (LPTSTR)szKey, szValue, &cb) == ERROR_SUCCESS) {
|
|
|
|
hres =SHCoCreateInstance(szValue, NULL, NULL, &IID_IUnknown, &punk);
|
|
if (SUCCEEDED(hres)) {
|
|
cc.pcphk = NULL;
|
|
cc.fAnsiCrossOver = FALSE;
|
|
hres = punk->lpVtbl->QueryInterface(punk,&IID_IShellCopyHook,&cc.pcphk);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DSA_InsertItem(hdsaCopyHooks, 0x7FFF, &cc);
|
|
}
|
|
#ifdef UNICODE
|
|
else
|
|
{
|
|
hres = punk->lpVtbl->QueryInterface(punk,&IID_IShellCopyHookA,&cc.pcphk);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
cc.fAnsiCrossOver = TRUE;
|
|
DSA_InsertItem(hdsaCopyHooks, 0x7FFF, &cc);
|
|
}
|
|
}
|
|
#endif
|
|
punk->lpVtbl->Release(punk);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note that we do not fill in *phdsaCopyHooks until we have finished
|
|
// initializing it, so if any other thread sees it as non-NULL, it will
|
|
// be guaranteed to be initialized and we will not need to enter the
|
|
// critical section. If another thread sees it as NULL, we check it
|
|
// again inside the critical section to let other threads finish.
|
|
if (*phdsaCopyHooks == NULL)
|
|
{
|
|
ENTERCRITICAL;
|
|
if (*phdsaCopyHooks == NULL)
|
|
{
|
|
*phdsaCopyHooks = hdsaCopyHooks;
|
|
hdsaCopyHooks = NULL; // Indicate that we used it.
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
//
|
|
// If we did not use this hdsa (because it is already initialized
|
|
// by another thread), destroy it.
|
|
//
|
|
if (hdsaCopyHooks)
|
|
{
|
|
_CopyHookTerminate(&hdsaCopyHooks, FALSE);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return bRet;
|
|
}
|
|
|
|
int CallCopyHooks(HDSA *phdsaCopyHooks, LPCTSTR lpszSubKey,
|
|
HWND hwnd, UINT wFunc, FILEOP_FLAGS fFlags,
|
|
LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
|
|
LPCTSTR pszDestFile, DWORD dwDestAttribs)
|
|
{
|
|
LPCALLABLECOPYHOOK pcc;
|
|
int i;
|
|
int iReturn;
|
|
HDSA hdsaCopyHooks;
|
|
|
|
if (!*phdsaCopyHooks && !CopyHookInitialize(phdsaCopyHooks, lpszSubKey))
|
|
return IDYES;
|
|
|
|
hdsaCopyHooks = *phdsaCopyHooks;
|
|
|
|
for (i = DSA_GetItemCount(hdsaCopyHooks) - 1; i >= 0; i--) {
|
|
pcc = (LPCALLABLECOPYHOOK)DSA_GetItemPtr(hdsaCopyHooks, i);
|
|
#ifdef UNICODE
|
|
if (!pcc->fAnsiCrossOver)
|
|
{
|
|
#endif
|
|
iReturn = pcc->pcphk->lpVtbl->CopyCallback(pcc->pcphk,
|
|
hwnd, wFunc, fFlags,
|
|
pszSrcFile, dwSrcAttribs,
|
|
pszDestFile, dwDestAttribs);
|
|
#ifdef UNICODE
|
|
}
|
|
else
|
|
{
|
|
CHAR szSrcFileA[MAX_PATH];
|
|
CHAR szDestFileA[MAX_PATH];
|
|
LPSTR pszSrcFileA = NULL;
|
|
LPSTR pszDestFileA = NULL;
|
|
LPCOPYHOOKA pcphkA = (LPCOPYHOOKA)pcc->pcphk;
|
|
|
|
if (pszSrcFile)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
pszSrcFile, -1,
|
|
szSrcFileA, ARRAYSIZE(szSrcFileA),
|
|
NULL, NULL);
|
|
pszSrcFileA = szSrcFileA;
|
|
}
|
|
if (pszDestFile)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
pszDestFile, -1,
|
|
szDestFileA, ARRAYSIZE(szDestFileA),
|
|
NULL, NULL);
|
|
pszDestFileA = szDestFileA;
|
|
}
|
|
iReturn = pcphkA->lpVtbl->CopyCallback(pcphkA,
|
|
hwnd, wFunc, fFlags,
|
|
pszSrcFileA, dwSrcAttribs,
|
|
pszDestFileA, dwDestAttribs);
|
|
|
|
}
|
|
#endif
|
|
if (iReturn != IDYES) {
|
|
return iReturn;
|
|
}
|
|
}
|
|
return IDYES;
|
|
}
|
|
|
|
// These need to be per-instance since we are storing interfaces pointers
|
|
#pragma data_seg(DATASEG_PERINSTANCE)
|
|
HDSA g_hdsaFileCopyHooks = NULL;
|
|
HDSA g_hdsaPrinterCopyHooks = NULL;
|
|
#pragma data_seg()
|
|
|
|
const TCHAR c_szSTRREG_SHEX_COPYHOOK[] = STRREG_SHEX_COPYHOOK;
|
|
const TCHAR c_szSTRREG_SHEX_PRNCOPYHOOK[] = STRREG_SHEX_PRNCOPYHOOK;
|
|
|
|
|
|
int CallFileCopyHooks(HWND hwnd, UINT wFunc, FILEOP_FLAGS fFlags,
|
|
LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
|
|
LPCTSTR pszDestFile, DWORD dwDestAttribs)
|
|
{
|
|
return(CallCopyHooks(&g_hdsaFileCopyHooks, c_szSTRREG_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)
|
|
{
|
|
DebugMsg(DM_TRACE,TEXT("sh TR - CallPrinterCopyHook(%x)"), wFunc);
|
|
|
|
return(CallCopyHooks(&g_hdsaPrinterCopyHooks, c_szSTRREG_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 CopyHookInitialize when the second
|
|
// thread is cleaning up its local hdsaCopyHoos, which does not require
|
|
// a critical section either.
|
|
//
|
|
void _CopyHookTerminate(HDSA *phdsaCopyHooks, BOOL fProcessDetach)
|
|
{
|
|
LPCALLABLECOPYHOOK pcc;
|
|
int i;
|
|
HDSA hdsaCopyHooks = *phdsaCopyHooks;
|
|
|
|
*phdsaCopyHooks = NULL;
|
|
|
|
if (!hdsaCopyHooks)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
|
|
for (i = DSA_GetItemCount(hdsaCopyHooks) - 1; i >= 0; i--) {
|
|
pcc = (LPCALLABLECOPYHOOK)DSA_GetItemPtr(hdsaCopyHooks, i);
|
|
pcc->pcphk->lpVtbl->Release(pcc->pcphk);
|
|
}
|
|
}
|
|
DSA_Destroy(hdsaCopyHooks);
|
|
}
|
|
|
|
|
|
void CopyHooksTerminate(void)
|
|
{
|
|
if (g_hdsaFileCopyHooks)
|
|
_CopyHookTerminate(&g_hdsaFileCopyHooks, TRUE);
|
|
|
|
if (g_hdsaPrinterCopyHooks)
|
|
_CopyHookTerminate(&g_hdsaPrinterCopyHooks, TRUE);
|
|
}
|