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

Windows Kernel

Development Platform:

Visual C++

  1. /*****************************************************************************
  2.  *
  3.  *    ftpstm.cpp - IStream interface
  4.  *
  5.  *****************************************************************************/
  6. #include "priv.h"
  7. #include "ftpstm.h"
  8. #include "ftpurl.h"
  9. #define     UPDATE_PROGRESS_EVERY       (10*1024)       // Update progress every 10k
  10. /*****************************************************************************
  11.  *    CFtpStm::ReadOrWrite
  12.  *****************************************************************************/
  13. HRESULT CFtpStm::ReadOrWrite(LPVOID pv, ULONG cb, ULONG * pcb, DWORD dwAccess, STMIO io, HRESULT hresFail)
  14. {
  15.     HRESULT hr = STG_E_ACCESSDENIED;
  16.     if (EVAL(m_dwAccessType & dwAccess))
  17.     {
  18.         ULONG cbOut;
  19.         if (!pcb)
  20.             pcb = &cbOut;
  21.         hr = io(m_hint, TRUE, pv, cb, pcb);
  22.         if (SUCCEEDED(hr) && m_ppd)
  23.         {
  24.             m_uliComplete.QuadPart += cb;
  25.             m_ulBytesSinceProgressUpdate += cb;
  26.             if (m_ulBytesSinceProgressUpdate > UPDATE_PROGRESS_EVERY)
  27.             {
  28.                 m_ulBytesSinceProgressUpdate = 0;
  29.                 EVAL(SUCCEEDED(m_ppd->SetProgress64(m_uliComplete.QuadPart, m_uliTotal.QuadPart)));
  30.             }
  31.             if (TRUE == m_ppd->HasUserCancelled())
  32.                 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  33.         }
  34.     }
  35.     return hr;
  36. }
  37. //===========================
  38. // *** IStream Interface ***
  39. //===========================
  40. /*****************************************************************************
  41.  *    IStream::Read
  42.  *****************************************************************************/
  43. HRESULT CFtpStm::Read(LPVOID pv, ULONG cb, PULONG pcb)
  44. {
  45.     return ReadOrWrite(pv, cb, pcb, GENERIC_READ, InternetReadFileWrap, S_FALSE);
  46. }
  47. /*****************************************************************************
  48.  *    IStream::Write
  49.  *****************************************************************************/
  50. HRESULT CFtpStm::Write(LPCVOID pv, ULONG cb, PULONG pcb)
  51. {
  52.     return ReadOrWrite((LPVOID)pv, cb, pcb, GENERIC_WRITE, (STMIO) InternetWriteFileWrap, STG_E_WRITEFAULT);
  53. }
  54. /*****************************************************************************
  55.  *    IStream::CopyTo
  56.  *
  57.  *    _UNOBVIOUS_:  Implementing CopyTo is mandatory for drag/drop to work.
  58.  *****************************************************************************/
  59. #define SIZE_STREAM_COPY_BUFFER     (1024*16)        // 16k is the perfect size for 
  60. HRESULT CFtpStm::CopyTo(IStream * pstmDest, ULARGE_INTEGER cbToCopy, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  61. {
  62.     HRESULT hr = E_FAIL;
  63.     IStream * pstmSrc;
  64.     if (EVAL(SUCCEEDED(hr = QueryInterface(IID_IStream, (LPVOID *) &pstmSrc))))
  65.     {
  66.         ULARGE_INTEGER uliTotalIn;
  67.         ULARGE_INTEGER uliTotalOut;
  68.         uliTotalIn.QuadPart = uliTotalOut.QuadPart = 0;
  69.         BYTE buffer[SIZE_STREAM_COPY_BUFFER];
  70.         for (;;)
  71.         {
  72.             // Very unusual loop control
  73.             ULONG cbIn = 0;        // In case pstmSrc forgets to
  74.             //    No matter how you write this, the compiler emits horrid code.
  75.             ULONG cb = (ULONG)min(SIZE_STREAM_COPY_BUFFER, cbToCopy.LowPart);
  76.             hr = pstmSrc->Read(buffer, cb, &cbIn);
  77.             uliTotalIn.QuadPart += cbIn;
  78.             if (SUCCEEDED(hr) && cbIn)
  79.             {
  80.                 ULARGE_INTEGER uliOut;    // In case pstmDest forgets to
  81.                 uliOut.QuadPart = 0;
  82.                 hr = pstmDest->Write(buffer, cbIn, &(uliOut.LowPart));
  83.                 uliTotalOut.QuadPart += uliOut.QuadPart;
  84.                 if (EVAL(SUCCEEDED(hr) && uliOut.QuadPart))
  85.                 {
  86.                     // Onward
  87.                 }
  88.                 else
  89.                 {
  90.                     break;        // Error or medium full
  91.                 }
  92.             }
  93.             else
  94.             {
  95.                 break;            // Error or EOF reached
  96.             }
  97.         }
  98.         if (pcbRead)
  99.             pcbRead->QuadPart = uliTotalIn.QuadPart;
  100.         if (pcbWritten)
  101.             pcbWritten->QuadPart = uliTotalOut.QuadPart;
  102.         pstmSrc->Release();
  103.     }
  104.     return hr;
  105. }
  106. /*****************************************************************************
  107.  *    IStream::Commit
  108.  *
  109.  *    NOTE: WinINet doesn't really implement this, so I just do my best
  110.  *****************************************************************************/
  111. HRESULT CFtpStm::Commit(DWORD grfCommitFlags)
  112. {
  113.     return S_OK;
  114. }
  115. /*****************************************************************************
  116.  *    IStream::LockRegion
  117.  *
  118.  *    You can't lock an ftp stream.
  119.  *****************************************************************************/
  120. HRESULT CFtpStm::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  121. {
  122.     return STG_E_INVALIDFUNCTION;
  123. }
  124. /*****************************************************************************
  125.  *    IStream::UnlockRegion
  126.  *
  127.  *    You can't unlock an ftp stream because you can't lock one...
  128.  *****************************************************************************/
  129. HRESULT CFtpStm::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
  130. {
  131.     return STG_E_INVALIDFUNCTION;
  132. }
  133. /*****************************************************************************
  134.  *    IStream::Stat
  135.  *
  136.  *    We fill in what we can.
  137.  *
  138.  *    As the pwcsName, we put the URL that the stream represents, and
  139.  *    install ourselves as the clsid.
  140.  *****************************************************************************/
  141. HRESULT CFtpStm::Stat(STATSTG *pstat, DWORD grfStatFlag)
  142. {
  143.     HRESULT hr;
  144.     ZeroMemory(pstat, sizeof(*pstat));
  145.     pstat->type = STGTY_STREAM;
  146.     pstat->mtime = FtpPidl_GetFileTime(ILFindLastID(m_pidl));
  147.     pstat->cbSize.QuadPart = FtpItemID_GetFileSize(m_pidl);
  148.     pstat->grfMode |= STGM_SHARE_EXCLUSIVE | STGM_DIRECT;
  149.     if (m_dwAccessType & GENERIC_READ)
  150.         pstat->grfMode |= STGM_READ;
  151.     if (m_dwAccessType & GENERIC_WRITE)
  152.         pstat->grfMode |= STGM_WRITE;
  153.     if (grfStatFlag & STATFLAG_NONAME)
  154.         hr = S_OK;
  155.     else
  156.     {
  157.         DWORD cchSize = (lstrlenW(FtpPidl_GetLastFileDisplayName(m_pidl)) + 1);
  158.         pstat->pwcsName = (LPWSTR) SHAlloc(cchSize * sizeof(WCHAR));
  159.         if (pstat->pwcsName)
  160.         {
  161.             StrCpyNW(pstat->pwcsName, FtpPidl_GetLastFileDisplayName(m_pidl), cchSize);
  162.             hr = S_OK;
  163.         }
  164.         else
  165.             hr = STG_E_INSUFFICIENTMEMORY;    // N.B., not E_OUTOFMEMORY
  166.     }
  167.     return hr;
  168. }
  169. /*****************************************************************************
  170.     FUNCTION:   CFtpStm_Create
  171.     DESCRIPTION:
  172.         The caller will display errors, so don't do that here.
  173. *****************************************************************************/
  174. HRESULT CFtpStm_Create(CFtpDir * pfd, LPCITEMIDLIST pidl, DWORD dwAccess, IStream ** ppstream, ULARGE_INTEGER uliComplete, ULARGE_INTEGER uliTotal, IProgressDialog * ppd, BOOL fClosePrgDlg)
  175. {
  176.     CFtpStm * pfstm = new CFtpStm();
  177.     HRESULT hr = E_OUTOFMEMORY;
  178.     DWORD dwError = ERROR_SUCCESS;
  179.     *ppstream = NULL;
  180.     if (pfstm)
  181.     {
  182.         Pidl_Set(&(pfstm->m_pidl), pidl);
  183.         ASSERT(pfstm->m_pidl);
  184.         pfstm->m_dwAccessType = dwAccess;
  185.         IUnknown_Set(&pfstm->m_pfd, pfd);
  186.         IUnknown_Set((IUnknown **)&pfstm->m_ppd, (IUnknown *)ppd);
  187.         pfstm->m_uliComplete = uliComplete;
  188.         pfstm->m_uliTotal = uliTotal;
  189.         pfstm->m_fClosePrgDlg = fClosePrgDlg;
  190.         //      GetHint() is going to want to spew status into the Status Bar
  191.         //   But how do we get the hwnd?  This is an architectural question that
  192.         //   we need to solve for all Shell Extensions.  The answer is to not use
  193.         //   the progress bar in the status bar but a Progress Dialog.  But it's
  194.         //   the responsibility of the caller to do that.
  195.         HWND hwnd = NULL;
  196.         hr = pfd->GetHint(hwnd, NULL, &pfstm->m_hintSession, NULL, NULL);
  197.         if (EVAL(SUCCEEDED(hr)))
  198.         {
  199.             LPITEMIDLIST pidlVirtualRoot;
  200.             hr = pfd->GetFtpSite()->GetVirtualRoot(&pidlVirtualRoot);
  201.             if (EVAL(SUCCEEDED(hr)))
  202.             {
  203.                 LPITEMIDLIST pidlOriginalFtpPath;
  204.                 CWireEncoding * pwe = pfd->GetFtpSite()->GetCWireEncoding();
  205.                 hr = FtpGetCurrentDirectoryPidlWrap(pfstm->m_hintSession, TRUE, pwe, &pidlOriginalFtpPath);
  206.                 if (SUCCEEDED(hr))
  207.                 {
  208.                     LPITEMIDLIST pidlWithVirtualRoot;
  209.                     hr = FtpPidl_InsertVirtualRoot(pidlVirtualRoot, pidl, &pidlWithVirtualRoot);
  210.                     if (SUCCEEDED(hr))
  211.                     {
  212.                         hr = FtpSetCurrentDirectoryPidlWrap(pfstm->m_hintSession, TRUE, pidlWithVirtualRoot, TRUE, TRUE);
  213.                         if (SUCCEEDED(hr))
  214.                         {
  215.                             DWORD dwDownloadType = FtpPidl_GetDownloadType(pidl);
  216.                             // PERF: I bet we would be faster if we delayed the open until
  217.                             //       the first ::Read(), ::Write(), or ::CopyToStream() call.
  218.                             Pidl_Set(&pfstm->m_pidlOriginalFtpPath, pidlOriginalFtpPath);
  219.                             hr = FtpOpenFileWrap(pfstm->m_hintSession, TRUE, FtpPidl_GetLastItemWireName(pidl), pfstm->m_dwAccessType, dwDownloadType, 0, &pfstm->m_hint);
  220.                         }
  221.                         ILFree(pidlWithVirtualRoot);
  222.                     }
  223.                     ILFree(pidlOriginalFtpPath);
  224.                 }
  225.                 ILFree(pidlVirtualRoot);
  226.             }
  227.         }
  228.         if (SUCCEEDED(hr))
  229.             hr = pfstm->QueryInterface(IID_IStream, (LPVOID *) ppstream);
  230.         pfstm->Release();
  231.     }
  232.     return hr;
  233. }
  234. /****************************************************
  235.     Constructor
  236. ****************************************************/
  237. CFtpStm::CFtpStm() : m_cRef(1)
  238. {
  239.     DllAddRef();
  240.     // This needs to be allocated in Zero Inited Memory.
  241.     // Assert that all Member Variables are inited to Zero.
  242.     ASSERT(!m_hint);
  243.     ASSERT(!m_dwAccessType);
  244.     ASSERT(!m_pfd);
  245.     ASSERT(!m_hintSession);
  246.     ASSERT(!m_pidl);
  247.     ASSERT(!m_ppd);
  248.     LEAK_ADDREF(LEAK_CFtpStm);
  249. }
  250. /****************************************************
  251.     Destructor
  252. ****************************************************/
  253. CFtpStm::~CFtpStm()
  254. {
  255.     if (m_hint)
  256.     {
  257.         InternetCloseHandle(m_hint);
  258.     }
  259.     // This COM object works like this:
  260.     // 1. The constructor opens a handle to the server and
  261.     //    Changes directory into the dir we are going to work in.
  262.     // 2. The original dir is saved (m_pidlOriginalFtpPath) in order to be restored later
  263.     //    because we cache the internet handle for perf and to keep our place on the server.
  264.     // 3. The caller of this COM object can then copy data.
  265.     // 4. We then Change directory to the original dir here before we close the internet handle.s
  266.     if (m_pidlOriginalFtpPath && EVAL(m_hintSession))
  267.     {
  268.         EVAL(SUCCEEDED(FtpSetCurrentDirectoryPidlWrap(m_hintSession, TRUE, m_pidlOriginalFtpPath, TRUE, TRUE)));
  269.         Pidl_Set(&m_pidlOriginalFtpPath, NULL);
  270.     }
  271.     if (m_hintSession)
  272.         m_pfd->ReleaseHint(m_hintSession);
  273.     ATOMICRELEASE(m_pfd);
  274.     if (m_ppd && m_fClosePrgDlg)
  275.         EVAL(SUCCEEDED(m_ppd->StopProgressDialog()));
  276.     ATOMICRELEASE(m_ppd);
  277.     ILFree(m_pidl);
  278.     ILFree(m_pidlOriginalFtpPath);
  279.     DllRelease();
  280.     LEAK_DELREF(LEAK_CFtpStm);
  281. }
  282. //===========================
  283. // *** IUnknown Interface ***
  284. //===========================
  285. ULONG CFtpStm::AddRef()
  286. {
  287.     m_cRef++;
  288.     return m_cRef;
  289. }
  290. ULONG CFtpStm::Release()
  291. {
  292.     ASSERT(m_cRef > 0);
  293.     m_cRef--;
  294.     if (m_cRef > 0)
  295.         return m_cRef;
  296.     delete this;
  297.     return 0;
  298. }
  299. HRESULT CFtpStm::QueryInterface(REFIID riid, void **ppvObj)
  300. {
  301.     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream))
  302.     {
  303.         *ppvObj = SAFECAST(this, IStream*);
  304.     }
  305.     else
  306.     {
  307.         TraceMsg(TF_FTPQI, "CFtpStm::QueryInterface() failed.");
  308.         *ppvObj = NULL;
  309.         return E_NOINTERFACE;
  310.     }
  311.     AddRef();
  312.     return S_OK;
  313. }