fret
fret

Reputation: 1590

How do I create a CFSTR_FILEDESCRIPTOR of unknown size?

I have an email client that allows the user to export a folder of email as a MBOX file. The UX is that they drag the folder from inside the application to an explorer folder (e.g. the Desktop) and a file copy commences via the application adding CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS to the data object being dropped. The issue arises when working out how to specify the size of the "file". Because internally I store the email in a database and it takes quite a while to fully encode the output MBOX, especially if the folder has many emails. Until that encoding is complete I don't have an exact size... just an estimate.

Currently I return an IStream pointer to windows, and over-specify the size in the file descriptor (estimate * 3 or something). Then when I hit the end of my data I return a IStream::Read length less then the input buffer size. Which causes Windows to give up on the copy. In Windows 7 it leaves the "partial" file there in the destination folder which is perfect, but in XP it fails the copy completely, leaving nothing in the destination folder. Other versions may exhibit different behaviour.

Is there a way of dropping a file of unknown size onto explorer that has to be generated by the source application?

Alternatively can I just get the destination folder path and do all the copy progress + output internally to my application? This would be great, I have all the code to do it already. Problem is I'm not the process accepting the drop.

Bonus round: This also needs to work on Linux/GTK and Mac/Carbon so any pointers there would be helpful too.

Upvotes: 2

Views: 448

Answers (2)

Denis Anisimov
Denis Anisimov

Reputation: 3317

Windows Explorer use three methods to detect size of stream (in order of priority):

  1. nFileSizeHigh/Low fields of FILEDESCRIPTOR structure if FD_FILESIZE flags is present.
  2. Calling IStream.Seek(0, STREAM_SEEK_END, FileSize).
  3. Calling IStream.Stat. cbSize field of STATSTG structure is used as MAX file size only.

To pass to Explorer a file with unknown size it is necessary:

  1. Remove FD_FILESIZE flags from FILEDESCRIPTOR structure.
  2. IStream.Seek must not be implemented (must return E_NOTIMPL).
  3. IStream.Stat must set cbSize field to -1 (0xFFFFFFFFFFFFFFFF).

Upvotes: 4

Remy Lebeau
Remy Lebeau

Reputation: 596437

Is there a way of dropping a file of unknown size onto explorer that has to be generated by the source application?

When providing CFSTR_FILEDESCRIPTOR, you don't have to provide a file size at all if you don't know it ahead of time. The FD_FILESIZE flag in the FILEDESCRIPTOR::dwFlags field is optional. Provide an exact size only if you know it, otherwise don't provide a size at all, not even an estimate. The copy will still proceed, but the target won't know the final size until IStream::Read() returns S_FALSE to indicate the end of the stream has been reached.

Alternatively can I just get the destination folder path and do all the copy progress + output internally to my application?

A drop operation does not provide any information about the target at all. And for good reason - the source doesn't need to know. A drop target does not need to know where the source data is coming from, only how to access it. The drag source does not need to know how the drop target will use the data, only how to provide the data to it.

Think of what happens if you drop a file (virtual or otherwise) onto an Explorer folder that is implemented as a Shell Namespace Extension that saves the file to another database, or uploads it to a remote server. The filesystem is not involved, so you wouldn't be able to manually copy your data to the target even if you wanted to. Only the target knows how its data is stored.

That being said, the only way I know to get the path of a filesystem folder being dropped into is to drag&drop a dummy file, and then monitor the filesystem for where the drop target creates/copies the file to. Then you can replace the dummy file with your real file. But this is not very reliable, and not very friendly to the target.

Upvotes: 1

Related Questions