⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpcm.cpp

📁 很好用的ftp源码
💻 CPP
📖 第 1 页 / 共 5 页
字号:
                    switch (FtpConfirmReplaceDialog(hwnd, &wfdSrc, &wfdDest, nObjs, pff))
                    {
                    case IDC_REPLACE_YESTOALL:
                        *pOps = opsYesToAll;
                        // FALLTHROUGH

                    case IDC_REPLACE_YES:
                        hr = S_OK;
                        break;

                    case IDC_REPLACE_NOTOALL:
                        *pOps = opsNoToAll;
                        // FALLTHROUGH

                    case IDC_REPLACE_NO:
                        hr = S_FALSE;
                        break;

                    default:
                        ASSERT(0);        // Huh?
                        // FALLTHROUGH

                    case IDC_REPLACE_CANCEL:
                        *pOps = opsCancel;
                        hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
                        break;
                    }
                    FindClose(hfindDest);
                }
            }
        }
    }

    return hr;
}


// The following struct is used when recursively downloading
// files/dirs from the FTP server after a "Download" verb.
typedef struct tagDOWNLOADSTRUCT
{
    LPCWSTR             pwzDestRootPath;    // Dir on FileSys of the Download Destination
    LPCITEMIDLIST       pidlRoot;           // Base URL of the Download Source
    DWORD               dwInternetFlags;    // Binary, ASCII, AutoDetect?
    HWND                hwndParent;         // hwnd for Confirm UI
    OPS                 ops;                // Do we cancel?
    CFtpFolder *        pff;
    CFtpDir *           pfd;

    // Progress
    PROGRESSINFO        progInfo;
} DOWNLOADSTRUCT;


/*****************************************************************************\
     FUNCTION: _CalcDestName
 
    DESCRIPTION:
        This recursive function starts at pwzDestDir as the dest FS path and
    pidlRoot as the src ftp path.  We need to construct pwzDestPath which
    is the current path.  This will be done by adding the relative path
    (pidlFull - pidlRoot) to pwzDestDir.  pidlFull can point to either a file
    or a directory.
 
    PARAMETERS: (Example. "C:\dir1\dir2\dir3\file.txt")
         pwzDestParentPath: "C:\dir1\dir2\dir3"
         pwzDestDir: "C:\dir1\dir2\dir3\file.txt"
         pwzDestFileName: "file.txt"

    Example. "C:\dir1\dir2\dir3\"
         pwzDestParentPath: "C:\dir1\dir2"
         pwzDestDir: "C:\dir1\dir2\dir3"
         pwzDestFileName: "dir3"
\*****************************************************************************/
HRESULT _CalcDestName(LPCWSTR pwzDestDir, LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlFull, LPWSTR pwzDestParentPath, DWORD cchDestParentPathSize,
                      LPWSTR pwzDestPath, DWORD cchDestPathSize, LPWSTR pwzDestFileName, DWORD cchDestFileNameSize)
{
    HRESULT hr = S_OK;
    WCHAR wzFtpPathTemp[MAX_PATH];
    WCHAR wzFSPathTemp[MAX_PATH];
    LPITEMIDLIST pidlRootIterate = (LPITEMIDLIST) pidlRoot;    // I promise to iterate only
    LPITEMIDLIST pidlFullIterate = (LPITEMIDLIST) pidlFull;    // I promise to iterate only

    // This one is easy.
    StrCpyNW(pwzDestFileName, FtpPidl_GetLastFileDisplayName(pidlFull), cchDestFileNameSize);  // The dest filename is easy.

    // Let's find the relative path between pidlRoot and pidlFull.
    while (!ILIsEmpty(pidlRootIterate) && !ILIsEmpty(pidlFullIterate) && FtpItemID_IsEqual(pidlRootIterate, pidlFullIterate))
    {
        pidlFullIterate = _ILNext(pidlFullIterate);
        pidlRootIterate = _ILNext(pidlRootIterate);
    }

    ASSERT(ILIsEmpty(pidlRootIterate) && !ILIsEmpty(pidlFullIterate));  // Asure pidlFull is a superset of pidlRoot
    LPITEMIDLIST pidlParent = ILClone(pidlFullIterate);

    if (pidlParent)
    {
        ILRemoveLastID(pidlParent); // Remove the item that will be created (file or dir)

        GetDisplayPathFromPidl(pidlParent, wzFtpPathTemp, ARRAYSIZE(wzFtpPathTemp), TRUE);   // Full path w/o last item.
        StrCpyNW(pwzDestParentPath, pwzDestDir, cchDestParentPathSize);  // Put the base dest.
        UrlPathToFilePath(wzFtpPathTemp, wzFSPathTemp, ARRAYSIZE(wzFSPathTemp));
        if (!PathAppendW(pwzDestParentPath, wzFSPathTemp))
            hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);    // Path too long, probably.

        ILFree(pidlParent);
    }

    if (SUCCEEDED(hr))
    {
        GetDisplayPathFromPidl(pidlFullIterate, wzFtpPathTemp, ARRAYSIZE(wzFSPathTemp), FALSE);   // Full Path including item.
        StrCpyNW(pwzDestPath, pwzDestDir, cchDestParentPathSize);  // Put the base dest.
        UrlPathToFilePath(wzFtpPathTemp, wzFSPathTemp, ARRAYSIZE(wzFSPathTemp));
        if (!PathAppendW(pwzDestPath, wzFSPathTemp))
            hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);    // Path too long, probably.
    }

    return hr;
}


// This defines the size of a directory measured by the amount of time it would take compared to a file.
#define VIRTUAL_DIR_SIZE        1000        // about 1k.

/*****************************************************************************\
     FUNCTION: DownloadItemStackPig
 
    DESCRIPTION:
        This function will download the specified item and it's contents if it
    is a directory.
\*****************************************************************************/
HRESULT DownloadItemStackPig(HINTERNET hint, LPCITEMIDLIST pidlFull, BOOL * pfValidhinst, DOWNLOADSTRUCT * pDownLoad, CFtpDir ** ppfd)
{
    HRESULT hr;
    WCHAR wzDestParentPath[MAX_PATH];       // If item is "C:\dir1\dir2copy\", the this is "C:\dir1"
    WCHAR wzDestPath[MAX_PATH];             // This is "C:\dir1\dir2copy\"
    WCHAR wzDestFileName[MAX_PATH];         // This is "dir2copy"

    hr = _CalcDestName(pDownLoad->pwzDestRootPath, pDownLoad->pidlRoot, pidlFull, wzDestParentPath, ARRAYSIZE(wzDestParentPath), wzDestPath, ARRAYSIZE(wzDestPath), wzDestFileName, ARRAYSIZE(wzDestFileName));
    if (SUCCEEDED(hr))
    {
        if (pDownLoad->progInfo.ppd)
            EVAL(SUCCEEDED(UpdateDownloadProgress(&(pDownLoad->progInfo), pidlFull, wzDestParentPath, wzDestFileName)));

        if (pDownLoad->progInfo.ppd && pDownLoad->progInfo.ppd->HasUserCancelled())
            hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
        else
        {
            // Is this a dir/folder that we need to recurse into?
            if (FILE_ATTRIBUTE_DIRECTORY & FtpPidl_GetAttributes(pidlFull))
            {
                // Yes, so let's go...

                if (EVAL((PathFileExistsW(wzDestPath) && PathIsDirectoryW(wzDestPath)) ||
                            CreateDirectoryW(wzDestPath, NULL)))
                {
                    EVAL(SetFileAttributes(wzDestPath, FtpPidl_GetAttributes(pidlFull)));
                    hr = pDownLoad->pfd->GetFtpSite()->GetFtpDir(pidlFull, ppfd);
                    if (!EVAL(SUCCEEDED(hr)))
                        TraceMsg(TF_ERROR, "DownloadItemStackPig() GetFtpDir failed hr=%#08lx", hr);
                }
                else
                {
                    hr = E_FAIL;
                    TraceMsg(TF_ERROR, "DownloadItemStackPig() CreateDirectory or PathFileExists failed hr=%#08lx", hr);
                }
            }
            else
            {
                BOOL fDeleteRequired;
                ULARGE_INTEGER uliFileSize;

                pDownLoad->progInfo.dwCompletedInCurFile = 0;
                pDownLoad->progInfo.dwLastDisplayed = 0;

                hr = ConfirmDownloadReplace(wzDestPath, pidlFull, &(pDownLoad->ops), GetProgressHWnd(pDownLoad->progInfo.ppd, pDownLoad->hwndParent), pDownLoad->pff, pDownLoad->pfd, 1, &fDeleteRequired);
                if (S_OK == hr)
                {
                    if (fDeleteRequired)
                    {
                        if (!DeleteFileW(wzDestPath))
                            hr = HRESULT_FROM_WIN32(GetLastError());
                    }

                    // Don't copy the file if it's a SoftLink because of the possible
                    // recursion case.
                    if (EVAL(SUCCEEDED(hr)) && (0 != FtpPidl_GetAttributes(pidlFull)))
                    {
                        // Contemplate adding a callback function in order to feed the status bar.
                        hr = FtpGetFileExPidlWrap(hint, TRUE, pidlFull, wzDestPath, TRUE, FtpPidl_GetAttributes(pidlFull), pDownLoad->dwInternetFlags, (DWORD_PTR)&(pDownLoad->progInfo));
                        if (FAILED(hr))
                        {
                            if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
                            {
                                // We need to display the error now while the extended error info is still valid.
                                // This is because as we walk out of the resursive call, we will be calling
                                // FtpSetCurrentDirectory() which will wipe clean the extended error msg.
                                DisplayWininetError(pDownLoad->hwndParent, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_DOWNLOADING, IDS_FTPERR_WININET, MB_OK, pDownLoad->progInfo.ppd);
                                hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);   // Don't display any more error dialogs.
                            }
                        }
                        else
                        {
                            // The docs imply that (FILE_SHARE_READ | FILE_SHARE_WRITE) means that other callers need both, but
                            // I want them to be able to use either.
                            HANDLE hFile = CreateFileW(wzDestPath, GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FtpPidl_GetAttributes(pidlFull), NULL);

                            // FtpGetFile() won't set the time/date correctly, so we will.
                            if (EVAL(INVALID_HANDLE_VALUE != hFile))
                            {
                                FILETIME ftLastWriteTime = FtpPidl_GetFileTime(ILFindLastID(pidlFull));

                                // Since the file time on the disk is stored in a time zone independent way (UTC)
                                // we have a problem because FTP WIN32_FIND_DATA is in the local time zone.  So we
                                // need to convert the FTP local time to UTC when we set the file.
                                // Note that we are using an optimization that uses the fact that FTP always
                                // has the same time for LastAccessTime, LastWriteTime, and CreationTime.
    //                                ASSERT(pwfd->ftCreationTime.dwLowDateTime = pwfd->ftLastAccessTime.dwLowDateTime = pwfd->ftLastWriteTime.dwLowDateTime);
    //                                ASSERT(pwfd->ftCreationTime.dwHighDateTime = pwfd->ftLastAccessTime.dwHighDateTime = pwfd->ftLastWriteTime.dwHighDateTime);

                                // priv.h has notes on how time works.
                                SetFileTime(hFile, &ftLastWriteTime, &ftLastWriteTime, &ftLastWriteTime);
                                CloseHandle(hFile);
                            }
                            SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, wzDestPath, NULL);
                        }
                    }
                }

                uliFileSize.QuadPart = FtpPidl_GetFileSize(pidlFull);
                pDownLoad->progInfo.uliBytesCompleted.QuadPart += uliFileSize.QuadPart;
            }
        }
    }

    if (pfValidhinst)
        *pfValidhinst = (pDownLoad->progInfo.hint ? TRUE : FALSE);

    return hr;
}


/*****************************************************************************\
     FUNCTION: DownloadItemCB
 
    DESCRIPTION:
        This function will download the specified item and it's contents if it
    is a directory.
\*****************************************************************************/
HRESULT DownloadItemCB(LPVOID pvFuncCB, HINTERNET hint, LPCITEMIDLIST pidlFull, BOOL * pfValidhinst, LPVOID pvData)
{
    DOWNLOADSTRUCT * pDownLoad = (DOWNLOADSTRUCT *) pvData;
    LPFNPROCESSITEMCB pfnProcessItemCB = (LPFNPROCESSITEMCB) pvFuncCB;
    CFtpDir * pfdNew = NULL;
    HRESULT hr = DownloadItemStackPig(hint, pidlFull, pfValidhinst, pDownLoad, &pfdNew);

    if (SUCCEEDED(hr) && pfdNew)    // pfdNew Maybe NULL if cancelled
    {
        CFtpDir * pfdOriginal = pDownLoad->pfd;

        pDownLoad->pfd = pfdNew;
        hr = EnumFolder(pfnProcessItemCB, hint, pidlFull, pDownLoad->pff->GetCWireEncoding(), pfValidhinst, pvData);
        pDownLoad->pfd = pfdOriginal;

        pfdNew->Release();
    }

    return hr;
}


// BUGBUG/TODO: First, make this work on pidls that Bind to IStorages.
//              Second, nuke the CDownloadDialog code.
HRESULT ShowDownloadDialog(HWND hwnd, LPTSTR pszPath, DWORD cchSize)
{
    TCHAR szMessage[MAX_URL_STRING];
    HRESULT hr;
    LPITEMIDLIST pidlDefault = NULL;
    LPITEMIDLIST pidlFolder = NULL;
    HKEY hkey = NULL;
    IStream * pstrm = NULL;

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, SZ_REGKEY_MICROSOFTSOFTWARE, 0, (KEY_READ | KEY_WRITE), &hkey))
    {
        pstrm = SHOpenRegStream(hkey, SZ_REGKEY_FTPCLASS, SZ_REGVALUE_DOWNLOAD_DIR, STGM_READWRITE);
        if (pstrm)
            ILLoadFromStream(pstrm, &pidlDefault);  // Will return (NULL == pidlDefault) if the reg value is empty.
    }

    if (!pidlDefault && (SHELL_VERSION_W95NT4 == GetShellVersion()))   // If reg key is empty.
        pidlDefault = SHCloneSpecialIDList(NULL, CSIDL_PERSONAL, TRUE);

    EVAL(LoadString(HINST_THISDLL, IDS_DLG_DOWNLOAD_TITLE, szMessage, ARRAYSIZE(szMessage)));
    hr = BrowseForDir(hwnd, szMessage, pidlDefault, &pidlFolder);
    if (pstrm)
    {
        // Do we want to save the new pidl?
        if (S_OK == hr)
        {
            LARGE_INTEGER li = {0};
            ULARGE_INTEGER uli = {0};

            // rewind the stream to the beginning so that when we
            // add a new pidl it does not get appended to the first one
            pstrm->Seek(li, STREAM_SEEK_SET, &uli);
            ILSaveToStream(pstrm, pidlFolder);
        }

        pstrm->Release();
    }

    if (S_OK == hr)
    {
        ASSERT(cchSize >= MAX_PATH);        // This is an assumption SHGetPathFromIDList makes.
        hr = (SHGetPathFromIDList(pidlFolder, pszPath) ? S_OK : E_FAIL);
    }

    if (hkey)
        RegCloseKey(hkey);

    if (pidlDefault)
        ILFree(pidlDefault);

    if (pidlFolder)
        ILFree(pidlFolder);

    return hr;
}


/*****************************************************************************\
    FUNCTION: _InvokeDownloadVerb

    DESCRIPTION:
        The user just selected file(s) and/or folder(s) and selected the
    "download" verb.  We need t

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -