Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2074 lines
65 KiB

  1. /*****************************************************************************
  2. *
  3. * ftpobj.cpp - IDataObject interface
  4. *
  5. *****************************************************************************/
  6. #include "priv.h"
  7. #include "ftpobj.h"
  8. #include "ftpurl.h"
  9. #include <shlwapi.h>
  10. // CLSIDs
  11. // {299D0193-6DAA-11d2-B679-006097DF5BD4}
  12. const GUID CLSID_FtpDataObject = { 0x299d0193, 0x6daa, 0x11d2, 0xb6, 0x79, 0x0, 0x60, 0x97, 0xdf, 0x5b, 0xd4 };
  13. /*****************************************************************************
  14. *
  15. * g_dropTypes conveniently mirrors our FORMATETCs.
  16. *
  17. * Hardly coincidence, of course. Enum_Fe did the real work.
  18. *
  19. *****************************************************************************/
  20. /*****************************************************************************
  21. *
  22. * Preinitialized global data.
  23. *
  24. *****************************************************************************/
  25. FORMATETC g_formatEtcOffsets;
  26. FORMATETC g_formatPasteSucceeded;
  27. CLIPFORMAT g_cfTargetCLSID;
  28. FORMATETC g_dropTypes[] =
  29. {
  30. { 0, 0, DVASPECT_CONTENT, -1, TYMED_ISTREAM }, // DROP_FCont
  31. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_FGDW
  32. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_FGDA
  33. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_IDList
  34. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_URL
  35. // { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_Offsets
  36. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_PrefDe
  37. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_PerfDe
  38. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_FTP_PRIVATE
  39. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_OLEPERSIST - see _RenderOlePersist() for desc.
  40. { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_Hdrop
  41. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }, // DROP_FNMA
  42. { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL } // DROP_FNMW
  43. };
  44. /*****************************************************************************\
  45. GLOBAL: c_stgInit
  46. DESCRIPTION:
  47. Mostly straightforward. The only major weirdness is that cfURL
  48. is delay-rendered iff the m_pflHfpl contains only one object. Otherwise,
  49. cfURL is not supported. (URLs can refer to only one object at a time.)
  50. \*****************************************************************************/
  51. STGMEDIUM c_stgInit[] =
  52. {
  53. { 0, 0, 0 }, // DROP_FCont
  54. { TYMED_HGLOBAL, 0, 0 }, // DROP_FGDW - delay-rendered
  55. { TYMED_HGLOBAL, 0, 0 }, // DROP_FGDA - delay-rendered
  56. { TYMED_HGLOBAL, 0, 0 }, // DROP_IDList - delay-rendered
  57. { 0, 0, 0 }, // DROP_URL - opt delay-rendered
  58. // { 0, 0, 0 }, // DROP_Offsets
  59. { TYMED_HGLOBAL, 0, 0 }, // DROP_PrefDe - delay-rendered
  60. { 0, 0, 0 }, // DROP_PerfDe
  61. { TYMED_HGLOBAL, 0, 0 }, // DROP_FTP_PRIVATE
  62. { TYMED_HGLOBAL, 0, 0 }, // DROP_OLEPERSIST - see _RenderOlePersist() for desc.
  63. { 0, 0, 0 }, // DROP_Hdrop
  64. { 0, 0, 0 }, // DROP_FNMA
  65. { 0, 0, 0 } // DROP_FNMW
  66. };
  67. /*****************************************************************************\
  68. FUNCTION: TraceMsgWithFormatEtc
  69. DESCRIPTION:
  70. \*****************************************************************************/
  71. void TraceMsgWithFormat(DWORD dwFlags, LPCSTR pszBefore, LPFORMATETC pFormatEtc, LPCSTR pszAfter, HRESULT hr)
  72. {
  73. #ifdef DEBUG
  74. TCHAR szFormatName[MAX_PATH];
  75. TCHAR szMedium[MAX_PATH];
  76. szFormatName[0] = 0;
  77. szMedium[0] = 0;
  78. if (pFormatEtc)
  79. {
  80. // This may fail if it's a basic format.
  81. if (!GetClipboardFormatName(pFormatEtc->cfFormat, szFormatName, ARRAYSIZE(szFormatName)))
  82. wnsprintf(szFormatName, ARRAYSIZE(szFormatName), TEXT("Pre-defined=%d"), pFormatEtc->cfFormat);
  83. switch (pFormatEtc->tymed)
  84. {
  85. case TYMED_HGLOBAL: StrCpyN(szMedium, TEXT("HGLOBAL"), ARRAYSIZE(szMedium)); break;
  86. case TYMED_FILE: StrCpyN(szMedium, TEXT("File"), ARRAYSIZE(szMedium)); break;
  87. case TYMED_GDI: StrCpyN(szMedium, TEXT("GDI"), ARRAYSIZE(szMedium)); break;
  88. case TYMED_MFPICT: StrCpyN(szMedium, TEXT("MFPICT"), ARRAYSIZE(szMedium)); break;
  89. case TYMED_ENHMF: StrCpyN(szMedium, TEXT("ENHMF"), ARRAYSIZE(szMedium)); break;
  90. case TYMED_ISTORAGE: StrCpyN(szMedium, TEXT("ISTORAGE"), ARRAYSIZE(szMedium)); break;
  91. case TYMED_ISTREAM: StrCpyN(szMedium, TEXT("ISTREAM"), ARRAYSIZE(szMedium)); break;
  92. }
  93. }
  94. else
  95. {
  96. szMedium[0] = 0;
  97. }
  98. TraceMsg(dwFlags, "%hs [FRMTETC: %ls, lndx: %d, %ls] hr=%#08lx, %hs", pszBefore, szFormatName, pFormatEtc->lindex, szMedium, hr, pszAfter);
  99. #endif // DEBUG
  100. }
  101. /*****************************************************************************\
  102. FUNCTION: _IsLindexOkay
  103. DESCRIPTION:
  104. If ife != DROP_FCont, then pfeWant->lindex must be -1.
  105. If ife == DROP_FCont, then pfeWant->lindex must be in the range
  106. 0 ... m_pflHfpl->GetCount() - 1
  107. \*****************************************************************************/
  108. BOOL CFtpObj::_IsLindexOkay(int ife, FORMATETC *pfeWant)
  109. {
  110. BOOL fResult;
  111. if (ife != DROP_FCont)
  112. fResult = pfeWant->lindex == -1;
  113. else
  114. fResult = (LONG)pfeWant->lindex < m_pflHfpl->GetCount();
  115. return fResult;
  116. }
  117. /*****************************************************************************\
  118. FUNCTION: _FindData
  119. DESCRIPTION:
  120. Locate our FORMATETC/STGMEDIUM given a FORMATETC from somebody else.
  121. On success, stores the index found into *piOut.
  122. We do not allow clients to change the TYMED of a FORMATETC, so
  123. in fact checking the TYMED is what we want, even on a SetData.
  124. \*****************************************************************************/
  125. HRESULT CFtpObj::_FindData(FORMATETC *pfe, PINT piOut)
  126. {
  127. int nIndex;
  128. HRESULT hres = DV_E_FORMATETC;
  129. *piOut = 0;
  130. for (nIndex = DROP_FCont; nIndex < DROP_OFFERMAX; nIndex++)
  131. {
  132. ASSERT(0 == (g_dropTypes[nIndex]).ptd);
  133. ASSERT(g_dropTypes[nIndex].dwAspect == DVASPECT_CONTENT);
  134. if ((pfe->cfFormat == g_dropTypes[nIndex].cfFormat) && !ShouldSkipDropFormat(nIndex))
  135. {
  136. if (EVAL(g_dropTypes[nIndex].ptd == NULL))
  137. {
  138. if (EVAL(pfe->dwAspect == DVASPECT_CONTENT))
  139. {
  140. if (EVAL(g_dropTypes[nIndex].tymed & pfe->tymed))
  141. {
  142. if (EVAL(_IsLindexOkay(nIndex, pfe)))
  143. {
  144. *piOut = nIndex;
  145. hres = S_OK;
  146. }
  147. else
  148. hres = DV_E_LINDEX;
  149. }
  150. else
  151. hres = DV_E_TYMED;
  152. }
  153. else
  154. hres = DV_E_DVASPECT;
  155. }
  156. else
  157. hres = DV_E_DVTARGETDEVICE;
  158. break;
  159. }
  160. }
  161. return hres;
  162. }
  163. /*****************************************************************************\
  164. FUNCTION: _FindDataForGet
  165. DESCRIPTION:
  166. Locate our FORMATETC/STGMEDIUM given a FORMATETC from somebody else.
  167. On success, stores the index found into *piOut. Unlike _FindData, we will
  168. fail the call if the data object doesn't currently have the clipboard format.
  169. (Delayed render counts as "currently having it". What we are filtering out
  170. are formats for which GetData will necessarily fail.)
  171. \*****************************************************************************/
  172. HRESULT CFtpObj::_FindDataForGet(FORMATETC *pfe, PINT piOut)
  173. {
  174. HRESULT hr = _FindData(pfe, piOut);
  175. // TODO: g_cfHIDA should return an array of pidls for each folder.
  176. // If we do this, the caller will support creating Shortcuts
  177. // (LNK files) that point to these pidls. We may want to do
  178. // that later.
  179. if (SUCCEEDED(hr))
  180. {
  181. if (*piOut != DROP_FCont)
  182. {
  183. if (m_stgCache[*piOut].tymed)
  184. {
  185. // Do we have data at all?
  186. // (possibly delay-rendered)
  187. }
  188. else
  189. hr = DV_E_FORMATETC; // I guess not
  190. }
  191. else
  192. {
  193. // File contents always okay
  194. }
  195. }
  196. #ifdef DEBUG
  197. if (FAILED(hr))
  198. {
  199. //TraceMsg(TF_FTPDRAGDROP, "CFtpObj::_FindDataForGet(FORMATETC.cfFormat=%d) Failed.", pfe->cfFormat);
  200. *piOut = 0xBAADF00D;
  201. }
  202. #endif
  203. return hr;
  204. }
  205. // The following are used to enumerate sub directories when creating a list of pidls for
  206. // a directory download (Ftp->FileSys).
  207. typedef struct tagGENPIDLLIST
  208. {
  209. CFtpPidlList * ppidlList;
  210. IMalloc * pm;
  211. IProgressDialog * ppd;
  212. CWireEncoding * pwe;
  213. } GENPIDLLIST;
  214. /*****************************************************************************\
  215. FUNCTION: ProcessItemCB
  216. DESCRIPTION:
  217. This function will add the specified pidl to the list. It will then
  218. detect if it's a folder and if so, will call EnumFolder() to recursively
  219. enum it's contents and call ProcessItemCB() for each one.
  220. PARAMETERS:
  221. \*****************************************************************************/
  222. HRESULT ProcessItemCB(LPVOID pvFuncCB, HINTERNET hint, LPCITEMIDLIST pidlFull, BOOL * pfValidhinst, LPVOID pvData)
  223. {
  224. GENPIDLLIST * pGenPidlList = (GENPIDLLIST *) pvData;
  225. HRESULT hr = S_OK;
  226. // Does the user want to cancel?
  227. if (pGenPidlList->ppd && pGenPidlList->ppd->HasUserCancelled())
  228. {
  229. EVAL(SUCCEEDED(pGenPidlList->ppd->StopProgressDialog()));
  230. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  231. }
  232. if (SUCCEEDED(hr))
  233. {
  234. // No, don't cancel so continue...
  235. // Add everything except SoftLinks.
  236. // This is because dir SoftLinks may cause infinite recurion.
  237. // Someday, we may want to upload a shortcut but
  238. // that's too much work for now.
  239. if (0 != FtpPidl_GetAttributes(pidlFull))
  240. {
  241. // We exist to do this:
  242. pGenPidlList->ppidlList->InsertSorted(pidlFull);
  243. }
  244. // Is this a dir/folder that we need to recurse into?
  245. if (SUCCEEDED(hr) && (FILE_ATTRIBUTE_DIRECTORY & FtpPidl_GetAttributes(pidlFull)))
  246. {
  247. hr = EnumFolder((LPFNPROCESSITEMCB) pvFuncCB, hint, pidlFull, pGenPidlList->pwe, pfValidhinst, pvData);
  248. }
  249. }
  250. return hr;
  251. }
  252. /*****************************************************************************\
  253. FUNCTION: _ExpandPidlListRecursively
  254. DESCRIPTION:
  255. This function will take the pidl list (ppidlListSrc) and call into it
  256. to enumerate. It will provide ProcessItemCB as the callback function.
  257. This function will help it create a new CFtpPidlList which will not only
  258. contain the pidls in a base folder, but also all the pidls in any subfolders
  259. that are in the original list.
  260. Delay-render a file group descriptor.
  261. \*****************************************************************************/
  262. CFtpPidlList * CFtpObj::_ExpandPidlListRecursively(CFtpPidlList * ppidlListSrc)
  263. {
  264. GENPIDLLIST pep = {0};
  265. pep.ppidlList = NULL;
  266. pep.ppd = m_ppd;
  267. pep.pwe = m_pff->GetCWireEncoding();
  268. if (SUCCEEDED(CFtpPidlList_Create(0, NULL, &pep.ppidlList)))
  269. {
  270. m_pff->GetItemAllocator(&pep.pm);
  271. if (EVAL(m_pfd) && EVAL(pep.pm))
  272. {
  273. HINTERNET hint;
  274. if (SUCCEEDED(m_pfd->GetHint(NULL, NULL, &hint, NULL, m_pff)))
  275. {
  276. LPITEMIDLIST pidlRoot = ILClone(m_pfd->GetPidlReference());
  277. if (pidlRoot)
  278. {
  279. HRESULT hr = ppidlListSrc->RecursiveEnum(pidlRoot, ProcessItemCB, hint, (LPVOID) &pep);
  280. if (m_ppd)
  281. EVAL(SUCCEEDED(m_ppd->StopProgressDialog()));
  282. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr) && !m_fErrAlreadyDisplayed)
  283. {
  284. pep.ppidlList->Release();
  285. pep.ppidlList = NULL;
  286. // Oh, I want a real hwnd, but where or where can I get one?
  287. DisplayWininetErrorEx(NULL, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_DROPFAIL, IDS_FTPERR_WININET, MB_OK, NULL, NULL);
  288. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Wrong permissions
  289. // We need to suppress subsequent error dlgs from this location
  290. // because callers like to ask for FILEGROUPDESCRIPTORA and
  291. // if that fails, ask for FILEGROUPDESCRIPTORW and we don't
  292. // want an error dialog for each.
  293. m_fErrAlreadyDisplayed = TRUE;
  294. }
  295. ILFree(pidlRoot);
  296. }
  297. m_pfd->ReleaseHint(hint);
  298. pep.pm->Release();
  299. }
  300. }
  301. }
  302. return pep.ppidlList;
  303. }
  304. /*****************************************************************************\
  305. FUNCTION: _DelayRender_FGD
  306. DESCRIPTION:
  307. Delay-render a file group descriptor
  308. \*****************************************************************************/
  309. HGLOBAL CFtpObj::_DelayRender_FGD(BOOL fUnicode)
  310. {
  311. HGLOBAL hGlobal = NULL;
  312. if (m_fCheckSecurity &&
  313. ZoneCheckPidlAction(SAFECAST(this, IInternetSecurityMgrSite *), URLACTION_SHELL_FILE_DOWNLOAD, m_pff->GetPrivatePidlReference(), (PUAF_DEFAULT | PUAF_WARN_IF_DENIED)))
  314. {
  315. m_pflHfpl->TraceDump(m_pff->GetPrivatePidlReference(), TEXT("_DelayRender_FGD() TraceDump before"));
  316. CFtpPidlList * pPidlList;
  317. if (!m_fFGDRendered)
  318. {
  319. pPidlList = _ExpandPidlListRecursively(m_pflHfpl);
  320. if (pPidlList)
  321. {
  322. // We succeeded so now it's expanded.
  323. m_fFGDRendered = TRUE;
  324. }
  325. }
  326. else
  327. {
  328. m_pflHfpl->AddRef();
  329. pPidlList = m_pflHfpl;
  330. }
  331. if (pPidlList)
  332. {
  333. hGlobal = Misc_HFGD_Create(pPidlList, m_pff->GetPrivatePidlReference(), fUnicode);
  334. IUnknown_Set(&m_pflHfpl, pPidlList);
  335. m_pflHfpl->TraceDump(m_pff->GetPrivatePidlReference(), TEXT("_DelayRender_FGD() TraceDump after"));
  336. pPidlList->Release();
  337. }
  338. }
  339. else
  340. {
  341. // Suppress future UI. We don't need to check any more
  342. // because our pidl won't change. We could not pass PUAF_WARN_IF_DENIED
  343. // but that won't suppress the UI in the prompt case. (Only admins can
  344. // turn on the prompt case).
  345. m_fCheckSecurity = FALSE;
  346. }
  347. return hGlobal;
  348. }
  349. /*****************************************************************************\
  350. FUNCTION: _DelayRender_IDList
  351. DESCRIPTION:
  352. Delay-render an ID List Array (HIDA)
  353. \*****************************************************************************/
  354. HRESULT CFtpObj::_DelayRender_IDList(STGMEDIUM * pStgMedium)
  355. {
  356. pStgMedium->hGlobal = Misc_HIDA_Create(m_pff->GetPublicRootPidlReference(), m_pflHfpl);
  357. ASSERT(pStgMedium->hGlobal);
  358. return S_OK;
  359. }
  360. /*****************************************************************************\
  361. FUNCTION: _DelayRender_URL
  362. DESCRIPTION:
  363. The caller wants an URL in an Ansi String
  364. \*****************************************************************************/
  365. HRESULT CFtpObj::_DelayRender_URL(STGMEDIUM * pStgMedium)
  366. {
  367. LPSTR pszUrl = NULL;
  368. LPITEMIDLIST pidlFull = NULL;
  369. LPITEMIDLIST pidl = m_pflHfpl->GetPidl(0);
  370. ASSERT(pidl); // We need this
  371. // Sometimes m_pflHfpl->GetPidl(0) is fully qualified and
  372. // sometimes it's not.
  373. if (!FtpID_IsServerItemID(pidl))
  374. {
  375. pidlFull = ILCombine(m_pfd->GetPidlReference(), pidl);
  376. pidl = pidlFull;
  377. }
  378. ASSERT(m_pflHfpl->GetCount() == 1); // How do we give them more than 1 URL?
  379. if (pidl)
  380. {
  381. TCHAR szUrl[MAX_URL_STRING];
  382. if (EVAL(SUCCEEDED(UrlCreateFromPidl(pidl, SHGDN_FORADDRESSBAR, szUrl, ARRAYSIZE(szUrl), (ICU_ESCAPE | ICU_USERNAME), TRUE))))
  383. {
  384. DWORD cchSize = (lstrlen(szUrl) + 1);
  385. pszUrl = (LPSTR) LocalAlloc(LPTR, (cchSize * sizeof(CHAR)));
  386. if (pszUrl)
  387. SHTCharToAnsi(szUrl, pszUrl, cchSize);
  388. }
  389. ILFree(pidlFull);
  390. }
  391. pStgMedium->hGlobal = (HGLOBAL) pszUrl;
  392. return S_OK;
  393. }
  394. #pragma BEGIN_CONST_DATA
  395. DROPEFFECT c_deCopyLink = DROPEFFECT_COPY | DROPEFFECT_LINK;
  396. DROPEFFECT c_deLink = DROPEFFECT_LINK;
  397. #pragma END_CONST_DATA
  398. /*****************************************************************************\
  399. FUNCTION: _DelayRender_PrefDe
  400. DESCRIPTION:
  401. Delay-render a preferred drop effect.
  402. The preferred drop effect is DROPEFFECT_COPY (with DROPEFFECT_LINK as fallback),
  403. unless you are dragging an FTP site, in which case it's just DROPEFFECT_LINK.
  404. DROPEFFECT_MOVE is never preferred. We can do it; it just isn't preferred.
  405. NOTES: About DROPEFFECT_MOVE
  406. We cannot support Move on platforms before NT5 because of a Recycle Bin bug
  407. were it would clain to have succeeded with the copy but it actually didn't
  408. copy anything. On NT5, the Recycle Bin drop target will call pDataObject->SetData()
  409. with a data type of "Dropped On" and the data being the CLSID of the drop
  410. target in addition to really copying the files to the recycle bin. This will
  411. let us delete the files knowing they are in the recycle bin.
  412. \*****************************************************************************/
  413. HRESULT CFtpObj::_DelayRender_PrefDe(STGMEDIUM * pStgMedium)
  414. {
  415. DROPEFFECT * pde;
  416. if (!m_pfd->IsRoot())
  417. pde = &c_deCopyLink;
  418. else
  419. pde = &c_deLink;
  420. return Misc_CreateHglob(sizeof(*pde), pde, &pStgMedium->hGlobal);
  421. }
  422. /*****************************************************************************\
  423. FUNCTION: _RenderOlePersist
  424. DESCRIPTION:
  425. When the copy source goes away (the process shuts down), it calls
  426. OleFlushClipboard. OLE will then copy our data, release us, and then
  427. give out our data later. This works for most things except for:
  428. 1. When lindex needs to very. This doesn't work because ole doesn't know
  429. how to ask us how may lindexs they need to copy.
  430. 2. If this object has a private interface OLE doesn't know about. For us,
  431. it's IAsyncOperation.
  432. To get around this problem, we want OLE to recreate us when some possible
  433. paste target calls OleGetClipboard. We want OLE to call OleLoadFromStream()
  434. to have us CoCreated and reload our persisted data via IPersistStream.
  435. OLE doesn't want to do this by default or they may have backward compat
  436. problems so they want a sign from the heavens, or at least from us, that
  437. we will work. They ping our "OleClipboardPersistOnFlush" clipboard format
  438. to ask this.
  439. \*****************************************************************************/
  440. HRESULT CFtpObj::_RenderOlePersist(STGMEDIUM * pStgMedium)
  441. {
  442. // The actual cookie value is opaque to the outside world. Since
  443. // we don't use it either, we just leave it at zero in case we use
  444. // it in the future. It's mere existence will cause OLE to do the
  445. // use our IPersistStream, which is what we want.
  446. DWORD dwCookie = 0;
  447. return Misc_CreateHglob(sizeof(dwCookie), &dwCookie, &pStgMedium->hGlobal);
  448. }
  449. /*****************************************************************************\
  450. FUNCTION: _RenderFGD
  451. DESCRIPTION:
  452. \*****************************************************************************/
  453. HRESULT CFtpObj::_RenderFGD(int nIndex, STGMEDIUM * pStgMedium)
  454. {
  455. HRESULT hr = _DoProgressForLegacySystemsPre();
  456. if (SUCCEEDED(hr))
  457. pStgMedium->hGlobal = _DelayRender_FGD((DROP_FGDW == nIndex) ? TRUE : FALSE);
  458. if (!pStgMedium->hGlobal)
  459. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Probably failed because of Zones check.
  460. return hr;
  461. }
  462. /*****************************************************************************\
  463. FUNCTION: _ForceRender
  464. DESCRIPTION:
  465. We previously delayed rendering the data for perf reasons. This function
  466. was called, so we now need to render the data.
  467. \*****************************************************************************/
  468. HRESULT CFtpObj::_ForceRender(int nIndex)
  469. {
  470. HRESULT hr = S_OK;
  471. // We only support HGLOBALs here, but the caller may be valid
  472. // to ask for something we don't support or an extended data.
  473. // ASSERT((m_stgCache[nIndex].tymed) == TYMED_HGLOBAL);
  474. if (!m_stgCache[nIndex].hGlobal)
  475. {
  476. STGMEDIUM medium = {TYMED_HGLOBAL, 0, NULL};
  477. switch (nIndex)
  478. {
  479. case DROP_FCont:
  480. ASSERT(0);
  481. break;
  482. case DROP_FGDW:
  483. case DROP_FGDA:
  484. hr = _RenderFGD(nIndex, &medium);
  485. break;
  486. case DROP_IDList:
  487. hr = _DelayRender_IDList(&medium);
  488. break;
  489. /* Nuke
  490. case DROP_Offsets:
  491. ASSERT(0);
  492. // hglob = _DelayRender_Offsets();
  493. break;
  494. */
  495. case DROP_PrefDe:
  496. hr = _DelayRender_PrefDe(&medium);
  497. break;
  498. case DROP_PerfDe:
  499. ASSERT(0);
  500. // hglob = _DelayRender_PerfDe();
  501. break;
  502. case DROP_FTP_PRIVATE:
  503. hr = DV_E_FORMATETC;
  504. break;
  505. case DROP_OLEPERSIST:
  506. hr = _RenderOlePersist(&medium);
  507. break;
  508. case DROP_Hdrop:
  509. ASSERT(0);
  510. // hglob = _DelayRender_Hdrop();
  511. break;
  512. case DROP_FNMA:
  513. ASSERT(0);
  514. // hglob = _DelayRender_FNM();
  515. break;
  516. case DROP_FNMW:
  517. ASSERT(0);
  518. // hglob = _DelayRender_FNM();
  519. break;
  520. case DROP_URL:
  521. hr = _DelayRender_URL(&medium);
  522. break;
  523. default:
  524. ASSERT(0); // Should never hit.
  525. break;
  526. }
  527. if (medium.hGlobal) // Will fail if the Zones Security Check Fails.
  528. {
  529. m_stgCache[nIndex].pUnkForRelease = NULL;
  530. m_stgCache[nIndex].hGlobal = medium.hGlobal;
  531. }
  532. else
  533. {
  534. if (S_OK == hr)
  535. hr = E_OUTOFMEMORY;
  536. }
  537. }
  538. if (FAILED(hr))
  539. TraceMsg(TF_FTPDRAGDROP, "CFtpObj::_ForceRender() FAILED. hres=%#08lx", hr);
  540. return hr;
  541. }
  542. /*****************************************************************************\
  543. FUNCTION: _DoProgressForLegacySystemsPre
  544. DESCRIPTION:
  545. Shell's pre-NT5 didn't do progress on the File Contents drop, so we
  546. will do it here. This function will display a progress dialog while we
  547. walk the server and expand the pidls that are needed to be copied.
  548. Later,
  549. \*****************************************************************************/
  550. HRESULT CFtpObj::_DoProgressForLegacySystemsPre(void)
  551. {
  552. HRESULT hr = S_OK;
  553. if (DEBUG_LEGACY_PROGRESS || (SHELL_VERSION_NT5 > GetShellVersion()))
  554. {
  555. TraceMsg(TF_ALWAYS, "CFtpObj::_DoProgressForLegacySystemsPre() going to do the Legacy dialogs.");
  556. // Do we need to initialize the list?
  557. if (!m_ppd && (-1 == m_nStartIndex))
  558. {
  559. // Yes, so create the create the dialog and find the sizes of the list.
  560. if (m_ppd)
  561. _CloseProgressDialog();
  562. m_uliCompleted.QuadPart = 0;
  563. m_uliTotal.QuadPart = 0;
  564. m_ppd = CProgressDialog_CreateInstance(IDS_COPY_TITLE, IDA_FTPDOWNLOAD);
  565. if (m_ppd)
  566. {
  567. WCHAR wzProgressDialogStr[MAX_PATH];
  568. // Tell the user we are calculating how long it will take.
  569. if (EVAL(LoadStringW(HINST_THISDLL, IDS_PROGRESS_DOWNLOADTIMECALC, wzProgressDialogStr, ARRAYSIZE(wzProgressDialogStr))))
  570. EVAL(SUCCEEDED(m_ppd->SetLine(2, wzProgressDialogStr, FALSE, NULL)));
  571. // We give a NULL punkEnableModless because we don't want to go modal.
  572. EVAL(SUCCEEDED(m_ppd->StartProgressDialog(NULL, NULL, PROGDLG_AUTOTIME, NULL)));
  573. }
  574. }
  575. }
  576. return hr;
  577. }
  578. /*****************************************************************************\
  579. FUNCTION: _DoProgressForLegacySystemsStart
  580. DESCRIPTION:
  581. Shell's pre-NT5 didn't do progress on the File Contents drop, so we
  582. will do it here. Only return FAILED(hr) if IProgressDialog::HasUserCancelled().
  583. \*****************************************************************************/
  584. HRESULT CFtpObj::_DoProgressForLegacySystemsStart(LPCITEMIDLIST pidl, int nIndex)
  585. {
  586. HRESULT hr = S_OK;
  587. if (DEBUG_LEGACY_PROGRESS || (SHELL_VERSION_NT5 > GetShellVersion()))
  588. {
  589. TraceMsg(TF_ALWAYS, "CFtpObj::_DoProgressForLegacySystemsStart() going to do the Legacy dialogs.");
  590. // Do we need to initialize the list?
  591. if (-1 == m_nStartIndex)
  592. hr = _SetProgressDialogValues(nIndex); // Yes, so do so.
  593. if (EVAL(m_ppd))
  594. {
  595. WCHAR wzTemplate[MAX_PATH];
  596. WCHAR wzPath[MAX_PATH];
  597. WCHAR wzStatusText[MAX_PATH];
  598. WCHAR wzTemp[MAX_PATH];
  599. LPITEMIDLIST pidlBase = (LPITEMIDLIST) pidl;
  600. EVAL(SUCCEEDED(m_ppd->StartProgressDialog(NULL, NULL, PROGDLG_AUTOTIME, NULL)));
  601. // Generate the string "Downloading <FileName>..." status string
  602. EVAL(LoadStringW(HINST_THISDLL, IDS_DOWNLOADING, wzTemplate, ARRAYSIZE(wzTemplate)));
  603. FtpPidl_GetLastItemDisplayName(pidl, wzTemp, ARRAYSIZE(wzTemp));
  604. wnsprintfW(wzStatusText, ARRAYSIZE(wzStatusText), wzTemplate, wzTemp);
  605. EVAL(SUCCEEDED(m_ppd->SetLine(1, wzStatusText, FALSE, NULL)));
  606. if (FtpPidl_IsDirectory(pidl, FALSE))
  607. {
  608. pidlBase = ILClone(pidl);
  609. ILRemoveLastID(pidlBase);
  610. }
  611. // Generate the string "From <SrcFileDir>" status string
  612. GetDisplayPathFromPidl(pidlBase, wzPath, ARRAYSIZE(wzPath), TRUE);
  613. EVAL(LoadStringW(HINST_THISDLL, IDS_DL_SRC_DIR, wzTemplate, ARRAYSIZE(wzTemplate)));
  614. wnsprintfW(wzStatusText, ARRAYSIZE(wzStatusText), wzTemplate, wzPath);
  615. EVAL(SUCCEEDED(m_ppd->SetLine(2, wzStatusText, FALSE, NULL)));
  616. EVAL(SUCCEEDED(m_ppd->SetProgress64(m_uliCompleted.QuadPart, m_uliTotal.QuadPart)));
  617. TraceMsg(TF_ALWAYS, "CFtpObj::_DoProgressForLegacySystemsStart() SetProgress64(%#08lx, %#08lx)", m_uliCompleted.LowPart, m_uliTotal.LowPart);
  618. if (m_ppd->HasUserCancelled())
  619. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  620. if (pidlBase != pidl) // Did we allocated it?
  621. ILFree(pidlBase);
  622. }
  623. }
  624. return hr;
  625. }
  626. /*****************************************************************************\
  627. FUNCTION: _DoProgressForLegacySystemsPost
  628. DESCRIPTION:
  629. Shell's pre-NT5 didn't do progress on the File Contents drop, so we
  630. will do it here. Only return FAILED(hr) if IProgressDialog::HasUserCancelled().
  631. \*****************************************************************************/
  632. HRESULT CFtpObj::_DoProgressForLegacySystemsPost(LPCITEMIDLIST pidl, BOOL fLast)
  633. {
  634. HRESULT hr = S_OK;
  635. if ((DEBUG_LEGACY_PROGRESS || (SHELL_VERSION_NT5 > GetShellVersion())) && EVAL(m_ppd))
  636. {
  637. if (pidl)
  638. {
  639. // Add the file size to the Completed.
  640. m_uliCompleted.QuadPart += FtpPidl_GetFileSize(pidl);
  641. }
  642. TraceMsg(TF_ALWAYS, "CFtpObj::_DoProgressForLegacySystemsPost() Closing DLG");
  643. if (fLast)
  644. IUnknown_Set((IUnknown **)&m_ppd, NULL); // The stream will close the dialog and release it.
  645. }
  646. return hr;
  647. }
  648. HRESULT CFtpObj::_SetProgressDialogValues(int nIndex)
  649. {
  650. HRESULT hr = S_OK;
  651. m_nStartIndex = nIndex;
  652. if (EVAL(m_ppd))
  653. {
  654. // Calculate m_nEndIndex
  655. while (nIndex < m_pflHfpl->GetCount())
  656. {
  657. if (!FtpPidl_IsDirectory(m_pflHfpl->GetPidl(nIndex), FALSE))
  658. m_nEndIndex = nIndex;
  659. nIndex++;
  660. }
  661. for (nIndex = 0; nIndex < m_pflHfpl->GetCount(); nIndex++)
  662. {
  663. LPCITEMIDLIST pidl = m_pflHfpl->GetPidl(nIndex);
  664. m_uliTotal.QuadPart += FtpPidl_GetFileSize(pidl);
  665. }
  666. // Reset because the above for loop can take a long time and the estimated time
  667. // is based on the time between ::StartProgressDialog() and the first
  668. // ::SetProgress() call.
  669. EVAL(SUCCEEDED(m_ppd->Timer(PDTIMER_RESET, NULL)));
  670. }
  671. return hr;
  672. }
  673. HRESULT CFtpObj::_CloseProgressDialog(void)
  674. {
  675. m_nStartIndex = -1; // Indicate we haven't inited yet.
  676. if (m_ppd)
  677. {
  678. EVAL(SUCCEEDED(m_ppd->StopProgressDialog()));
  679. IUnknown_Set((IUnknown **)&m_ppd, NULL);
  680. }
  681. return S_OK;
  682. }
  683. HRESULT CFtpObj::_RefThread(void)
  684. {
  685. if (NULL == m_punkThreadRef)
  686. {
  687. // This is valid to fail from some hosts who won't go away,
  688. // so they don't need to support ref counting threads.
  689. SHGetThreadRef(&m_punkThreadRef);
  690. }
  691. return S_OK;
  692. }
  693. HRESULT CFtpObj::_RenderFileContents(LPFORMATETC pfe, LPSTGMEDIUM pstg)
  694. {
  695. HRESULT hr = E_INVALIDARG;
  696. // callers have a bad habit of asking for lindex == -1 because
  697. // that means 'all' data. But how can you hand out one IStream* for
  698. // all files?
  699. if (-1 != pfe->lindex)
  700. {
  701. LPITEMIDLIST pidl = m_pflHfpl->GetPidl(pfe->lindex);
  702. // FileContents are always regenerated afresh.
  703. pstg->pUnkForRelease = 0;
  704. pstg->tymed = TYMED_ISTREAM;
  705. if (pidl)
  706. {
  707. hr = _DoProgressForLegacySystemsStart(pidl, pfe->lindex);
  708. if (SUCCEEDED(hr))
  709. {
  710. // Is it a directory?
  711. if (FtpPidl_IsDirectory(pidl, FALSE))
  712. {
  713. // Yes, so pack the name and attributes
  714. hr = DV_E_LINDEX;
  715. AssertMsg(0, TEXT("Someone is asking for a FILECONTENTs for a directory item."));
  716. }
  717. else
  718. {
  719. // No, so give them the stream.
  720. // shell32 v5 will display progress dialogs, but we need to
  721. // display progress dialogs for shell32 v3 or v4. We do this
  722. // by creating the progress dialog when the caller asks for the
  723. // first stream. We then need to find out when they call for
  724. // the last stream and then hand off the IProgressDialog to the
  725. // CFtpStm. The CFtpStm will then close down the dialog when the
  726. // caller closes it.
  727. hr = CFtpStm_Create(m_pfd, pidl, GENERIC_READ, &pstg->pstm, m_uliCompleted, m_uliTotal, m_ppd, (pfe->lindex == m_nEndIndex));
  728. EVAL(SUCCEEDED(_DoProgressForLegacySystemsPost(pidl, (pfe->lindex == m_nEndIndex))));
  729. }
  730. }
  731. else
  732. {
  733. // The user may have cancelled
  734. ASSERT(HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr);
  735. }
  736. }
  737. if (FAILED(hr))
  738. _CloseProgressDialog();
  739. }
  740. //TraceMsg(TF_FTPDRAGDROP, "CFtpObj::GetData() CFtpStm_Create() returned hr=%#08lx", hr);
  741. return hr;
  742. }
  743. /*****************************************************************************\
  744. FUNCTION: IsEqualFORMATETC
  745. DESCRIPTION:
  746. The two fields of a FORMATETC that need to match to be equivalent are:
  747. cfFormat and lindex.
  748. \*****************************************************************************/
  749. BOOL IsEqualFORMATETC(FORMATETC * pfe1, FORMATETC * pfe2)
  750. {
  751. BOOL fIsEqual = FALSE;
  752. if ((pfe1->cfFormat == pfe2->cfFormat) && (pfe1->lindex == pfe2->lindex))
  753. {
  754. fIsEqual = TRUE;
  755. }
  756. return fIsEqual;
  757. }
  758. /*****************************************************************************\
  759. FUNCTION: _FreeExtraData
  760. DESCRIPTION:
  761. \*****************************************************************************/
  762. int CFtpObj::_DSA_FreeCB(LPVOID pvItem, LPVOID pvlparam)
  763. {
  764. FORMATETC_STGMEDIUM * pfs = (FORMATETC_STGMEDIUM *) pvItem;
  765. if (EVAL(pfs))
  766. ReleaseStgMedium(&(pfs->medium));
  767. return 1;
  768. }
  769. /*****************************************************************************\
  770. FUNCTION: _FindSetDataIndex
  771. DESCRIPTION:
  772. \*****************************************************************************/
  773. int CFtpObj::_FindExtraDataIndex(FORMATETC *pfe)
  774. {
  775. int nIndex;
  776. for (nIndex = (DSA_GetItemCount(m_hdsaSetData) - 1); nIndex >= 0; nIndex--)
  777. {
  778. FORMATETC_STGMEDIUM * pfs = (FORMATETC_STGMEDIUM *) DSA_GetItemPtr(m_hdsaSetData, nIndex);
  779. if (IsEqualFORMATETC(pfe, &pfs->formatEtc))
  780. {
  781. return nIndex;
  782. }
  783. }
  784. return -1;
  785. }
  786. /*****************************************************************************\
  787. FUNCTION: _SetExtraData
  788. DESCRIPTION:
  789. We don't render the data, but we will carry it because someone may need
  790. or want it. This is the case with the drag source's defview pushing in
  791. the icon points via CFSTR_SHELLIDLISTOFFSET for the drop target.
  792. \*****************************************************************************/
  793. HRESULT CFtpObj::_SetExtraData(FORMATETC *pfe, STGMEDIUM *pstg, BOOL fRelease)
  794. {
  795. HRESULT hr;
  796. int nIndex = _FindExtraDataIndex(pfe);
  797. // Do we already have someone's copy?
  798. if (-1 == nIndex)
  799. {
  800. FORMATETC_STGMEDIUM fs;
  801. fs.formatEtc = *pfe;
  802. // If there is a pointer, copy the data because we can't maintain the lifetime
  803. // of the pointer.
  804. if (fs.formatEtc.ptd)
  805. {
  806. fs.dvTargetDevice = *(pfe->ptd);
  807. fs.formatEtc.ptd = &fs.dvTargetDevice;
  808. }
  809. hr = CopyStgMediumWrap(pstg, &fs.medium);
  810. if (EVAL(SUCCEEDED(hr)))
  811. {
  812. // No, so just append it to the end.
  813. DSA_AppendItem(m_hdsaSetData, &fs);
  814. }
  815. }
  816. else
  817. {
  818. FORMATETC_STGMEDIUM fs;
  819. DSA_GetItem(m_hdsaSetData, nIndex, &fs);
  820. // Free the previous guy.
  821. ReleaseStgMedium(&fs.medium);
  822. // Yes, so Replace it.
  823. hr = CopyStgMediumWrap(pstg, &fs.medium);
  824. if (EVAL(SUCCEEDED(hr)))
  825. {
  826. // Replace the data.
  827. DSA_SetItem(m_hdsaSetData, nIndex, &fs);
  828. }
  829. }
  830. return hr;
  831. }
  832. typedef struct
  833. {
  834. DWORD dwVersion;
  835. DWORD dwExtraSize; // After pidl list
  836. BOOL fFGDRendered;
  837. DWORD dwReserved1;
  838. DWORD dwReserved2;
  839. } FTPDATAOBJ_PERSISTSTRUCT;
  840. /*****************************************************************************\
  841. FUNCTION: FormatEtcSaveToStream
  842. DESCRIPTION:
  843. \*****************************************************************************/
  844. HRESULT FormatEtcSaveToStream(IStream *pStm, FORMATETC * pFormatEtc)
  845. {
  846. HRESULT hr = E_INVALIDARG;
  847. if (pStm)
  848. {
  849. // We don't support ptd because where would the allocation be
  850. // on the load?
  851. if (EVAL(NULL == pFormatEtc->ptd))
  852. {
  853. WCHAR szFormatName[MAX_PATH];
  854. if (EVAL(GetClipboardFormatNameW(pFormatEtc->cfFormat, szFormatName, ARRAYSIZE(szFormatName))))
  855. {
  856. DWORD cbFormatNameSize = ((lstrlenW(szFormatName) + 1) * sizeof(szFormatName[0]));
  857. hr = pStm->Write(pFormatEtc, SIZEOF(*pFormatEtc), NULL);
  858. if (SUCCEEDED(hr))
  859. {
  860. hr = pStm->Write(&cbFormatNameSize, SIZEOF(cbFormatNameSize), NULL);
  861. if (SUCCEEDED(hr))
  862. {
  863. hr = pStm->Write(szFormatName, cbFormatNameSize, NULL);
  864. }
  865. }
  866. }
  867. else
  868. hr = HRESULT_FROM_WIN32(GetLastError());
  869. }
  870. }
  871. return hr;
  872. }
  873. /*****************************************************************************\
  874. FUNCTION: FormatEtcLoadFromStream
  875. DESCRIPTION:
  876. \*****************************************************************************/
  877. HRESULT FormatEtcLoadFromStream(IStream *pStm, FORMATETC * pFormatEtc)
  878. {
  879. HRESULT hr = E_INVALIDARG;
  880. if (pStm)
  881. {
  882. hr = pStm->Read(pFormatEtc, SIZEOF(*pFormatEtc), NULL);
  883. ASSERT(NULL == pFormatEtc->ptd); // We don't support this.
  884. if (SUCCEEDED(hr))
  885. {
  886. DWORD cbFormatNameSize;
  887. hr = pStm->Read(&cbFormatNameSize, SIZEOF(cbFormatNameSize), NULL);
  888. if (SUCCEEDED(hr))
  889. {
  890. WCHAR szFormatName[MAX_PATH];
  891. hr = pStm->Read(szFormatName, cbFormatNameSize, NULL);
  892. if (SUCCEEDED(hr))
  893. {
  894. pFormatEtc->cfFormat = (CLIPFORMAT)RegisterClipboardFormatW(szFormatName);
  895. }
  896. }
  897. }
  898. else
  899. hr = HRESULT_FROM_WIN32(GetLastError());
  900. }
  901. return hr;
  902. }
  903. typedef struct
  904. {
  905. DWORD dwVersion;
  906. DWORD dwExtraSize; // After this struct
  907. DWORD dwTymed; // What type of data is stored?
  908. BOOL fUnkForRelease; // Did we save the object after this?
  909. DWORD dwReserved1; //
  910. DWORD dwReserved2; //
  911. } STGMEDIUM_PERSISTSTRUCT;
  912. /*****************************************************************************\
  913. FUNCTION: StgMediumSaveToStream
  914. DESCRIPTION:
  915. \*****************************************************************************/
  916. HRESULT StgMediumSaveToStream(IStream *pStm, STGMEDIUM * pMedium)
  917. {
  918. HRESULT hr = E_INVALIDARG;
  919. if (pStm)
  920. {
  921. STGMEDIUM_PERSISTSTRUCT smps = {0};
  922. smps.dwVersion = 1;
  923. smps.dwTymed = pMedium->tymed;
  924. switch (pMedium->tymed)
  925. {
  926. case TYMED_HGLOBAL:
  927. {
  928. IStream * pstmHGlobal;
  929. hr = CreateStreamOnHGlobal(pMedium->hGlobal, FALSE, &pstmHGlobal);
  930. if (SUCCEEDED(hr))
  931. {
  932. STATSTG statStg;
  933. hr = pstmHGlobal->Stat(&statStg, STATFLAG_NONAME);
  934. if (SUCCEEDED(hr))
  935. {
  936. ASSERT(!statStg.cbSize.HighPart);
  937. smps.dwExtraSize = statStg.cbSize.LowPart;
  938. hr = pStm->Write(&smps, SIZEOF(smps), NULL);
  939. if (SUCCEEDED(hr))
  940. hr = pstmHGlobal->CopyTo(pStm, statStg.cbSize, NULL, NULL);
  941. }
  942. pstmHGlobal->Release();
  943. }
  944. }
  945. break;
  946. case TYMED_FILE:
  947. smps.dwExtraSize = ((lstrlenW(pMedium->lpszFileName) + 1) * sizeof(WCHAR));
  948. hr = pStm->Write(&smps, SIZEOF(smps), NULL);
  949. if (SUCCEEDED(hr))
  950. {
  951. hr = pStm->Write(pMedium->lpszFileName, smps.dwExtraSize, NULL);
  952. ASSERT(SUCCEEDED(hr));
  953. }
  954. break;
  955. case TYMED_GDI:
  956. case TYMED_MFPICT:
  957. case TYMED_ENHMF:
  958. case TYMED_ISTORAGE:
  959. case TYMED_ISTREAM:
  960. default:
  961. ASSERT(0); // What are you doing? Impl this if you need it.
  962. hr = E_NOTIMPL;
  963. break;
  964. }
  965. }
  966. return hr;
  967. }
  968. LPWSTR OLESTRAlloc(DWORD cchSize)
  969. {
  970. return (LPWSTR) new WCHAR [cchSize + 1];
  971. }
  972. /*****************************************************************************\
  973. FUNCTION: StgMediumLoadFromStream
  974. DESCRIPTION:
  975. \*****************************************************************************/
  976. HRESULT StgMediumLoadFromStream(IStream *pStm, STGMEDIUM * pMedium)
  977. {
  978. HRESULT hr = E_INVALIDARG;
  979. if (pStm && pMedium)
  980. {
  981. STGMEDIUM_PERSISTSTRUCT smps;
  982. pMedium->pUnkForRelease = NULL;
  983. hr = pStm->Read(&smps, SIZEOF(smps), NULL);
  984. if (SUCCEEDED(hr))
  985. {
  986. pMedium->tymed = smps.dwTymed;
  987. ASSERT(!pMedium->pUnkForRelease);
  988. switch (pMedium->tymed)
  989. {
  990. case TYMED_HGLOBAL:
  991. {
  992. IStream * pstmTemp;
  993. hr = CreateStreamOnHGlobal(NULL, FALSE, &pstmTemp);
  994. if (SUCCEEDED(hr))
  995. {
  996. ULARGE_INTEGER uli = {0};
  997. uli.LowPart = smps.dwExtraSize;
  998. hr = pStm->CopyTo(pstmTemp, uli, NULL, NULL);
  999. if (SUCCEEDED(hr))
  1000. {
  1001. hr = GetHGlobalFromStream(pstmTemp, &pMedium->hGlobal);
  1002. }
  1003. pstmTemp->Release();
  1004. }
  1005. }
  1006. break;
  1007. case TYMED_FILE:
  1008. pMedium->lpszFileName = OLESTRAlloc(smps.dwExtraSize / sizeof(WCHAR));
  1009. if (pMedium->lpszFileName)
  1010. hr = pStm->Read(pMedium->lpszFileName, smps.dwExtraSize, NULL);
  1011. else
  1012. hr = E_OUTOFMEMORY;
  1013. break;
  1014. case TYMED_GDI:
  1015. case TYMED_MFPICT:
  1016. case TYMED_ENHMF:
  1017. case TYMED_ISTORAGE:
  1018. case TYMED_ISTREAM:
  1019. default:
  1020. ASSERT(0); // What are you doing? Impl this if you need it.
  1021. // Some future version must have done the save, so skip the
  1022. // data so we don't leave unread data.
  1023. if (0 != smps.dwExtraSize)
  1024. {
  1025. LARGE_INTEGER li = {0};
  1026. li.LowPart = smps.dwExtraSize;
  1027. EVAL(SUCCEEDED(pStm->Seek(li, STREAM_SEEK_CUR, NULL)));
  1028. }
  1029. hr = E_NOTIMPL;
  1030. break;
  1031. }
  1032. }
  1033. }
  1034. return hr;
  1035. }
  1036. /*****************************************************************************\
  1037. FUNCTION: FORMATETC_STGMEDIUMSaveToStream
  1038. DESCRIPTION:
  1039. \*****************************************************************************/
  1040. HRESULT FORMATETC_STGMEDIUMSaveToStream(IStream *pStm, FORMATETC_STGMEDIUM * pfdops)
  1041. {
  1042. HRESULT hr = E_INVALIDARG;
  1043. if (pStm)
  1044. {
  1045. hr = FormatEtcSaveToStream(pStm, &pfdops->formatEtc);
  1046. if (SUCCEEDED(hr))
  1047. hr = StgMediumSaveToStream(pStm, &pfdops->medium);
  1048. }
  1049. return hr;
  1050. }
  1051. /*****************************************************************************\
  1052. FUNCTION: FORMATETC_STGMEDIUMLoadFromStream
  1053. DESCRIPTION:
  1054. \*****************************************************************************/
  1055. HRESULT FORMATETC_STGMEDIUMLoadFromStream(IStream *pStm, FORMATETC_STGMEDIUM * pfdops)
  1056. {
  1057. HRESULT hr = E_INVALIDARG;
  1058. if (pStm)
  1059. {
  1060. hr = FormatEtcLoadFromStream(pStm, &pfdops->formatEtc);
  1061. if (SUCCEEDED(hr))
  1062. hr = StgMediumLoadFromStream(pStm, &pfdops->medium);
  1063. }
  1064. return hr;
  1065. }
  1066. /////////////////////////////////
  1067. ////// IAsynchDataObject Impl
  1068. /////////////////////////////////
  1069. /*****************************************************************************\
  1070. FUNCTION: IAsyncOperation::GetAsyncMode
  1071. DESCRIPTION:
  1072. \*****************************************************************************/
  1073. HRESULT CFtpObj::GetAsyncMode(BOOL * pfIsOpAsync)
  1074. {
  1075. *pfIsOpAsync = TRUE;
  1076. return S_OK;
  1077. }
  1078. /*****************************************************************************\
  1079. FUNCTION: IAsyncOperation::StartOperation
  1080. DESCRIPTION:
  1081. \*****************************************************************************/
  1082. HRESULT CFtpObj::StartOperation(IBindCtx * pbcReserved)
  1083. {
  1084. ASSERT(!pbcReserved);
  1085. m_fDidAsynchStart = TRUE;
  1086. return S_OK;
  1087. }
  1088. /*****************************************************************************\
  1089. FUNCTION: IAsyncOperation::InOperation
  1090. DESCRIPTION:
  1091. \*****************************************************************************/
  1092. HRESULT CFtpObj::InOperation(BOOL * pfInAsyncOp)
  1093. {
  1094. if (m_fDidAsynchStart)
  1095. *pfInAsyncOp = TRUE;
  1096. else
  1097. *pfInAsyncOp = FALSE;
  1098. return S_OK;
  1099. }
  1100. /*****************************************************************************\
  1101. FUNCTION: IAsyncOperation::EndOperation
  1102. DESCRIPTION:
  1103. \*****************************************************************************/
  1104. HRESULT CFtpObj::EndOperation(HRESULT hResult, IBindCtx * pbcReserved, DWORD dwEffects)
  1105. {
  1106. if (SUCCEEDED(hResult) &&
  1107. (DROPEFFECT_MOVE == dwEffects))
  1108. {
  1109. CFtpPidlList * pPidlListNew = CreateRelativePidlList(m_pff, m_pflHfpl);
  1110. if (pPidlListNew)
  1111. {
  1112. Misc_DeleteHfpl(m_pff, GetDesktopWindow(), pPidlListNew);
  1113. pPidlListNew->Release();
  1114. }
  1115. }
  1116. m_fDidAsynchStart = FALSE;
  1117. return S_OK;
  1118. }
  1119. /////////////////////////////////
  1120. ////// IPersistStream Impl
  1121. /////////////////////////////////
  1122. /*****************************************************************************\
  1123. FUNCTION: IPersistStream::Load
  1124. DESCRIPTION:
  1125. See IPersistStream::Save() for the layout of the stream.
  1126. \*****************************************************************************/
  1127. HRESULT CFtpObj::Load(IStream *pStm)
  1128. {
  1129. HRESULT hr = E_INVALIDARG;
  1130. if (pStm)
  1131. {
  1132. FTPDATAOBJ_PERSISTSTRUCT fdoss;
  1133. DWORD dwNumPidls;
  1134. DWORD dwNumStgMedium;
  1135. hr = pStm->Read(&fdoss, SIZEOF(fdoss), NULL); // #1
  1136. // If we rev the version, read it now (fdoss.dwVersion)
  1137. if (SUCCEEDED(hr))
  1138. {
  1139. LPITEMIDLIST pidl = NULL; // ILLoadFromStream frees the param
  1140. ASSERT(!m_pff);
  1141. m_fFGDRendered = fdoss.fFGDRendered;
  1142. hr = ILLoadFromStream(pStm, &pidl); // #2
  1143. if (SUCCEEDED(hr))
  1144. {
  1145. hr = SHBindToIDList(pidl, NULL, IID_CFtpFolder, (void **)&m_pff);
  1146. if (SUCCEEDED(hr))
  1147. m_pfd = m_pff->GetFtpDir();
  1148. ASSERT(m_pfd);
  1149. ILFree(pidl);
  1150. }
  1151. }
  1152. if (SUCCEEDED(hr))
  1153. {
  1154. hr = pStm->Read(&dwNumPidls, SIZEOF(dwNumPidls), NULL); // #3
  1155. if (SUCCEEDED(hr))
  1156. hr = CFtpPidlList_Create(0, NULL, &m_pflHfpl);
  1157. }
  1158. if (SUCCEEDED(hr))
  1159. {
  1160. for (int nIndex = 0; (nIndex < (int)dwNumPidls) && SUCCEEDED(hr); nIndex++)
  1161. {
  1162. LPITEMIDLIST pidl = NULL; // ILLoadFromStream frees the param
  1163. hr = ILLoadFromStream(pStm, &pidl); // #4
  1164. if (SUCCEEDED(hr))
  1165. {
  1166. hr = m_pflHfpl->InsertSorted(pidl);
  1167. ILFree(pidl);
  1168. }
  1169. }
  1170. }
  1171. if (SUCCEEDED(hr))
  1172. hr = pStm->Read(&dwNumStgMedium, SIZEOF(dwNumStgMedium), NULL); // #5
  1173. if (SUCCEEDED(hr))
  1174. {
  1175. for (int nIndex = 0; (nIndex < (int)dwNumStgMedium) && SUCCEEDED(hr); nIndex++)
  1176. {
  1177. FORMATETC_STGMEDIUM fs;
  1178. hr = FORMATETC_STGMEDIUMLoadFromStream(pStm, &fs); // #6
  1179. if (SUCCEEDED(hr))
  1180. DSA_AppendItem(m_hdsaSetData, &fs);
  1181. }
  1182. }
  1183. if (SUCCEEDED(hr))
  1184. {
  1185. // We may be reading a version newer than us, so skip their data.
  1186. if (0 != fdoss.dwExtraSize)
  1187. {
  1188. LARGE_INTEGER li = {0};
  1189. li.LowPart = fdoss.dwExtraSize;
  1190. hr = pStm->Seek(li, STREAM_SEEK_CUR, NULL);
  1191. }
  1192. }
  1193. }
  1194. return hr;
  1195. }
  1196. /*****************************************************************************\
  1197. FUNCTION: IPersistStream::Save
  1198. DESCRIPTION:
  1199. The stream will be layed out in the following way:
  1200. Version 1:
  1201. 1. FTPDATAOBJ_PERSISTSTRUCT - Constant sized data.
  1202. <PidlList BEGIN>
  1203. 2. PIDL pidl - Pidl for m_pff. It will be a public pidl (fully qualified
  1204. from the shell root)
  1205. 3. DWORD dwNumPidls - Number of pidls coming.
  1206. 4. PIDL pidl(n) - Pidl in slot (n) of m_pflHfpl
  1207. <PidlList END>
  1208. 5. DWORD dwNumStgMedium - Number of FORMATETC_STGMEDIUMs coming
  1209. 6. FORMATETC_STGMEDIUM fmtstg(n) - dwNumStgMedium FORMATETC_STGMEDIUMs.
  1210. \*****************************************************************************/
  1211. HRESULT CFtpObj::Save(IStream *pStm, BOOL fClearDirty)
  1212. {
  1213. HRESULT hr = E_INVALIDARG;
  1214. if (pStm)
  1215. {
  1216. FTPDATAOBJ_PERSISTSTRUCT fdoss = {0};
  1217. DWORD dwNumPidls = m_pflHfpl->GetCount();
  1218. DWORD dwNumStgMedium = DSA_GetItemCount(m_hdsaSetData);
  1219. fdoss.dwVersion = 1;
  1220. fdoss.fFGDRendered = m_fFGDRendered;
  1221. hr = pStm->Write(&fdoss, SIZEOF(fdoss), NULL); // #1
  1222. if (SUCCEEDED(hr))
  1223. {
  1224. ASSERT(m_pff);
  1225. hr = ILSaveToStream(pStm, m_pff->GetPublicRootPidlReference()); // #2
  1226. }
  1227. if (SUCCEEDED(hr))
  1228. hr = pStm->Write(&dwNumPidls, SIZEOF(dwNumPidls), NULL); // #3
  1229. if (SUCCEEDED(hr))
  1230. {
  1231. for (int nIndex = 0; (nIndex < (int)dwNumPidls) && SUCCEEDED(hr); nIndex++)
  1232. {
  1233. LPITEMIDLIST pidlCur = m_pflHfpl->GetPidl(nIndex);
  1234. ASSERT(pidlCur);
  1235. hr = ILSaveToStream(pStm, pidlCur); // #4
  1236. }
  1237. }
  1238. if (SUCCEEDED(hr))
  1239. hr = pStm->Write(&dwNumStgMedium, SIZEOF(dwNumStgMedium), NULL); // #5
  1240. if (SUCCEEDED(hr))
  1241. {
  1242. for (int nIndex = 0; (nIndex < (int)dwNumStgMedium) && SUCCEEDED(hr); nIndex++)
  1243. {
  1244. FORMATETC_STGMEDIUM fs;
  1245. DSA_GetItem(m_hdsaSetData, nIndex, &fs);
  1246. hr = FORMATETC_STGMEDIUMSaveToStream(pStm, &fs); // #6
  1247. }
  1248. }
  1249. }
  1250. return hr;
  1251. }
  1252. #define MAX_STREAM_SIZE (500 * 1024) // 500k
  1253. /*****************************************************************************\
  1254. FUNCTION: IPersistStream::GetSizeMax
  1255. DESCRIPTION:
  1256. Now this is tough. I can't calculate the real value because I don't know
  1257. how big the hglobals are going to be for the user provided data. I will
  1258. assume everything fits in
  1259. \*****************************************************************************/
  1260. HRESULT CFtpObj::GetSizeMax(ULARGE_INTEGER * pcbSize)
  1261. {
  1262. if (pcbSize)
  1263. {
  1264. pcbSize->HighPart = 0;
  1265. pcbSize->LowPart = MAX_STREAM_SIZE;
  1266. }
  1267. return E_NOTIMPL;
  1268. }
  1269. /////////////////////////////////
  1270. ////// IDataObject Impl
  1271. /////////////////////////////////
  1272. /*****************************************************************************\
  1273. FUNCTION: IDataObject::GetData
  1274. DESCRIPTION:
  1275. Render the data in the requested format and put it into the
  1276. STGMEDIUM structure.
  1277. \*****************************************************************************/
  1278. HRESULT CFtpObj::GetData(LPFORMATETC pfe, LPSTGMEDIUM pstg)
  1279. {
  1280. int ife;
  1281. HRESULT hr;
  1282. hr = _FindDataForGet(pfe, &ife);
  1283. if (SUCCEEDED(hr))
  1284. {
  1285. if (ife == DROP_FCont)
  1286. hr = _RenderFileContents(pfe, pstg);
  1287. else
  1288. {
  1289. hr = _ForceRender(ife);
  1290. if (SUCCEEDED(hr)) // May not succeed for security reasons.
  1291. {
  1292. ASSERT(m_stgCache[ife].hGlobal);
  1293. // It's possible to use the hacking STGMEDIUM.pUnkForRelease to give away
  1294. // pointers to our data, but we then need massive amounts of code to babysite
  1295. // the lifetime of those pointers. This becomes more work when ::SetData() can
  1296. // replace that data, so we just take the hit of the memcpy for less code.
  1297. hr = CopyStgMediumWrap(&m_stgCache[ife], pstg);
  1298. ASSERT(SUCCEEDED(hr));
  1299. ASSERT(NULL == pstg->pUnkForRelease);
  1300. //TraceMsg(TF_FTPDRAGDROP, "CFtpObj::GetData() pstg->hGlobal=%#08lx. pstg->pUnkForRelease=%#08lx.", pstg->hGlobal, pstg->pUnkForRelease);
  1301. }
  1302. }
  1303. TraceMsgWithFormat(TF_FTPDRAGDROP, "CFtpObj::GetData()", pfe, "Format in static list", hr);
  1304. }
  1305. else
  1306. {
  1307. int nIndex = _FindExtraDataIndex(pfe);
  1308. if (-1 == nIndex)
  1309. hr = E_FAIL;
  1310. else
  1311. {
  1312. FORMATETC_STGMEDIUM fs;
  1313. DSA_GetItem(m_hdsaSetData, nIndex, &fs);
  1314. hr = CopyStgMediumWrap(&fs.medium, pstg);
  1315. }
  1316. TraceMsgWithFormat(TF_FTPDRAGDROP, "CFtpObj::GetData()", pfe, "Looking in dyn list", hr);
  1317. }
  1318. return hr;
  1319. }
  1320. /*****************************************************************************\
  1321. IDataObject::GetDataHere
  1322. Render the data in the requested format and put it into the
  1323. object provided by the caller.
  1324. \*****************************************************************************/
  1325. HRESULT CFtpObj::GetDataHere(FORMATETC *pfe, STGMEDIUM *pstg)
  1326. {
  1327. TraceMsg(TF_FTPDRAGDROP, "CFtpObj::GetDataHere() pfe->cfFormat=%d.", pfe->cfFormat);
  1328. return E_NOTIMPL;
  1329. }
  1330. /*****************************************************************************\
  1331. FUNCTION: IDataObject::QueryGetData
  1332. DESCRIPTION:
  1333. Indicate whether we could provide data in the requested format.
  1334. \*****************************************************************************/
  1335. HRESULT CFtpObj::QueryGetData(FORMATETC *pfe)
  1336. {
  1337. int ife;
  1338. HRESULT hr = _FindDataForGet(pfe, &ife);
  1339. if (FAILED(hr))
  1340. {
  1341. // If it wasn't one of the types we offer, see if it was given to us via
  1342. // IDataObject::SetData().
  1343. int nIndex = _FindExtraDataIndex(pfe);
  1344. if (-1 != nIndex)
  1345. hr = S_OK;
  1346. }
  1347. TraceMsgWithFormat(TF_FTPDRAGDROP, "CFtpObj::QueryGetData()", pfe, "", hr);
  1348. return hr;
  1349. }
  1350. /*****************************************************************************\
  1351. FUNCTION: IDataObject::GetCanonicalFormatEtc
  1352. DESCRIPTION:
  1353. Our data are not sensitive to device-specific renderings,
  1354. so we do what the book tells us to do.
  1355. Or we *try* to do what the book tells us to do.
  1356. OLE random documentation of the day:
  1357. IDataObject::GetCanonicalFormatEtc.
  1358. Turns out that the man page contradicts itself within sentences:
  1359. DATA_S_SAMEFORMATETC - The FORMATETC structures are the same
  1360. and NULL is returned in pfeOut.
  1361. If the data object never provides device-specific renderings,
  1362. the implementation of IDataObject::GetCanonicalFormatEtc
  1363. simply copies the input FORMATETC to the output FORMATETC,
  1364. stores a null in the ptd field, and returns DATA_S_SAMEFORMATETC.
  1365. And it turns out that the shell doesn't do *either* of these things.
  1366. It just returns DATA_S_SAMEFORMATETC and doesn't touch pfeOut.
  1367. The book is even more confused. Under pfeOut, it says
  1368. The value is NULL if the method returns DATA_S_SAMEFORMATETC.
  1369. This makes no sense. The caller provides the value of pfeOut.
  1370. How can the caller possibly know that the method is going to return
  1371. DATA_S_SAMEFORMATETC before it calls it? If you expect the
  1372. method to write "pfeOut = 0" before returning, you're nuts. That
  1373. communicates nothing to the caller.
  1374. I'll just do what the shell does.
  1375. \*****************************************************************************/
  1376. HRESULT CFtpObj::GetCanonicalFormatEtc(FORMATETC *pfeIn, FORMATETC *pfeOut)
  1377. {
  1378. return DATA_S_SAMEFORMATETC;
  1379. }
  1380. /*****************************************************************************\
  1381. FUNCTION: IDataObject::SetData
  1382. DESCRIPTION:
  1383. We let people change TYMED_HGLOBAL gizmos, but nothing else.
  1384. We need to do a careful two-step when replacing the HGLOBAL.
  1385. If the user gave us a plain HGLOBAL without a pUnkForRelease,
  1386. we need to invent our own pUnkForRelease to track it. But we
  1387. don't want to release the old STGMEDIUM until we're sure we
  1388. can accept the new one.
  1389. fRelease == 0 makes life doubly interesting, because we also
  1390. have to clone the HGLOBAL (and remember to free the clone on the
  1391. error path).
  1392. _SOMEDAY_/TODO -- Need to support PerformedDropEffect so we can
  1393. clean up stuff on a cut/paste.
  1394. \*****************************************************************************/
  1395. HRESULT CFtpObj::SetData(FORMATETC *pfe, STGMEDIUM *pstg, BOOL fRelease)
  1396. {
  1397. int ife;
  1398. HRESULT hr;
  1399. hr = _FindData(pfe, &ife);
  1400. if (SUCCEEDED(hr))
  1401. {
  1402. if (ife == DROP_FCont)
  1403. {
  1404. TraceMsg(TF_FTPDRAGDROP, "CFtpObj::SetData(FORMATETC.cfFormat=%d) ife == DROP_FCont", pfe->cfFormat);
  1405. hr = DV_E_FORMATETC;
  1406. }
  1407. else
  1408. {
  1409. ASSERT(g_dropTypes[ife].tymed == TYMED_HGLOBAL);
  1410. ASSERT(pstg->tymed == TYMED_HGLOBAL);
  1411. if (pstg->hGlobal)
  1412. {
  1413. STGMEDIUM stg = {0};
  1414. hr = CopyStgMediumWrap(pstg, &stg);
  1415. if (SUCCEEDED(hr))
  1416. {
  1417. ReleaseStgMedium(&m_stgCache[ife]);
  1418. m_stgCache[ife] = stg;
  1419. }
  1420. }
  1421. else
  1422. { // Tried to SetData a _DelayRender
  1423. hr = DV_E_STGMEDIUM;
  1424. }
  1425. }
  1426. TraceMsgWithFormat(TF_FTPDRAGDROP, "CFtpObj::SetData()", pfe, "in static list", hr);
  1427. }
  1428. else
  1429. {
  1430. hr = _SetExtraData(pfe, pstg, fRelease);
  1431. TraceMsgWithFormat(TF_FTPDRAGDROP, "CFtpObj::SetData()", pfe, "in dyn list", hr);
  1432. }
  1433. return hr;
  1434. }
  1435. /*****************************************************************************\
  1436. FUNCTION: IDataObject::EnumFormatEtc
  1437. DESCRIPTION:
  1438. _UNDOCUMENTED_: If you drag something from a DefView, it will
  1439. check the data object to see if it has a hida. If so, then it
  1440. will cook up a CFSTR_SHELLIDLISTOFFSET *for you* and SetData
  1441. the information into the data object. So in order to get
  1442. position-aware drag/drop working, you must allow DefView to change
  1443. your CFSTR_SHELLIDLISTOFFSET.
  1444. We allow all FORMATETCs to be modified except for FileContents.
  1445. \*****************************************************************************/
  1446. HRESULT CFtpObj::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenum)
  1447. {
  1448. HRESULT hres;
  1449. switch (dwDirection)
  1450. {
  1451. case DATADIR_GET:
  1452. hres = CFtpEfe_Create(DROP_OFFERMAX - DROP_FCont, &g_dropTypes[DROP_FCont],
  1453. &m_stgCache[DROP_FCont], this, ppenum);
  1454. TraceMsg(TF_FTPDRAGDROP, "CFtpObj::EnumFormatEtc(DATADIR_GET) CFtpEfe_Create() returned hres=%#08lx", hres);
  1455. break;
  1456. case DATADIR_SET:
  1457. hres = CFtpEfe_Create(DROP_OFFERMAX - DROP_OFFERMIN, &g_dropTypes[DROP_OFFERMIN],
  1458. &m_stgCache[DROP_OFFERMIN], NULL, ppenum);
  1459. TraceMsg(TF_FTPDRAGDROP, "CFtpObj::EnumFormatEtc(DATADIR_SET) CFtpEfe_Create() returned hres=%#08lx", hres);
  1460. break;
  1461. default:
  1462. ASSERT(0);
  1463. hres = E_NOTIMPL;
  1464. break;
  1465. }
  1466. return hres;
  1467. }
  1468. /*****************************************************************************\
  1469. FUNCTION: IDataObject::DAdvise
  1470. DESCRIPTION:
  1471. \*****************************************************************************/
  1472. HRESULT CFtpObj::DAdvise(FORMATETC *pfe, DWORD advfl, IAdviseSink *padv, DWORD *pdwConnection)
  1473. {
  1474. return OLE_E_ADVISENOTSUPPORTED;
  1475. }
  1476. /*****************************************************************************\
  1477. FUNCTION: IDataObject::DUnadvise
  1478. DESCRIPTION:
  1479. \*****************************************************************************/
  1480. HRESULT CFtpObj::DUnadvise(DWORD dwConnection)
  1481. {
  1482. return OLE_E_ADVISENOTSUPPORTED;
  1483. }
  1484. /*****************************************************************************\
  1485. FUNCTION: IDataObject::EnumDAdvise
  1486. DESCRIPTION:
  1487. \*****************************************************************************/
  1488. HRESULT CFtpObj::EnumDAdvise(IEnumSTATDATA **ppeadv)
  1489. {
  1490. return OLE_E_ADVISENOTSUPPORTED;
  1491. }
  1492. /*****************************************************************************\
  1493. FUNCTION: CFtpObj_Create
  1494. DESCRIPTION:
  1495. \*****************************************************************************/
  1496. HRESULT CFtpObj_Create(CFtpFolder * pff, CFtpPidlList * pflHfpl, REFIID riid, LPVOID * ppvObj)
  1497. {
  1498. HRESULT hres;
  1499. CFtpObj * pfo;
  1500. *ppvObj = NULL;
  1501. hres = CFtpObj_Create(pff, pflHfpl, &pfo);
  1502. if (SUCCEEDED(hres))
  1503. {
  1504. pfo->QueryInterface(riid, ppvObj);
  1505. pfo->Release();
  1506. }
  1507. return hres;
  1508. }
  1509. /*****************************************************************************\
  1510. FUNCTION: CFtpObj_Create
  1511. DESCRIPTION:
  1512. \*****************************************************************************/
  1513. HRESULT CFtpObj_Create(CFtpFolder * pff, CFtpPidlList * pflHfpl, CFtpObj ** ppfo)
  1514. {
  1515. HRESULT hres = S_OK;
  1516. if (EVAL(pflHfpl->GetCount()))
  1517. {
  1518. *ppfo = new CFtpObj();
  1519. if (*ppfo)
  1520. {
  1521. CFtpObj * pfo = *ppfo;
  1522. pfo->m_pfd = pff->GetFtpDir();
  1523. if (pfo->m_pfd)
  1524. {
  1525. pfo->m_pff = pff;
  1526. if (pff)
  1527. pff->AddRef();
  1528. IUnknown_Set(&pfo->m_pflHfpl, pflHfpl);
  1529. if (pfo->m_pflHfpl->GetCount() == 1)
  1530. {
  1531. pfo->m_stgCache[DROP_URL].tymed = TYMED_HGLOBAL;
  1532. }
  1533. }
  1534. else
  1535. {
  1536. hres = E_FAIL;
  1537. (*ppfo)->Release();
  1538. *ppfo = NULL;
  1539. }
  1540. }
  1541. else
  1542. hres = E_OUTOFMEMORY;
  1543. }
  1544. else
  1545. {
  1546. *ppfo = NULL;
  1547. hres = E_INVALIDARG; /* Trying to get UI object of nil? */
  1548. }
  1549. return hres;
  1550. }
  1551. /*****************************************************************************\
  1552. FUNCTION: CFtpObj_Create
  1553. DESCRIPTION:
  1554. This will be called by the Class Factory when the IDataObject gets
  1555. persisted and then wants to be recreated in a new process. (Happens
  1556. after the original thread/process calls OleFlushClipboard.
  1557. \*****************************************************************************/
  1558. HRESULT CFtpObj_Create(REFIID riid, void ** ppvObj)
  1559. {
  1560. HRESULT hr = E_OUTOFMEMORY;
  1561. CFtpObj * pfo = new CFtpObj();
  1562. *ppvObj = NULL;
  1563. if (pfo)
  1564. {
  1565. hr = pfo->QueryInterface(riid, ppvObj);
  1566. pfo->Release();
  1567. }
  1568. return hr;
  1569. }
  1570. #define SETDATA_GROWSIZE 3
  1571. /****************************************************\
  1572. Constructor
  1573. \****************************************************/
  1574. CFtpObj::CFtpObj() : m_cRef(1)
  1575. {
  1576. DllAddRef();
  1577. // This needs to be allocated in Zero Inited Memory.
  1578. // Assert that all Member Variables are inited to Zero.
  1579. ASSERT(!m_pff);
  1580. ASSERT(!m_pfd);
  1581. ASSERT(!m_pflHfpl);
  1582. ASSERT(!m_fDidAsynchStart);
  1583. // NT #245306: If the user drags files from an FTP window (Thread 1)
  1584. // to a shell window (Thread 2), the shell window will do
  1585. // the drop on a background thread (thread 3). Since the
  1586. // UI thread is no longer blocked, the user can now close
  1587. // the window. The problem is that OLE is using Thread 2
  1588. // for marshalling. In order to solve this problem, we
  1589. // ref count the thread for items that rely on it.
  1590. // This include FTP, normal Download, and other things
  1591. // in the future.
  1592. // SHIncrementThreadModelessCount();
  1593. m_nStartIndex = -1; // -1 means we don't know the start.
  1594. m_fFGDRendered = FALSE;
  1595. m_fCheckSecurity = TRUE; // We need to keep checking.
  1596. m_hdsaSetData = DSA_Create(sizeof(FORMATETC_STGMEDIUM), SETDATA_GROWSIZE);
  1597. for (int nIndex = 0; nIndex < ARRAYSIZE(c_stgInit); nIndex++)
  1598. {
  1599. ASSERT(nIndex < ARRAYSIZE(m_stgCache));
  1600. m_stgCache[nIndex] = c_stgInit[nIndex];
  1601. }
  1602. _RefThread();
  1603. // The receiver may use us in the background, so make sure that our thread
  1604. // doesn't go away.
  1605. LEAK_ADDREF(LEAK_CFtpObj);
  1606. }
  1607. /****************************************************\
  1608. Destructor
  1609. \****************************************************/
  1610. CFtpObj::~CFtpObj()
  1611. {
  1612. int ife;
  1613. _CloseProgressDialog();
  1614. for (ife = DROP_OFFERMIN; ife < DROP_OFFERMAX; ife++)
  1615. {
  1616. ReleaseStgMedium(&m_stgCache[ife]);
  1617. }
  1618. if (m_ppd)
  1619. m_ppd->StopProgressDialog();
  1620. IUnknown_Set((IUnknown **)&m_ppd, NULL);
  1621. IUnknown_Set(&m_pff, NULL);
  1622. IUnknown_Set(&m_pfd, NULL);
  1623. IUnknown_Set(&m_pflHfpl, NULL);
  1624. DSA_DestroyCallback(m_hdsaSetData, &_DSA_FreeCB, NULL);
  1625. // NT #245306: If the user drags files from an FTP window (Thread 1)
  1626. // to a shell window (Thread 2), the shell window will do
  1627. // the drop on a background thread (thread 3). Since the
  1628. // UI thread is no longer blocked, the user can now close
  1629. // the window. The problem is that OLE is using Thread 2
  1630. // for marshalling. In order to solve this problem, we
  1631. // ref count the thread for items that rely on it.
  1632. // This include FTP, normal Download, and other things
  1633. // in the future.
  1634. ATOMICRELEASE(m_punkThreadRef);
  1635. DllRelease();
  1636. LEAK_DELREF(LEAK_CFtpObj);
  1637. }
  1638. //===========================
  1639. // *** IUnknown Interface ***
  1640. //===========================
  1641. ULONG CFtpObj::AddRef()
  1642. {
  1643. m_cRef++;
  1644. return m_cRef;
  1645. }
  1646. ULONG CFtpObj::Release()
  1647. {
  1648. ASSERT(m_cRef > 0);
  1649. m_cRef--;
  1650. if (m_cRef > 0)
  1651. return m_cRef;
  1652. delete this;
  1653. return 0;
  1654. }
  1655. HRESULT CFtpObj::QueryInterface(REFIID riid, void **ppvObj)
  1656. {
  1657. static const QITAB qit[] = {
  1658. QITABENT(CFtpObj, IDataObject),
  1659. QITABENT(CFtpObj, IInternetSecurityMgrSite),
  1660. QITABENT(CFtpObj, IPersist),
  1661. QITABENT(CFtpObj, IPersistStream),
  1662. QITABENT(CFtpObj, IAsyncOperation),
  1663. { 0 },
  1664. };
  1665. return QISearch(this, qit, riid, ppvObj);
  1666. }