What a drag: Dragging a virtual file (IStream edition) - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20080319-00/?p=23073
Raymond Chen 2008年03月19日
拖拽虚拟文件(IStream 版本)
上一次,我们看到了如何拖拽一个虚拟文件,其内容以内存中的字节块(HGLOBAL
)形式表示。通常,字节块不是表示虚拟文件内容的便捷方式。你可能更倾向于以流的形式来表示它。例如,内容可能是动态生成的(比如由算法输出),或者它可能来自外部来源(比如正在下载的网页)。让我们将上次的程序转换为以流的形式返回文件内容。我们需要做的首个更改是在我们的构造函数中,让它报告文件内容为流而不是HGLOBAL
:
#include <shlwapi.h> // 用于 SHOpenRegStream
CTinyDataObject::CTinyDataObject() : m_cRef(1)
{
SetFORMATETC(&m_rgfe[DATA_FILEGROUPDESCRIPTOR],
RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
SetFORMATETC(&m_rgfe[DATA_FILECONTENTS],
RegisterClipboardFormat(CFSTR_FILECONTENTS),
TYMED_ISTREAM, /* lindex */ 0);
}
接下来,我们需要在IDataObject::GetData
处理器中产生该流及其对应的描述符:
HRESULT CTinyDataObject::GetData(FORMATETC *pfe, STGMEDIUM *pmed)
{
ZeroMemory(pmed, sizeof(*pmed));
switch (GetDataIndex(pfe)) {
case DATA_FILEGROUPDESCRIPTOR:
{
FILEGROUPDESCRIPTOR fgd;
ZeroMemory(&fgd, sizeof(fgd));
fgd.cItems = 1;
StringCchCopy(fgd.fgd[0].cFileName,
ARRAYSIZE(fgd.fgd[0].cFileName),
TEXT("Dummy"));
pmed->tymed = TYMED_HGLOBAL;
return CreateHGlobalFromBlob(&fgd, sizeof(fgd),
GMEM_MOVEABLE, &pmed->hGlobal);
}
case DATA_FILECONTENTS:
pmed->tymed = TYMED_ISTREAM;
pmed->pstm = SHOpenRegStream(HKEY_LOCAL_MACHINE,
TEXT("Hardware\\Description\\System\\CentralProcessor\\0"),
TEXT("~MHz"), STGM_READ);
// 正确设置流位置
if (pmed->pstm) {
LARGE_INTEGER liZero = { 0, 0 };
pmed->pstm->Seek(liZero, STREAM_SEEK_SET, NULL);
}
return pmed->pstm ? S_OK : E_FAIL;
}
return DV_E_FORMATETC;
}
当然,在现实生活中,你会使用一个更有趣的流而不是你的CPU速度。我只是选择了那个作为例子。
和基于HGLOBAL
的数据对象一样,你可以将这个数据对象拖放到资源管理器文件夹中创建文件,拖放到Outlook消息中创建附件,以及任何支持Shell虚拟文件传输模型的地方。和HGLOBAL
示例一样,你可以在FILEGROUPDESCRIPTOR
中设置各种可选信息,以使传输更顺畅,特别是预期的流大小。但我不会再深入讨论,因为这一系列文章的主题是“这是你能做的最少”。
但你已经知道足够多的信息来解决这个客户的问题:
我们需要知道用户将文件拖放到了哪个目录。我们需要从另一台计算机传输数据,所以我们的做法是让用户拖拽一个单一的虚拟文件,然后一旦我们知道用户将虚拟文件拖放到了哪里,我们可以进去,删除虚拟文件,并开始从远程计算机传输数据并将其保存到目标目录中的真正文件中。
下次,我们将看看用于文件传输的最终存储介质,TYMED_ISTORAGE
。