DragExt.cpp 25 KB

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