Source code of Windows XP (NT5)
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.

365 lines
12 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "copy.h"
  4. UINT DefView_CopyHook(const COPYHOOKINFO *pchi);
  5. int PathCopyHookCallback(HWND hwnd, UINT wFunc, LPCTSTR pszSrc, LPCTSTR pszDest);
  6. void _CopyHookTerminate(HDSA hdsaCopyHooks, BOOL fProcessDetach);
  7. typedef struct {
  8. ICopyHook * pcphk; // Either ICopyHookA *or LPCOPYHOOK
  9. BOOL fAnsiCrossOver; // TRUE for ICopyHookA *on UNICODE build
  10. } CALLABLECOPYHOOK;
  11. typedef struct
  12. {
  13. ICopyHook cphk;
  14. ICopyHookA cphkA;
  15. LONG cRef;
  16. } CCopyHook;
  17. STDMETHODIMP_(ULONG) CCopyHook_AddRef(ICopyHook *pcphk); // forward
  18. STDMETHODIMP CCopyHook_QueryInterface(ICopyHook *pcphk, REFIID riid, void **ppvObj)
  19. {
  20. CCopyHook *this = IToClass(CCopyHook, cphk, pcphk);
  21. if (IsEqualIID(riid, &IID_IShellCopyHook) ||
  22. IsEqualIID(riid, &IID_IUnknown))
  23. {
  24. *ppvObj = pcphk;
  25. }
  26. else if (IsEqualIID(riid, &IID_IShellCopyHookA))
  27. {
  28. *ppvObj = &this->cphkA;
  29. }
  30. else
  31. {
  32. *ppvObj = NULL;
  33. return E_NOINTERFACE;
  34. }
  35. CCopyHook_AddRef(&this->cphk);
  36. return NOERROR;
  37. }
  38. STDMETHODIMP_(ULONG) CCopyHook_AddRef(ICopyHook *pcphk)
  39. {
  40. CCopyHook *this = IToClass(CCopyHook, cphk, pcphk);
  41. return InterlockedIncrement(&this->cRef);
  42. }
  43. STDMETHODIMP_(ULONG) CCopyHook_Release(ICopyHook *pcphk)
  44. {
  45. CCopyHook *this = IToClass(CCopyHook, cphk, pcphk);
  46. if (InterlockedDecrement(&this->cRef))
  47. return this->cRef;
  48. LocalFree((HLOCAL)this);
  49. return 0;
  50. }
  51. STDMETHODIMP_(UINT) CCopyHook_CopyCallback(ICopyHook *pcphk, HWND hwnd, UINT wFunc, UINT wFlags,
  52. LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs)
  53. {
  54. COPYHOOKINFO chi = { hwnd, wFunc, wFlags, pszSrcFile, dwSrcAttribs, pszDestFile, dwDestAttribs };
  55. DebugMsg(DM_TRACE, TEXT("Event = %d, File = %s , %s"), wFunc, pszSrcFile,
  56. Dbg_SafeStr(pszDestFile));
  57. // check Special Folders first...
  58. if (PathCopyHookCallback(hwnd, wFunc, pszSrcFile, pszDestFile) == IDNO)
  59. {
  60. return IDNO;
  61. }
  62. if (wFunc != FO_COPY && !(wFlags & FOF_NOCONFIRMATION))
  63. {
  64. TCHAR szShortName[MAX_PATH];
  65. BOOL fInReg = (RLIsPathInList(pszSrcFile) != -1);
  66. BOOL fInBitBucket = IsFileInBitBucket(pszSrcFile);
  67. UINT iLength = GetShortPathName(pszSrcFile, szShortName, ARRAYSIZE(szShortName));
  68. // Don't double search for names that are the same (or already found)
  69. if (iLength != 0 && lstrcmpi(pszSrcFile, szShortName) != 0)
  70. {
  71. if (!fInReg)
  72. fInReg = (RLIsPathInList(szShortName) != -1);
  73. if (!fInBitBucket)
  74. fInBitBucket = IsFileInBitBucket(szShortName);
  75. }
  76. if (fInReg && !fInBitBucket)
  77. {
  78. LPCTSTR pszSpec = PathFindFileName(pszSrcFile);
  79. return ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_RENAMEFILESINREG),
  80. pszSpec, MB_YESNO | MB_ICONEXCLAMATION, pszSpec);
  81. }
  82. }
  83. return DefView_CopyHook(&chi);
  84. }
  85. ICopyHookVtbl c_CCopyHookVtbl = {
  86. CCopyHook_QueryInterface, CCopyHook_AddRef, CCopyHook_Release,
  87. CCopyHook_CopyCallback,
  88. };
  89. STDMETHODIMP CCopyHookA_QueryInterface(ICopyHookA *pcphkA, REFIID riid, void **ppvObj)
  90. {
  91. CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA);
  92. return CCopyHook_QueryInterface(&this->cphk,riid,ppvObj);
  93. }
  94. STDMETHODIMP_(ULONG) CCopyHookA_AddRef(ICopyHookA *pcphkA)
  95. {
  96. CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA);
  97. return CCopyHook_AddRef(&this->cphk);
  98. }
  99. STDMETHODIMP_(ULONG) CCopyHookA_Release(ICopyHookA *pcphkA)
  100. {
  101. CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA);
  102. return CCopyHook_Release(&this->cphk);
  103. }
  104. STDMETHODIMP_(UINT) CCopyHookA_CopyCallback(ICopyHookA *pcphkA, HWND hwnd, UINT wFunc, UINT wFlags,
  105. LPCSTR pszSrcFile, DWORD dwSrcAttribs, LPCSTR pszDestFile, DWORD dwDestAttribs)
  106. {
  107. WCHAR szSrcFileW[MAX_PATH];
  108. WCHAR szDestFileW[MAX_PATH];
  109. LPWSTR pszSrcFileW = NULL;
  110. LPWSTR pszDestFileW = NULL;
  111. CCopyHook *this = IToClass(CCopyHook, cphkA, pcphkA);
  112. if (pszSrcFile)
  113. {
  114. SHAnsiToUnicode(pszSrcFile, szSrcFileW, ARRAYSIZE(szSrcFileW));
  115. pszSrcFileW = szSrcFileW;
  116. }
  117. if (pszDestFile)
  118. {
  119. SHAnsiToUnicode(pszDestFile, szDestFileW, ARRAYSIZE(szDestFileW));
  120. pszDestFileW = szDestFileW;
  121. }
  122. return CCopyHook_CopyCallback(&this->cphk, hwnd, wFunc, wFlags,
  123. pszSrcFileW, dwSrcAttribs,
  124. pszDestFileW, dwDestAttribs);
  125. }
  126. ICopyHookAVtbl c_CCopyHookAVtbl = {
  127. CCopyHookA_QueryInterface, CCopyHookA_AddRef, CCopyHookA_Release,
  128. CCopyHookA_CopyCallback,
  129. };
  130. STDAPI SHCreateShellCopyHook(ICopyHook **pcphkOut, REFIID riid)
  131. {
  132. HRESULT hres = E_OUTOFMEMORY; // assume error;
  133. CCopyHook *pcphk = (void*)LocalAlloc(LPTR, SIZEOF(CCopyHook));
  134. if (pcphk)
  135. {
  136. pcphk->cphk.lpVtbl = &c_CCopyHookVtbl;
  137. pcphk->cphkA.lpVtbl = &c_CCopyHookAVtbl;
  138. pcphk->cRef = 1;
  139. hres = CCopyHook_QueryInterface(&pcphk->cphk, riid, pcphkOut);
  140. CCopyHook_Release(&pcphk->cphk);
  141. }
  142. return hres;
  143. }
  144. HRESULT CCopyHook_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  145. {
  146. return SHCreateShellCopyHook((ICopyHook **)ppv, riid);
  147. }
  148. // create the HDSA of copyhook objects
  149. HDSA CreateCopyHooks(LPCTSTR pszKey)
  150. {
  151. HDSA hdsaCopyHooks = DSA_Create(SIZEOF(CALLABLECOPYHOOK), 4);
  152. if (hdsaCopyHooks)
  153. {
  154. HKEY hk;
  155. if (RegOpenKey(HKEY_CLASSES_ROOT, pszKey, &hk) == ERROR_SUCCESS)
  156. {
  157. int i;
  158. TCHAR szKey[128];
  159. // iterate through the subkeys
  160. for (i = 0; RegEnumKey(hk, i, szKey, ARRAYSIZE(szKey)) == ERROR_SUCCESS; ++i)
  161. {
  162. TCHAR szCLSID[128];
  163. LONG cb = SIZEOF(szCLSID);
  164. // for each subkey, get the class id and do a cocreateinstance
  165. if (SHRegQueryValue(hk, szKey, szCLSID, &cb) == ERROR_SUCCESS)
  166. {
  167. IUnknown *punk;
  168. HRESULT hres = SHExtCoCreateInstance(szCLSID, NULL, NULL, &IID_IUnknown, &punk);
  169. if (SUCCEEDED(hres))
  170. {
  171. CALLABLECOPYHOOK cc;
  172. SHPinDllOfCLSIDStr(szCLSID);
  173. cc.pcphk = NULL;
  174. cc.fAnsiCrossOver = FALSE;
  175. hres = punk->lpVtbl->QueryInterface(punk, &IID_IShellCopyHook, &cc.pcphk);
  176. if (SUCCEEDED(hres))
  177. {
  178. DSA_AppendItem(hdsaCopyHooks, &cc);
  179. }
  180. else
  181. {
  182. hres = punk->lpVtbl->QueryInterface(punk, &IID_IShellCopyHookA, &cc.pcphk);
  183. if (SUCCEEDED(hres))
  184. {
  185. cc.fAnsiCrossOver = TRUE;
  186. DSA_AppendItem(hdsaCopyHooks, &cc);
  187. }
  188. }
  189. punk->lpVtbl->Release(punk);
  190. }
  191. }
  192. }
  193. RegCloseKey(hk);
  194. }
  195. }
  196. return hdsaCopyHooks;
  197. }
  198. int CallCopyHooks(HDSA *phdsaHooks, LPCTSTR pszKey, HWND hwnd, UINT wFunc, FILEOP_FLAGS fFlags,
  199. LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs)
  200. {
  201. int i;
  202. if (!*phdsaHooks)
  203. {
  204. HDSA hdsaTemp = CreateCopyHooks(pszKey);
  205. if (hdsaTemp == NULL)
  206. return IDYES;
  207. // we don't hold a CritSection when doing the above to avoid deadlocks,
  208. // now we need to atomicaly store our results. if someone beat us to this
  209. // we free the hdsa we created. SHInterlockedCompareExchange does this for us
  210. // letting us know where there is a race condition so we can free the dup copy
  211. if (SHInterlockedCompareExchange((void **)phdsaHooks, hdsaTemp, 0))
  212. {
  213. // some other thread raced with us, blow this away now
  214. _CopyHookTerminate(hdsaTemp, FALSE);
  215. }
  216. }
  217. for (i = DSA_GetItemCount(*phdsaHooks) - 1; i >= 0; i--)
  218. {
  219. int iReturn;
  220. CALLABLECOPYHOOK *pcc = (CALLABLECOPYHOOK *)DSA_GetItemPtr(*phdsaHooks, i);
  221. if (!pcc->fAnsiCrossOver)
  222. {
  223. iReturn = pcc->pcphk->lpVtbl->CopyCallback(pcc->pcphk,
  224. hwnd, wFunc, fFlags, pszSrcFile, dwSrcAttribs, pszDestFile, dwDestAttribs);
  225. }
  226. else
  227. {
  228. CHAR szSrcFileA[MAX_PATH];
  229. CHAR szDestFileA[MAX_PATH];
  230. LPSTR pszSrcFileA = NULL;
  231. LPSTR pszDestFileA = NULL;
  232. ICopyHookA *pcphkA = (LPCOPYHOOKA)pcc->pcphk;
  233. if (pszSrcFile)
  234. {
  235. SHUnicodeToAnsi(pszSrcFile, szSrcFileA, ARRAYSIZE(szSrcFileA));
  236. pszSrcFileA = szSrcFileA;
  237. }
  238. if (pszDestFile)
  239. {
  240. SHUnicodeToAnsi(pszDestFile, szDestFileA, ARRAYSIZE(szDestFileA));
  241. pszDestFileA = szDestFileA;
  242. }
  243. iReturn = pcphkA->lpVtbl->CopyCallback(pcphkA,
  244. hwnd, wFunc, fFlags,
  245. pszSrcFileA, dwSrcAttribs,
  246. pszDestFileA, dwDestAttribs);
  247. }
  248. if (iReturn != IDYES)
  249. return iReturn;
  250. }
  251. return IDYES;
  252. }
  253. // These need to be per-instance since we are storing interfaces pointers
  254. HDSA g_hdsaFileCopyHooks = NULL;
  255. HDSA g_hdsaPrinterCopyHooks = NULL;
  256. int CallFileCopyHooks(HWND hwnd, UINT wFunc, FILEOP_FLAGS fFlags,
  257. LPCTSTR pszSrcFile, DWORD dwSrcAttribs, LPCTSTR pszDestFile, DWORD dwDestAttribs)
  258. {
  259. return CallCopyHooks(&g_hdsaFileCopyHooks, STRREG_SHEX_COPYHOOK, hwnd,
  260. wFunc, fFlags, pszSrcFile, dwSrcAttribs, pszDestFile, dwDestAttribs);
  261. }
  262. int CallPrinterCopyHooks(HWND hwnd, UINT wFunc, PRINTEROP_FLAGS fFlags,
  263. LPCTSTR pszSrcPrinter, DWORD dwSrcAttribs, LPCTSTR pszDestPrinter, DWORD dwDestAttribs)
  264. {
  265. return CallCopyHooks(&g_hdsaPrinterCopyHooks, STRREG_SHEX_PRNCOPYHOOK, hwnd,
  266. wFunc, fFlags, pszSrcPrinter, dwSrcAttribs, pszDestPrinter, dwDestAttribs);
  267. }
  268. //
  269. // We will only call this on process detach, and these are per-process
  270. // globals, so we do not need a critical section here
  271. //
  272. // This function is also called from CreateCopyHooks when the second
  273. // thread is cleaning up its local hdsaCopyHoos, which does not require
  274. // a critical section either.
  275. //
  276. void _CopyHookTerminate(HDSA hdsaCopyHooks, BOOL fProcessDetach)
  277. {
  278. // Note that we must no call any of virtual functions when we are
  279. // processing PROCESS_DETACH signal, because the DLL might have been
  280. // already unloaded before shell32. We just hope that they don't
  281. // allocate any global thing to be cleaned. USER does the same thing
  282. // with undestroyed window. It does not send call its window procedure
  283. // when it is destroying an undestroyed window within its PROCESS_DETACH
  284. // code. (SatoNa/DavidDS)
  285. //
  286. if (!fProcessDetach)
  287. {
  288. int i;
  289. for (i = DSA_GetItemCount(hdsaCopyHooks) - 1; i >= 0; i--)
  290. {
  291. CALLABLECOPYHOOK *pcc = (CALLABLECOPYHOOK *)DSA_GetItemPtr(hdsaCopyHooks, i);
  292. pcc->pcphk->lpVtbl->Release(pcc->pcphk);
  293. }
  294. }
  295. DSA_Destroy(hdsaCopyHooks);
  296. }
  297. // called from ProcessDetatch
  298. // NOTE: we are seralized at this point, don't need critical sections
  299. void CopyHooksTerminate(void)
  300. {
  301. ASSERTDLLENTRY; // does not require a critical section
  302. if (g_hdsaFileCopyHooks)
  303. {
  304. _CopyHookTerminate(g_hdsaFileCopyHooks, TRUE);
  305. g_hdsaFileCopyHooks = NULL;
  306. }
  307. if (g_hdsaPrinterCopyHooks)
  308. {
  309. _CopyHookTerminate(g_hdsaPrinterCopyHooks, TRUE);
  310. g_hdsaPrinterCopyHooks = NULL;
  311. }
  312. }