如何创建未知大小的CFSTR_FILEDESCRIPTOR?

时间:2016-09-19 09:04:22

标签: winapi drag-and-drop

我有一个email client,允许用户将电子邮件文件夹导出为MBOX文件。 UX是将文件夹从应用程序内部拖动到资源管理器文件夹(例如桌面),文件副本通过应用程序开始,将CFSTR_FILEDESCRIPTOR和CFSTR_FILECONTENTS添加到要删除的数据对象。在研究如何指定“文件”的大小时会出现问题。因为在内部我将电子邮件存储在数据库中,并且完全编码输出MBOX需要相当长的时间,特别是如果文件夹有很多电子邮件。在编码完成之前,我没有确切的大小...只是估计。

目前我返回一个指向Windows的IStream指针,并在文件描述符中过度指定大小(估计* 3或其他)。然后,当我点击数据的末尾时,我返回的IStream :: Read长度小于输入缓冲区大小。这导致Windows放弃副本。在Windows 7中,它将“部分”文件保留在目标文件夹中,这是完美的,但在XP中它完全失败了副本,在目标文件夹中没有任何内容。其他版本可能表现出不同的行为。

有没有办法将未知大小的文件放到必须由源应用程序生成的资源管理器中?

或者我可以获取目标文件夹路径并在内部对我的应用程序执行所有复制进度+输出吗?这将是伟大的,我已经有了所有代码。问题是我不是接受这种下降的过程。

Bonus round:这也需要在Linux / GTK和Mac / Carbon上运行,因此任何指针都会有所帮助。

2 个答案:

答案 0 :(得分:2)

Windows资源管理器使用三种方法来检测流的大小(按优先级顺序):

  1. 如果存在FD_FILESIZE标志,则为FILEDESCRIPTOR结构的nFileSizeHigh / Low字段。
  2. 调用IStream.Seek(0,STREAM_SEEK_END,FileSize)。
  3. 调用IStream.Stat。 STATSTG结构的cbSize字段仅用作MAX文件大小。
  4. 要向资源管理器传递大小未知的文件,必须:

    1. 从FILEDESCRIPTOR结构中删除FD_FILESIZE标志。
    2. 不得实施IStream.Seek(必须返回E_NOTIMPL)。
    3. IStream.Stat必须将cbSize字段设置为-1(0xFFFFFFFFFFFFFFFF)。

答案 1 :(得分:1)

  

有没有办法将未知大小的文件放到必须由源应用程序生成的资源管理器中?

提供CFSTR_FILEDESCRIPTOR时,如果您事先不知道,则根本不需要提供文件大小。 FD_FILESIZE字段中的FILEDESCRIPTOR::dwFlags标志是可选的。只有在您知道的情况下才能提供准确的尺寸,否则根本不提供尺寸,甚至不提供估算。该副本仍将继续,但目标无法知道最终大小,直到IStream::Read()返回S_FALSE表示已达到流的结尾。

  

或者我可以直接获取目标文件夹路径并在内部对我的应用程序执行所有复制进度+输出吗?

放置操作根本不提供有关目标的任何信息。并且有充分的理由 - 来源并不需要知道。放置目标不需要知道源数据的来源,只知道如何访问它。拖动源不需要知道放置目标将如何使用数据,只需知道如何向其提供数据。

考虑将文件(虚拟或其他)拖放到Explorer文件夹上会发生什么,该文件夹实现为Shell命名空间扩展,将文件保存到另一个数据库,或将其上载到远程服务器。文件系统不涉及,因此即使您愿意,也无法手动将数据复制到目标。只有目标知道其数据的存储方式。

话虽这么说,我知道获取文件系统文件夹路径的唯一方法是拖放一个虚拟文件,然后监视文件系统以获取放置目标创建/复制文件的位置。然后,您可以用您的真实文件替换虚拟文件。但这不是很可靠,对目标也不是很友好。