我可以将 Outlook 中的项目拖到我的 SWT 应用程序中吗?
背景
我们基于 Eclipse RCP 3.6 的应用程序允许人们拖动文件进行存储/处理。当从文件系统中拖动文件时,这种方法可以正常工作,但当人们直接从 Outlook 中拖动项目(消息或附件)时,这种方法就不行了。
这似乎是因为 Outlook 希望通过 FileGroupDescriptorW
和 FileContents
向我们的应用程序提供文件,但 SWT 仅包含 FileTransfer
类型。 (在 FileTransfer
中,仅传递文件路径,并假设接收者可以找到并读取它们。FileGroupDescriptorW
/FileContents
方法可以直接在应用程序到应用程序之间提供文件,而无需将临时文件写入磁盘。)
我们尝试生成一个可以接受 FileGroupDescriptorW
的 ByteArrayTransfer
子类,并且文件内容
。根据网络上的一些示例,我们能够接收并解析 FileGroupDescriptorW ,它(顾名思义)描述了可传输的文件。 (参见下面的代码草图。)但是我们一直无法接受FileContents
。
这似乎是因为 Outlook 仅将 FileContents
数据提供为 TYMED_ISTREAM
或 TYMED_ISTORAGE
,但 SWT 只了解如何将数据交换为 TYMED_HGLOBAL
。其中,TYMED_ISTORAGE
似乎更可取,因为尚不清楚 TYMED_ISTREAM
如何提供对多个文件内容的访问。
(考虑到我们需要处理两个类型,我们也对 SWT 只选择和转换单个 TransferData
类型感到担忧,但我们认为我们可以在 Java 中以某种方式解决这个问题:看起来所有TransferData
都可以在流程的其他点使用。)
问题
我们是否走在正确的轨道上?有人设法在 SWT 中接受 FileContents
吗?我们是否有可能在不离开 Java 的情况下处理 TYMED_ISTORAGE 数据(即使通过创建基于片段的补丁或 SWT 的派生版本),或者我们是否必须构建一些新的也有本机支持代码吗?
相关代码片段
提取文件名的草图代码:
// 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;
}
}
};
在调试器中,我们看到 ByteArrayTransfer
的 isSupportedType()
最终返回 false
用于 FileContents
,因为以下测试未通过(因为其 tymed
是 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;
这段摘录自 org.eclipse.swt.internal.ole.win32.COM
让我们对简单的解决方案不再抱有希望:
public static final int TYMED_HGLOBAL = 1;
//public static final int TYMED_ISTORAGE = 8;
//public static final int TYMED_ISTREAM = 4;
谢谢。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
即使
尝试下面的代码..它应该可以工作
even if
Try below code.. it should work
您是否看过 https://bugs.eclipse.org/bugs/show_bug.cgi ?id=132514?
此 bugzilla 条目附带了一个可能令人感兴趣的补丁(针对相当旧的 SWT 版本)。
Have you looked at https://bugs.eclipse.org/bugs/show_bug.cgi?id=132514 ?
Attached to this bugzilla entry is an patch (against an rather old version of SWT) that might be of interest.
我遇到了同样的问题,并创建了一个小型库,为 JAVA SWT 提供拖放传输类。可以在这里找到:
https://github.com/HendrikHoetker/OutlookItemTransfer
目前它支持删除邮件从 Outlook 到 Java SWT 应用程序的项目将提供 OutlookItems 列表以及文件名和文件内容的字节数组。
一切都是纯 Java 且位于内存中(没有临时文件)。
在 SWT java 应用程序中的用法:
OutlookItem 将提供两个元素:字符串形式的文件名和字节数组形式的文件内容。
从这里开始,人们可以将其写入文件或进一步处理字节数组。
针对你上面的问题:
- 您在文件描述符中找到的是 Outlook 项目的文件名和指向 IDataObject 的指针
- IDataObject 可以被解析并提供一个 IStorage 对象
- IStorageObject 将成为根容器,提供类似于文件系统的进一步子 IStorageObject 或 IStream(目录 = IStorage,文件 = IStream
您可以在以下代码行中找到这些元素:
获取文件内容,请参阅 OutlookItemTransfer.java ,方法nativeToJava:
读取根IStorage,请参阅CompoundStorage,方法readOutlookStorage:
I had the same problem and created a small library providing a Drag'n Drop Transfer Class for JAVA SWT. It can be found here:
https://github.com/HendrikHoetker/OutlookItemTransfer
Currently it supports dropping Mail Items from Outlook to your Java SWT application and will provide a list of OutlookItems with the Filename and a byte array of the file contents.
All is pure Java and in-memory (no temp files).
Usage in your SWT java application:
The OutlookItem will then provide two elements: filename as String and file contents as array of byte.
From here on, one could write it to a file or further process the byte array.
To your question above:
- What you find in the file descriptor is the filename of the outlook item and a pointer to an IDataObject
- the IDataObject can be parsed and will provide an IStorage object
- The IStorageObject will be then a root container providing further sub-IStorageObjects or IStreams similar to a filesystem (directory = IStorage, file = IStream
You find those elements in the following lines of code:
Get File Contents, see OutlookItemTransfer.java, method nativeToJava:
Read the root IStorage, see CompoundStorage, method readOutlookStorage: