ftpobj.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 67k
Category:

Windows Kernel

Development Platform:

Visual C++

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