DragExt.cpp 25 KB

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