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