DragExt.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  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, LENOF(ClassID));
  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, LENOF(ClassID));
  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. return ShellExt->QueryInterface(Riid, PpvObj);
  472. }
  473. //---------------------------------------------------------------------------
  474. STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /*Lock*/)
  475. {
  476. DEBUG(L"LockServer");
  477. return NOERROR;
  478. }
  479. //---------------------------------------------------------------------------
  480. // CShellExt
  481. CShellExt::CShellExt()
  482. {
  483. DEBUG(L"CShellExt enter");
  484. FReferenceCounter = 0L;
  485. FDataObj = NULL;
  486. FMutex = CreateMutex(NULL, false, DRAG_EXT_MUTEX);
  487. FLastTicks = 0;
  488. GRefThisDll++;
  489. DEBUG(L"CShellExt leave");
  490. }
  491. //---------------------------------------------------------------------------
  492. CShellExt::~CShellExt()
  493. {
  494. DEBUG(L"~CShellExt enter");
  495. if (FDataObj)
  496. {
  497. FDataObj->Release();
  498. }
  499. CloseHandle(FMutex);
  500. GRefThisDll--;
  501. DEBUG(L"~CShellExt leave");
  502. }
  503. //---------------------------------------------------------------------------
  504. STDMETHODIMP CShellExt::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
  505. {
  506. DEBUG(L"CShellExt::QueryInterface enter");
  507. HRESULT Result = E_NOINTERFACE;
  508. *Ppv = NULL;
  509. if (!GEnabled)
  510. {
  511. DEBUG(L"CShellExt::QueryInterface shellext disabled");
  512. }
  513. else
  514. {
  515. if (IsEqualIID(Riid, IID_IShellExtInit) || IsEqualIID(Riid, IID_IUnknown))
  516. {
  517. DEBUG(L"CShellExt::QueryInterface is IShellExtInit or IUnknown");
  518. *Ppv = (LPSHELLEXTINIT)this;
  519. }
  520. else if (IsEqualIID(Riid, IID_IShellCopyHook))
  521. {
  522. DEBUG(L"CShellExt::QueryInterface is IShellCopyHook");
  523. *Ppv = (LPCOPYHOOK)this;
  524. }
  525. if (*Ppv)
  526. {
  527. AddRef();
  528. Result = NOERROR;
  529. }
  530. }
  531. DEBUG(L"CShellExt::QueryInterface leave");
  532. return Result;
  533. }
  534. //---------------------------------------------------------------------------
  535. STDMETHODIMP_(ULONG) CShellExt::AddRef()
  536. {
  537. DEBUG(L"CShellExt::AddRef");
  538. return ++FReferenceCounter;
  539. }
  540. //---------------------------------------------------------------------------
  541. STDMETHODIMP_(ULONG) CShellExt::Release()
  542. {
  543. DEBUG(L"CShellExt::Release");
  544. if (--FReferenceCounter)
  545. {
  546. return FReferenceCounter;
  547. }
  548. delete this;
  549. return 0;
  550. }
  551. //---------------------------------------------------------------------------
  552. STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /*IDFolder*/,
  553. LPDATAOBJECT DataObj, HKEY /*RegKey*/)
  554. {
  555. DEBUG(L"CShellExt::Initialize enter");
  556. if (FDataObj != NULL)
  557. {
  558. FDataObj->Release();
  559. FDataObj = NULL;
  560. }
  561. // duplicate the object pointer and registry handle
  562. if (DataObj != NULL)
  563. {
  564. FDataObj = DataObj;
  565. DataObj->AddRef();
  566. }
  567. DEBUG(L"CShellExt::Initialize leave");
  568. return NOERROR;
  569. }
  570. //---------------------------------------------------------------------------
  571. STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND /*Hwnd*/, UINT Func, UINT /*Flags*/,
  572. LPCWSTR SrcFile, DWORD /*SrcAttribs*/, LPCWSTR DestFile, DWORD /*DestAttribs*/)
  573. {
  574. DEBUG(L"CShellExt::CopyCallback enter");
  575. UINT Result = IDYES;
  576. if (GEnabled && ((Func == FO_COPY) || (Func == FO_MOVE)))
  577. {
  578. DEBUG(L"CShellExt::CopyCallback copy or move");
  579. unsigned long Ticks = GetTickCount();
  580. if (((Ticks - FLastTicks) >= 100) ||
  581. (FLastTicks > Ticks))
  582. {
  583. DEBUG(L"CShellExt::CopyCallback interval elapsed");
  584. DEBUG(L"CShellExt::CopyCallback source / dest:");
  585. DEBUG(SrcFile);
  586. DEBUG(DestFile);
  587. FLastTicks = Ticks;
  588. const wchar_t* BackPtr = wcsrchr(SrcFile, L'\\');
  589. if ((BackPtr != NULL) &&
  590. (wcsncmp(BackPtr + 1, DRAG_EXT_DUMMY_DIR_PREFIX,
  591. DRAG_EXT_DUMMY_DIR_PREFIX_LEN) == 0))
  592. {
  593. DEBUG(L"CShellExt::CopyCallback filename has prefix");
  594. HANDLE MapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
  595. false, DRAG_EXT_MAPPING);
  596. if (MapFile != NULL)
  597. {
  598. DEBUG(L"CShellExt::CopyCallback mapfile found");
  599. TDragExtCommStruct* CommStruct;
  600. CommStruct = static_cast<TDragExtCommStruct*>(MapViewOfFile(MapFile,
  601. FILE_MAP_ALL_ACCESS, 0, 0, 0));
  602. if (CommStruct != NULL)
  603. {
  604. DEBUG(L"CShellExt::CopyCallback mapview created");
  605. unsigned long WaitResult = WaitForSingleObject(FMutex, 1000);
  606. if (WaitResult != WAIT_TIMEOUT)
  607. {
  608. DEBUG(L"CShellExt::CopyCallback mutex got");
  609. if (CommStruct->Version >= TDragExtCommStruct::MinVersion)
  610. {
  611. DEBUG(L"CShellExt::CopyCallback supported structure version");
  612. if (CommStruct->Dragging)
  613. {
  614. DEBUG(L"CShellExt::CopyCallback dragging");
  615. DEBUG(CommStruct->DropDest);
  616. bool IsDropDest;
  617. if (_wcsicmp(CommStruct->DropDest, SrcFile) == 0)
  618. {
  619. IsDropDest = true;
  620. DEBUG(L"CShellExt::CopyCallback dragged file match as is");
  621. }
  622. else
  623. {
  624. wchar_t DropDestShort[MAX_PATH];
  625. wchar_t SrcFileShort[MAX_PATH];
  626. size_t DropDestSize = GetShortPathName(CommStruct->DropDest,
  627. DropDestShort, LENOF(DropDestShort));
  628. size_t SrcFileSize = GetShortPathName(SrcFile,
  629. SrcFileShort, LENOF(SrcFileShort));
  630. if ((DropDestSize == 0) || (SrcFileSize == 0))
  631. {
  632. DEBUG(L"CShellExt::CopyCallback cannot convert paths to short form");
  633. IsDropDest = false;
  634. }
  635. else if ((DropDestSize >= LENOF(DropDestShort)) ||
  636. (SrcFileSize >= LENOF(SrcFileShort)))
  637. {
  638. DEBUG(L"CShellExt::CopyCallback short paths too long");
  639. IsDropDest = false;
  640. }
  641. else
  642. {
  643. DEBUG(DropDestShort);
  644. DEBUG(SrcFileShort);
  645. if (_wcsicmp(DropDestShort, SrcFileShort) == 0)
  646. {
  647. DEBUG(L"CShellExt::CopyCallback dragged file match after converted to short form");
  648. IsDropDest = true;
  649. }
  650. else
  651. {
  652. DEBUG(L"CShellExt::CopyCallback dragged file does NOT match");
  653. IsDropDest = false;
  654. }
  655. }
  656. }
  657. if (IsDropDest)
  658. {
  659. CommStruct->Dragging = false;
  660. wcsncpy(CommStruct->DropDest, DestFile, LENOF(CommStruct->DropDest));
  661. CommStruct->DropDest[LENOF(CommStruct->DropDest)-1] = L'\0';
  662. Result = IDNO;
  663. DEBUG(L"CShellExt::CopyCallback dragging refused");
  664. }
  665. }
  666. else
  667. {
  668. DEBUG(L"CShellExt::CopyCallback NOT dragging");
  669. }
  670. }
  671. else
  672. {
  673. DEBUG(L"CShellExt::CopyCallback unsupported structure version");
  674. }
  675. ReleaseMutex(FMutex);
  676. DEBUG(L"CShellExt::CopyCallback mutex released");
  677. }
  678. else
  679. {
  680. DEBUG(L"CShellExt::CopyCallback mutex timeout");
  681. }
  682. UnmapViewOfFile(CommStruct);
  683. }
  684. else
  685. {
  686. DEBUG(L"CShellExt::CopyCallback mapview NOT created");
  687. }
  688. CloseHandle(MapFile);
  689. }
  690. else
  691. {
  692. DEBUG(L"CShellExt::CopyCallback mapfile NOT found");
  693. }
  694. }
  695. else
  696. {
  697. DEBUG(L"CShellExt::CopyCallback filename has NOT prefix");
  698. }
  699. }
  700. else
  701. {
  702. DEBUG(L"CShellExt::CopyCallback interval NOT elapsed");
  703. }
  704. }
  705. else
  706. {
  707. if (!GEnabled)
  708. {
  709. DEBUG(L"CShellExt::CopyCallback Disabled");
  710. }
  711. else
  712. {
  713. wchar_t Buf[1024];
  714. wcscpy(Buf, L"CShellExt::CopyCallback NOT copy nor move - X");
  715. // This is to avoid using swprintf, what's in a lib,
  716. // we do not link yet, in 64-bit build (maybe this changes, once
  717. // we switch to BCB build)
  718. Buf[wcslen(Buf) - 1] = L'0' + Func;
  719. DEBUG(Buf);
  720. }
  721. }
  722. DEBUG(L"CShellExt::CopyCallback leave");
  723. return Result;
  724. }