【问题标题】:Drag-and-drop files from a Zip folder into my window将 Zip 文件夹中的文件拖放到我的窗口中
【发布时间】:2011-05-13 09:40:42
【问题描述】:

当 Zip 存档的内容可以从 Windows 资源管理器的 Zip 文件夹拖放到我的窗口中时,我正在尝试实现一项功能。我实现了 IDropTarget 的所有必要方法,当我从 Windows 资源管理器拖放常规文件时,一切正常。

当我尝试从 Zip 文件夹中拖入文件时,问题发生在以下方法中:

HRESULT DragEnter(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{
    static FORMATETC fmtetc_file = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    HRESULT hr = pDataObject->QueryGetData(&fmtetc_file);
    if(hr == S_OK)
    {
        //Format supported
    }

    ...
}

我得到了从 QueryGetData() 返回的 S_FALSE。

有人知道我错过了什么吗?

【问题讨论】:

    标签: c++ zip windows-explorer


    【解决方案1】:

    我无法想象 Explorer 的 zip 文件处理程序实现了 CF_HDROP,因为这需要它在开始拖动之前提取文件。我打赌它使用CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS

    【讨论】:

    • 非常感谢,使用以下代码行为 DragEnter() 工作:FORMATETC fmtetc_file = {RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; if(pDataObject->QueryGetData(&fmtetc_file) ==S_OK) { } 但现在我的问题是如何获取 zip 存档的路径(其文件被拖到我的窗口)或文件本身的临时路径(如果 Shell 碰巧为我解压缩它)?
    • 拖放的全部意义在于抽象出诸如zip文件之类的实现细节;如果你能弄清楚,我会有点惊讶。
    • 对不起,卢克,但我还是想不通。那么我应该如何访问拖到我的窗口中的文件呢?
    • 我没有专门用 zip 文件夹扩展名对此进行测试,但我猜 FILEDESCRIPTOR 结构具有存档成员的相对路径(例如“folder1\folder2\filename.ext”)。如果归档成员很小,数据将是 IStream 或可能是 HGLOBAL。您必须将文件内容从 HGLOBAL 或 IStream 复制到您的目的地。
    • 谢谢。我做了一些测试,实际上我得到了 zip 存档中每个文件的 HGLOBAL,除了它包含的数据与文件的实际内容无关。它看起来更像是某种内部数据结构(当我们调用 DragQueryFile 时,就像普通文件的结构一样)。所以我想知道,对于 Zip 存档是否有替代 DragQueryFile() 的方法?
    【解决方案2】:

    我想我明白了。你能复习一下这个伪代码吗,我不太擅长 COM:

    Drop(IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
    {
        FORMATETC fmtetc_file_desc = {RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        if(pDataObject->QueryGetData(&fmtetc_file_desc) == S_OK)
        {
            STGMEDIUM stgmed;
            if(pDataObject->GetData(&fmtetc_file_desc, &stgmed) == S_OK)
            {
                if(stgmed.tymed & TYMED_HGLOBAL)
                {
                    FILEGROUPDESCRIPTOR* pFGD = (FILEGROUPDESCRIPTOR*)::GlobalLock(stgmed.hGlobal);
                    for(int f = 0; f < pFGD->cItems; f++)
                    {
                        STGMEDIUM stgmedFile = {0};
                        //You may want to move out the RegisterClipboardFormat() API into some global variable
                        FORMATETC fmtetc_file_desc = {RegisterClipboardFormat(CFSTR_FILECONTENTS), 0, DVASPECT_CONTENT, f, TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE};
                        if(pDataObject->GetData(&fmtetc_file_desc, &stgmedFile) == S_OK)
                        {
                            BOOL bReadOK = FALSE;
    
                            if(!bReadOK && (stgmedFile & TYMED_ISTREAM))
                            {
                                //Now read data from a stream & process it
                                //(If need be, it can be saved in a file)
                                IStream *pstm = pStgmed->pstm;
    
                                //Size of data in a steam & archived file name
                                STATSTG stg = {0};
                                SUCCEEDED(pstm->Stat(&stg, STATFLAG_DEFAULT) == S_OK);
    
                                //Then to read data from a stream
                                //Call repeatedly until all or required data is read)
                                SUCCEEDED(pstm->Read(pStorage, ncbBytesRead, &ucbBytesRead));
    
                                //If read and processed successfully
                                bReadOK = TRUE;
    
    
                                //Release mem
                                CoTaskMemFree(stg.pwcsName);
    
                            }
    
                            //Probably need to implement these as well?
                            if(!bReadOK && (stgmedFile & TYMED_ISTORAGE))
                            {
                            }
                            if(!bReadOK && (stgmedFile & TYMED_HGLOBAL))
                            {
                            }
    
    
                            ReleaseStgMedium(&stgmedFile);;
                        }
                    }
    
                    ::GlobalUnlock(stgmed.hGlobal);
                }
    
                ReleaseStgMedium(&stgmed);
    
            }
        }
    
    }
    

    【讨论】:

    • 我觉得不错。在我的测试中,我只见过 IStream;不确定是否实际使用了任何其他存储方法。此外,Stat() 正在返回 E_NOTIMPL,因此您可能只需要 Read() 直到 cbBytesRead == 0。
    • 嗯,真的。您在哪个版本的 Windows 上进行测试?
    • 感谢您的帮助。我看到由于某种原因 Stat() 和 Seek() 方法似乎没有在 XP 上实现。尽管如此,我还是能够弄清楚,现在它可以在所有平台上运行——从 XP 到 Win7。
    猜你喜欢
    • 1970-01-01
    • 2020-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多