Background
Our Eclipse RCP 3.6-based application lets people drag files in for storage/processing. This works fine when the files are dragged from a filesystem, but not when people drag items (messages or attachments) directly from Outlook.
This appears to be because Outlook wants to feed our application the files via a FileGroupDescriptorW
and FileContents
, but SWT only includes a FileTransfer
type. (In a FileTransfer
, only the file paths are passed, with the assumption that the receiver can locate and read them. The FileGroupDescriptorW
/FileContents
approach can supply files directly application-to-application without writing temporary files out to disk.)
We have tried to produce a ByteArrayTransfer
subclass that could accept FileGroupDescriptorW
and FileContents
. Based on some examples on the Web, we were able to receive and parse the FileGroupDescriptorW
, which (as the name implies) describes the files available for transfer. (See code sketch below.) But we have been unable to accept the FileContents
.
This seems to be because Outlook offers the FileContents
data only as TYMED_ISTREAM
or TYMED_ISTORAGE
, but SWT only understands how to exchange data as TYMED_HGLOBAL
. Of those, it appears that TYMED_ISTORAGE
would be preferable, since it's not clear how TYMED_ISTREAM
could provide access to multiple files' contents.
(We also have some concerns about SWT's desire to pick and convert only a single TransferData
type, given that we need to process two, but we think we could probably hack around that in Java somehow: it seems that all the TransferData
s are available at other points of the process.)
Questions
Are we on the right track here? Has anyone managed to accept FileContents
in SWT yet? Is there any chance that we could process the TYMED_ISTORAGE
data without leaving Java (even if by creating a fragment-based patch to, or a derived version of, SWT), or would we have to build some new native support code too?
Relevant code snippets
Sketch code that extracts file names:
// THIS IS NOT PRODUCTION-QUALITY CODE - FOR ILLUSTRATION ONLY
final Transfer transfer = new ByteArrayTransfer() {
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
@Override
protected String[] getTypeNames() {
return typeNames;
}
@Override
protected int[] getTypeIds() {
return typeIds;
}
@Override
protected Object nativeToJava(TransferData transferData) {
if (!isSupportedType(transferData))
return null;
final byte[] buffer = (byte[]) super.nativeToJava(transferData);
if (buffer == null)
return null;
try {
final DataInputStream in = new DataInputStream(new ByteArrayInputStream(buffer));
long count = 0;
for (int i = 0; i < 4; i++) {
count += in.readUnsignedByte() << i;
}
for (int i = 0; i < count; i++) {
final byte[] filenameBytes = new byte[260 * 2];
in.skipBytes(72); // probable architecture assumption(s) - may be wrong outside standard 32-bit Win XP
in.read(filenameBytes);
final String fileNameIncludingTrailingNulls = new String(filenameBytes, "UTF-16LE");
int stringLength = fileNameIncludingTrailingNulls.indexOf('\0');
if (stringLength == -1)
stringLength = 260;
final String fileName = fileNameIncludingTrailingNulls.substring(0, stringLength);
System.out.println("File " + i + ": " + fileName);
}
in.close();
return buffer;
}
catch (final Exception e) {
return null;
}
}
};
In the debugger, we see that ByteArrayTransfer
's isSupportedType()
ultimately returns false
for the FileContents
because the following test is not passed (since its tymed
is TYMED_ISTREAM | TYMED_ISTORAGE
):
if (format.cfFormat == types[i] &&
(format.dwAspect & COM.DVASPECT_CONTENT) == COM.DVASPECT_CONTENT &&
(format.tymed & COM.TYMED_HGLOBAL) == COM.TYMED_HGLOBAL )
return true;
This excerpt from org.eclipse.swt.internal.ole.win32.COM
leaves us feeling less hope for an easy solution:
public static final int TYMED_HGLOBAL = 1;
//public static final int TYMED_ISTORAGE = 8;
//public static final int TYMED_ISTREAM = 4;
Thanks.