DragExt.cpp 24 KB

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