DragExt.cpp 25 KB

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