DragExt.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. //---------------------------------------------------------------------------
  2. #pragma hdrstop
  3. //---------------------------------------------------------------------------
  4. #ifndef STRICT
  5. #define STRICT
  6. #endif
  7. //---------------------------------------------------------------------------
  8. #define LENOF(x) ( (sizeof((x))) / (sizeof(*(x))))
  9. //---------------------------------------------------------------------------
  10. #ifdef _MSC_VER
  11. #include <objbase.h>
  12. #define snwprintf _snwprintf
  13. #endif
  14. //---------------------------------------------------------------------------
  15. #include <initguid.h>
  16. #include <shlguid.h>
  17. #include <stdio.h>
  18. #pragma warn -inl
  19. #include <shlobj.h>
  20. #pragma warn .inl
  21. #include <olectl.h>
  22. #include <time.h>
  23. #include "DragExt.h"
  24. //---------------------------------------------------------------------------
  25. #ifdef __BORLANDC__
  26. #undef STDAPI
  27. #define STDAPI EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE
  28. #endif
  29. //---------------------------------------------------------------------------
  30. #define DEBUG(MSG) \
  31. if (GLogOn) \
  32. { \
  33. Debug(MSG); \
  34. }
  35. //---------------------------------------------------------------------------
  36. #define DRAG_EXT_REG_KEY L"Software\\Martin Prikryl\\WinSCP 2\\DragExt"
  37. #define DRAG_EXT_NAME L"WinSCP Shell Extension"
  38. #define THREADING_MODEL L"Apartment"
  39. #define CLSID_SIZE 39
  40. //---------------------------------------------------------------------------
  41. class CShellExtClassFactory : public IClassFactory
  42. {
  43. public:
  44. CShellExtClassFactory();
  45. virtual ~CShellExtClassFactory();
  46. // IUnknown members
  47. STDMETHODIMP QueryInterface(REFIID, LPVOID FAR*);
  48. STDMETHODIMP_(ULONG) AddRef();
  49. STDMETHODIMP_(ULONG) Release();
  50. // IClassFactory members
  51. STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR*);
  52. STDMETHODIMP LockServer(BOOL);
  53. protected:
  54. unsigned long FReferenceCounter;
  55. };
  56. //---------------------------------------------------------------------------
  57. class CShellExt : public IShellExtInit, ICopyHook
  58. {
  59. public:
  60. CShellExt();
  61. virtual ~CShellExt();
  62. // IUnknown members
  63. STDMETHODIMP QueryInterface(REFIID, LPVOID FAR*);
  64. STDMETHODIMP_(ULONG) AddRef();
  65. STDMETHODIMP_(ULONG) Release();
  66. // IShellExtInit methods
  67. STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
  68. // ICopyHook method
  69. STDMETHODIMP_(UINT) CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
  70. LPCWSTR SrcFile, DWORD SrcAttribs, LPCWSTR DestFile, DWORD DestAttribs);
  71. protected:
  72. unsigned long FReferenceCounter;
  73. LPDATAOBJECT FDataObj;
  74. HANDLE FMutex;
  75. unsigned long FLastTicks;
  76. };
  77. //---------------------------------------------------------------------------
  78. unsigned int GRefThisDll = 0;
  79. bool GEnabled = false;
  80. wchar_t GLogFile[MAX_PATH] = L"";
  81. bool GLogOn = false;
  82. FILE* GLogHandle = NULL;
  83. HANDLE GLogMutex;
  84. HINSTANCE GInstance;
  85. //---------------------------------------------------------------------------
  86. void Debug(const wchar_t* Message)
  87. {
  88. if (GLogOn)
  89. {
  90. unsigned long WaitResult = WaitForSingleObject(GLogMutex, 1000);
  91. if (WaitResult != WAIT_TIMEOUT)
  92. {
  93. try
  94. {
  95. if (GLogHandle == NULL)
  96. {
  97. if (wcslen(GLogFile) == 0)
  98. {
  99. GLogOn = false;
  100. }
  101. else
  102. {
  103. GLogHandle = _wfopen(GLogFile, L"at");
  104. if (GLogHandle == NULL)
  105. {
  106. GLogOn = false;
  107. }
  108. else
  109. {
  110. setbuf(GLogHandle, NULL);
  111. fwprintf(GLogHandle, L"----------------------------\n");
  112. }
  113. }
  114. }
  115. if (GLogOn)
  116. {
  117. SYSTEMTIME Time;
  118. GetSystemTime(&Time);
  119. fwprintf(GLogHandle, L"[%2d/%2d/%4d %2d:%02d:%02d.%03d][%04x] %s\n",
  120. Time.wDay, Time.wMonth, Time.wYear, Time.wHour, Time.wMinute,
  121. Time.wSecond, Time.wMilliseconds, GetCurrentThreadId(), Message);
  122. }
  123. }
  124. catch(...)
  125. {
  126. }
  127. ReleaseMutex(GLogMutex);
  128. }
  129. }
  130. }
  131. //---------------------------------------------------------------------------
  132. void LogVersion(HINSTANCE HInstance)
  133. {
  134. if (GLogOn)
  135. {
  136. wchar_t FileName[MAX_PATH];
  137. if (GetModuleFileName(HInstance, FileName, LENOF(FileName)) > 0)
  138. {
  139. Debug(FileName);
  140. unsigned long InfoHandle, Size;
  141. Size = GetFileVersionInfoSize(FileName, &InfoHandle);
  142. if (Size > 0)
  143. {
  144. void* Info;
  145. Info = new wchar_t[Size];
  146. if (GetFileVersionInfo(FileName, InfoHandle, Size, Info) != 0)
  147. {
  148. VS_FIXEDFILEINFO* VersionInfo;
  149. unsigned int VersionInfoSize;
  150. if (VerQueryValue(Info, L"\\", reinterpret_cast<void**>(&VersionInfo),
  151. &VersionInfoSize) != 0)
  152. {
  153. wchar_t VersionStr[100];
  154. snwprintf(VersionStr, LENOF(VersionStr), L"LogVersion %d.%d.%d.%d",
  155. HIWORD(VersionInfo->dwFileVersionMS),
  156. LOWORD(VersionInfo->dwFileVersionMS),
  157. HIWORD(VersionInfo->dwFileVersionLS),
  158. LOWORD(VersionInfo->dwFileVersionLS));
  159. Debug(VersionStr);
  160. }
  161. else
  162. {
  163. Debug(L"LogVersion no fixed version info");
  164. }
  165. }
  166. else
  167. {
  168. Debug(L"LogVersion cannot read version info");
  169. }
  170. }
  171. else
  172. {
  173. Debug(L"LogVersion no version info");
  174. }
  175. }
  176. }
  177. }
  178. //---------------------------------------------------------------------------
  179. extern "C" int APIENTRY
  180. DllMain(HINSTANCE HInstance, DWORD Reason, LPVOID /*Reserved*/)
  181. {
  182. if (Reason == DLL_PROCESS_ATTACH)
  183. {
  184. GInstance = HInstance;
  185. if (GRefThisDll == 0)
  186. {
  187. GLogMutex = CreateMutex(NULL, false, L"WinSCPDragExtLogMutex");
  188. for (int Root = 0; Root <= 1; Root++)
  189. {
  190. HKEY Key;
  191. if (RegOpenKeyEx(Root == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
  192. DRAG_EXT_REG_KEY, 0,
  193. STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  194. &Key) == ERROR_SUCCESS)
  195. {
  196. unsigned long Type;
  197. unsigned long Value;
  198. unsigned long Size;
  199. wchar_t Buf[MAX_PATH];
  200. Size = sizeof(Value);
  201. if ((RegQueryValueEx(Key, L"Enable", NULL, &Type,
  202. reinterpret_cast<unsigned char*>(&Value), &Size) == ERROR_SUCCESS) &&
  203. (Type == REG_DWORD))
  204. {
  205. GEnabled = (Value != 0);
  206. }
  207. Size = sizeof(Buf);
  208. if ((RegQueryValueEx(Key, L"LogFile", NULL, &Type,
  209. reinterpret_cast<unsigned char*>(&Buf), &Size) == ERROR_SUCCESS) &&
  210. (Type == REG_SZ))
  211. {
  212. wcsncpy(GLogFile, Buf, LENOF(GLogFile));
  213. GLogFile[LENOF(GLogFile) - 1] = L'\0';
  214. GLogOn = true;
  215. }
  216. RegCloseKey(Key);
  217. }
  218. }
  219. DEBUG(L"DllMain loaded settings");
  220. DEBUG(GEnabled ? L"DllMain enabled" : L"DllMain disabled");
  221. #ifdef UNICODE
  222. DEBUG(L"Unicode");
  223. #else
  224. DEBUG(L"Ansi");
  225. #endif
  226. LogVersion(HInstance);
  227. }
  228. else
  229. {
  230. DEBUG(L"DllMain settings already loaded");
  231. }
  232. DEBUG(L"DllMain attach leave");
  233. }
  234. else if (Reason == DLL_PROCESS_DETACH)
  235. {
  236. DEBUG(L"DllMain detach enter");
  237. CloseHandle(GLogMutex);
  238. }
  239. return 1; // ok
  240. }
  241. //---------------------------------------------------------------------------
  242. STDAPI DllCanUnloadNow(void)
  243. {
  244. bool CanUnload = (GRefThisDll == 0);
  245. DEBUG(CanUnload ? L"DllCanUnloadNow can" : L"DllCanUnloadNow cannot");
  246. return (CanUnload ? S_OK : S_FALSE);
  247. }
  248. //---------------------------------------------------------------------------
  249. STDAPI DllGetClassObject(REFCLSID Rclsid, REFIID Riid, LPVOID* PpvOut)
  250. {
  251. DEBUG(L"DllGetClassObject");
  252. *PpvOut = NULL;
  253. if (IsEqualIID(Rclsid, CLSID_ShellExtension))
  254. {
  255. DEBUG(L"DllGetClassObject is ShellExtension");
  256. CShellExtClassFactory* Pcf = new CShellExtClassFactory;
  257. return Pcf->QueryInterface(Riid, PpvOut);
  258. }
  259. return CLASS_E_CLASSNOTAVAILABLE;
  260. }
  261. //---------------------------------------------------------------------------
  262. bool RegisterServer(bool AllUsers)
  263. {
  264. DEBUG(L"RegisterServer enter");
  265. DEBUG(AllUsers ? L"RegisterServer all users" : L"RegisterServer current users");
  266. bool Result = false;
  267. HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  268. HKEY HKey;
  269. DWORD Unused;
  270. wchar_t ClassID[CLSID_SIZE];
  271. StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);
  272. if ((RegOpenKeyEx(RootKey, L"Software\\Classes", 0, KEY_WRITE, &HKey) ==
  273. ERROR_SUCCESS) &&
  274. (RegCreateKeyEx(HKey, L"CLSID", 0, NULL,
  275. REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey, &Unused) ==
  276. ERROR_SUCCESS))
  277. {
  278. DEBUG(L"RegisterServer CLSID created");
  279. if (RegCreateKey(HKey, ClassID, &HKey) == ERROR_SUCCESS)
  280. {
  281. DEBUG(L"RegisterServer class ID created");
  282. RegSetValueEx(HKey, NULL, 0, REG_SZ,
  283. reinterpret_cast<const unsigned char*>(DRAG_EXT_NAME), sizeof(DRAG_EXT_NAME));
  284. if (RegCreateKey(HKey, L"InProcServer32", &HKey) == ERROR_SUCCESS)
  285. {
  286. DEBUG(L"RegisterServer InProcServer32 created");
  287. wchar_t Filename[MAX_PATH];
  288. GetModuleFileName(GInstance, Filename, LENOF(Filename));
  289. RegSetValueEx(HKey, NULL, 0, REG_SZ,
  290. reinterpret_cast<unsigned char*>(Filename), (wcslen(Filename) + 1) * sizeof(wchar_t));
  291. RegSetValueEx(HKey, L"ThreadingModel", 0, REG_SZ,
  292. reinterpret_cast<const unsigned char*>(THREADING_MODEL),
  293. sizeof(THREADING_MODEL));
  294. }
  295. }
  296. RegCloseKey(HKey);
  297. if ((RegOpenKeyEx(RootKey, L"Software\\Classes",
  298. 0, KEY_WRITE, &HKey) == ERROR_SUCCESS) &&
  299. (RegCreateKeyEx(HKey,
  300. L"directory\\shellex\\CopyHookHandlers\\WinSCPCopyHook",
  301. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
  302. &Unused) == ERROR_SUCCESS))
  303. {
  304. DEBUG(L"RegisterServer WinSCPCopyHook created");
  305. RegSetValueEx(HKey, NULL, 0, REG_SZ,
  306. reinterpret_cast<unsigned char*>(ClassID), (wcslen(ClassID) + 1) * sizeof(wchar_t));
  307. RegCloseKey(HKey);
  308. if ((RegCreateKeyEx(RootKey, DRAG_EXT_REG_KEY,
  309. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
  310. &Unused) == ERROR_SUCCESS))
  311. {
  312. DEBUG(L"RegisterServer drag ext key created");
  313. unsigned long Value = 1;
  314. RegSetValueEx(HKey, L"Enable", 0, REG_DWORD,
  315. reinterpret_cast<unsigned char*>(&Value), sizeof(Value));
  316. RegCloseKey(HKey);
  317. Result = true;
  318. }
  319. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
  320. }
  321. }
  322. DEBUG(L"RegisterServer leave");
  323. return Result;
  324. }
  325. //---------------------------------------------------------------------------
  326. STDAPI DllRegisterServer()
  327. {
  328. DEBUG(L"DllRegisterServer enter");
  329. HRESULT Result;
  330. if (RegisterServer(true) || RegisterServer(false))
  331. {
  332. Result = S_OK;
  333. }
  334. else
  335. {
  336. Result = SELFREG_E_CLASS;
  337. }
  338. DEBUG(L"DllRegisterServer leave");
  339. return Result;
  340. }
  341. //---------------------------------------------------------------------------
  342. bool UnregisterServer(bool AllUsers)
  343. {
  344. DEBUG(L"UnregisterServer enter");
  345. DEBUG(AllUsers ? L"UnregisterServer all users" : L"UnregisterServer current users");
  346. bool Result = false;
  347. wchar_t ClassID[CLSID_SIZE];
  348. StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);
  349. HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  350. HKEY HKey;
  351. if ((RegOpenKeyEx(RootKey, L"Software\\Classes", 0, KEY_WRITE, &HKey) ==
  352. ERROR_SUCCESS) &&
  353. (RegOpenKeyEx(HKey, L"directory\\shellex\\CopyHookHandlers",
  354. 0, KEY_WRITE, &HKey) == ERROR_SUCCESS))
  355. {
  356. RegDeleteKey(HKey, L"WinSCPCopyHook");
  357. RegCloseKey(HKey);
  358. }
  359. if ((RegOpenKeyEx(RootKey, L"Software\\Classes", 0, KEY_WRITE, &HKey) ==
  360. ERROR_SUCCESS) &&
  361. (RegOpenKeyEx(HKey, L"CLSID", 0, KEY_WRITE, &HKey) ==
  362. ERROR_SUCCESS))
  363. {
  364. if (RegOpenKeyEx(HKey, ClassID, 0, KEY_WRITE, &HKey) == ERROR_SUCCESS)
  365. {
  366. RegDeleteKey(HKey, L"InProcServer32");
  367. RegCloseKey(HKey);
  368. if ((RegOpenKeyEx(RootKey, L"Software\\Classes", 0, KEY_WRITE, &HKey) ==
  369. ERROR_SUCCESS) &&
  370. (RegOpenKeyEx(HKey, L"CLSID", 0, KEY_WRITE, &HKey) ==
  371. ERROR_SUCCESS))
  372. {
  373. RegDeleteKey(HKey, ClassID);
  374. RegCloseKey(HKey);
  375. Result = true;
  376. }
  377. }
  378. }
  379. if (RegOpenKeyEx(RootKey, DRAG_EXT_REG_KEY, 0, KEY_WRITE, &HKey) ==
  380. ERROR_SUCCESS)
  381. {
  382. // Previously we were setting the value explicitly to 0,
  383. // but doing that for both HKLM and HKCU.
  384. // While on register, we set it to 1 for HKLM only,
  385. // what makes the extension disabled effectivelly,
  386. // as KHCU value 0, is kept even after re-registration
  387. RegDeleteValue(HKey, L"Enable");
  388. RegCloseKey(HKey);
  389. Result = true;
  390. }
  391. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
  392. DEBUG(L"UnregisterServer leave");
  393. return Result;
  394. }
  395. //---------------------------------------------------------------------------
  396. STDAPI DllUnregisterServer()
  397. {
  398. DEBUG(L"DllUnregisterServer enter");
  399. HRESULT Result = SELFREG_E_CLASS;
  400. if (UnregisterServer(true))
  401. {
  402. Result = S_OK;
  403. }
  404. if (UnregisterServer(false))
  405. {
  406. Result = S_OK;
  407. }
  408. DEBUG(L"DllUnregisterServer leave");
  409. return Result;
  410. }
  411. //---------------------------------------------------------------------------
  412. CShellExtClassFactory::CShellExtClassFactory()
  413. {
  414. DEBUG(L"CShellExtClassFactory");
  415. FReferenceCounter = 0;
  416. GRefThisDll++;
  417. }
  418. //---------------------------------------------------------------------------
  419. CShellExtClassFactory::~CShellExtClassFactory()
  420. {
  421. DEBUG(L"~CShellExtClassFactory");
  422. GRefThisDll--;
  423. }
  424. //---------------------------------------------------------------------------
  425. STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
  426. {
  427. DEBUG(L"QueryInterface");
  428. *Ppv = NULL;
  429. // Any interface on this object is the object pointer
  430. if (IsEqualIID(Riid, IID_IUnknown) || IsEqualIID(Riid, IID_IClassFactory))
  431. {
  432. DEBUG(L"QueryInterface is IUnknown or IClassFactory");
  433. *Ppv = (LPCLASSFACTORY)this;
  434. AddRef();
  435. return NOERROR;
  436. }
  437. return E_NOINTERFACE;
  438. }
  439. //---------------------------------------------------------------------------
  440. STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
  441. {
  442. DEBUG(L"AddRef");
  443. return ++FReferenceCounter;
  444. }
  445. //---------------------------------------------------------------------------
  446. STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
  447. {
  448. DEBUG(L"Release");
  449. if (--FReferenceCounter)
  450. {
  451. return FReferenceCounter;
  452. }
  453. delete this;
  454. return 0;
  455. }
  456. //---------------------------------------------------------------------------
  457. STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN UnkOuter,
  458. REFIID Riid, LPVOID* PpvObj)
  459. {
  460. DEBUG(L"CreateInstance");
  461. *PpvObj = NULL;
  462. // Shell extensions typically don't support aggregation (inheritance)
  463. if (UnkOuter)
  464. {
  465. return CLASS_E_NOAGGREGATION;
  466. }
  467. // Create the main shell extension object. The shell will then call
  468. // QueryInterface with IID_IShellExtInit--this is how shell extensions are
  469. // initialized.
  470. CShellExt* ShellExt = new CShellExt(); //Create the CShellExt object
  471. if (NULL == ShellExt)
  472. {
  473. return E_OUTOFMEMORY;
  474. }
  475. return ShellExt->QueryInterface(Riid, PpvObj);
  476. }
  477. //---------------------------------------------------------------------------
  478. STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /*Lock*/)
  479. {
  480. DEBUG(L"LockServer");
  481. return NOERROR;
  482. }
  483. //---------------------------------------------------------------------------
  484. // CShellExt
  485. CShellExt::CShellExt()
  486. {
  487. DEBUG(L"CShellExt enter");
  488. FReferenceCounter = 0L;
  489. FDataObj = NULL;
  490. FMutex = CreateMutex(NULL, false, DRAG_EXT_MUTEX);
  491. FLastTicks = 0;
  492. GRefThisDll++;
  493. DEBUG(L"CShellExt leave");
  494. }
  495. //---------------------------------------------------------------------------
  496. CShellExt::~CShellExt()
  497. {
  498. DEBUG(L"~CShellExt enter");
  499. if (FDataObj)
  500. {
  501. FDataObj->Release();
  502. }
  503. CloseHandle(FMutex);
  504. GRefThisDll--;
  505. DEBUG(L"~CShellExt leave");
  506. }
  507. //---------------------------------------------------------------------------
  508. STDMETHODIMP CShellExt::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
  509. {
  510. DEBUG(L"CShellExt::QueryInterface enter");
  511. HRESULT Result = E_NOINTERFACE;
  512. *Ppv = NULL;
  513. if (!GEnabled)
  514. {
  515. DEBUG(L"CShellExt::QueryInterface shellext disabled");
  516. }
  517. else
  518. {
  519. if (IsEqualIID(Riid, IID_IShellExtInit) || IsEqualIID(Riid, IID_IUnknown))
  520. {
  521. DEBUG(L"CShellExt::QueryInterface is IShellExtInit or IUnknown");
  522. *Ppv = (LPSHELLEXTINIT)this;
  523. }
  524. else if (IsEqualIID(Riid, IID_IShellCopyHook))
  525. {
  526. DEBUG(L"CShellExt::QueryInterface is IShellCopyHook");
  527. *Ppv = (LPCOPYHOOK)this;
  528. }
  529. if (*Ppv)
  530. {
  531. AddRef();
  532. Result = NOERROR;
  533. }
  534. }
  535. DEBUG(L"CShellExt::QueryInterface leave");
  536. return Result;
  537. }
  538. //---------------------------------------------------------------------------
  539. STDMETHODIMP_(ULONG) CShellExt::AddRef()
  540. {
  541. DEBUG(L"CShellExt::AddRef");
  542. return ++FReferenceCounter;
  543. }
  544. //---------------------------------------------------------------------------
  545. STDMETHODIMP_(ULONG) CShellExt::Release()
  546. {
  547. DEBUG(L"CShellExt::Release");
  548. if (--FReferenceCounter)
  549. {
  550. return FReferenceCounter;
  551. }
  552. delete this;
  553. return 0;
  554. }
  555. //---------------------------------------------------------------------------
  556. STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /*IDFolder*/,
  557. LPDATAOBJECT DataObj, HKEY /*RegKey*/)
  558. {
  559. DEBUG(L"CShellExt::Initialize enter");
  560. if (FDataObj != NULL)
  561. {
  562. FDataObj->Release();
  563. FDataObj = NULL;
  564. }
  565. // duplicate the object pointer and registry handle
  566. if (DataObj != NULL)
  567. {
  568. FDataObj = DataObj;
  569. DataObj->AddRef();
  570. }
  571. DEBUG(L"CShellExt::Initialize leave");
  572. return NOERROR;
  573. }
  574. //---------------------------------------------------------------------------
  575. STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND /*Hwnd*/, UINT Func, UINT /*Flags*/,
  576. LPCWSTR SrcFile, DWORD /*SrcAttribs*/, LPCWSTR DestFile, DWORD /*DestAttribs*/)
  577. {
  578. DEBUG(L"CShellExt::CopyCallback enter");
  579. UINT Result = IDYES;
  580. if (GEnabled && ((Func == FO_COPY) || (Func == FO_MOVE)))
  581. {
  582. DEBUG(L"CShellExt::CopyCallback copy or move");
  583. unsigned long Ticks = GetTickCount();
  584. if (((Ticks - FLastTicks) >= 100) ||
  585. (FLastTicks > Ticks))
  586. {
  587. DEBUG(L"CShellExt::CopyCallback interval elapsed");
  588. DEBUG(L"CShellExt::CopyCallback source / dest:");
  589. DEBUG(SrcFile);
  590. DEBUG(DestFile);
  591. FLastTicks = Ticks;
  592. const wchar_t* BackPtr = wcsrchr(SrcFile, L'\\');
  593. if ((BackPtr != NULL) &&
  594. (wcsncmp(BackPtr + 1, DRAG_EXT_DUMMY_DIR_PREFIX,
  595. DRAG_EXT_DUMMY_DIR_PREFIX_LEN) == 0))
  596. {
  597. DEBUG(L"CShellExt::CopyCallback filename has prefix");
  598. HANDLE MapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
  599. false, DRAG_EXT_MAPPING);
  600. if (MapFile != NULL)
  601. {
  602. DEBUG(L"CShellExt::CopyCallback mapfile found");
  603. TDragExtCommStruct* CommStruct;
  604. CommStruct = static_cast<TDragExtCommStruct*>(MapViewOfFile(MapFile,
  605. FILE_MAP_ALL_ACCESS, 0, 0, 0));
  606. if (CommStruct != NULL)
  607. {
  608. DEBUG(L"CShellExt::CopyCallback mapview created");
  609. unsigned long WaitResult = WaitForSingleObject(FMutex, 1000);
  610. if (WaitResult != WAIT_TIMEOUT)
  611. {
  612. DEBUG(L"CShellExt::CopyCallback mutex got");
  613. if (CommStruct->Version >= TDragExtCommStruct::MinVersion)
  614. {
  615. DEBUG(L"CShellExt::CopyCallback supported structure version");
  616. if (CommStruct->Dragging)
  617. {
  618. DEBUG(L"CShellExt::CopyCallback dragging");
  619. DEBUG(CommStruct->DropDest);
  620. bool IsDropDest;
  621. if (_wcsicmp(CommStruct->DropDest, SrcFile) == 0)
  622. {
  623. IsDropDest = true;
  624. DEBUG(L"CShellExt::CopyCallback dragged file match as is");
  625. }
  626. else
  627. {
  628. wchar_t DropDestShort[MAX_PATH];
  629. wchar_t SrcFileShort[MAX_PATH];
  630. size_t DropDestSize = GetShortPathName(CommStruct->DropDest,
  631. DropDestShort, LENOF(DropDestShort));
  632. size_t SrcFileSize = GetShortPathName(SrcFile,
  633. SrcFileShort, LENOF(SrcFileShort));
  634. if ((DropDestSize == 0) || (SrcFileSize == 0))
  635. {
  636. DEBUG(L"CShellExt::CopyCallback cannot convert paths to short form");
  637. IsDropDest = false;
  638. }
  639. else if ((DropDestSize >= LENOF(DropDestShort)) ||
  640. (SrcFileSize >= LENOF(SrcFileShort)))
  641. {
  642. DEBUG(L"CShellExt::CopyCallback short paths too long");
  643. IsDropDest = false;
  644. }
  645. else
  646. {
  647. DEBUG(DropDestShort);
  648. DEBUG(SrcFileShort);
  649. if (_wcsicmp(DropDestShort, SrcFileShort) == 0)
  650. {
  651. DEBUG(L"CShellExt::CopyCallback dragged file match after converted to short form");
  652. IsDropDest = true;
  653. }
  654. else
  655. {
  656. DEBUG(L"CShellExt::CopyCallback dragged file does NOT match");
  657. IsDropDest = false;
  658. }
  659. }
  660. }
  661. if (IsDropDest)
  662. {
  663. CommStruct->Dragging = false;
  664. wcsncpy(CommStruct->DropDest, DestFile, LENOF(CommStruct->DropDest));
  665. CommStruct->DropDest[LENOF(CommStruct->DropDest)-1] = L'\0';
  666. Result = IDNO;
  667. DEBUG(L"CShellExt::CopyCallback dragging refused");
  668. }
  669. }
  670. else
  671. {
  672. DEBUG(L"CShellExt::CopyCallback NOT dragging");
  673. }
  674. }
  675. else
  676. {
  677. DEBUG(L"CShellExt::CopyCallback unsupported structure version");
  678. }
  679. ReleaseMutex(FMutex);
  680. DEBUG(L"CShellExt::CopyCallback mutex released");
  681. }
  682. else
  683. {
  684. DEBUG(L"CShellExt::CopyCallback mutex timeout");
  685. }
  686. UnmapViewOfFile(CommStruct);
  687. }
  688. else
  689. {
  690. DEBUG(L"CShellExt::CopyCallback mapview NOT created");
  691. }
  692. CloseHandle(MapFile);
  693. }
  694. else
  695. {
  696. DEBUG(L"CShellExt::CopyCallback mapfile NOT found");
  697. }
  698. }
  699. else
  700. {
  701. DEBUG(L"CShellExt::CopyCallback filename has NOT prefix");
  702. }
  703. }
  704. else
  705. {
  706. DEBUG(L"CShellExt::CopyCallback interval NOT elapsed");
  707. }
  708. }
  709. else
  710. {
  711. DEBUG(L"CShellExt::CopyCallback NOT copy nor move");
  712. }
  713. DEBUG(L"CShellExt::CopyCallback leave");
  714. return Result;
  715. }