Могу ли я перетащить элементы из Outlook в мое приложение SWT? - PullRequest
5 голосов
/ 07 октября 2011

Фон

Наше приложение на основе Eclipse RCP 3.6 позволяет людям перетаскивать файлы для хранения / обработки.Это прекрасно работает, когда файлы перетаскиваются из файловой системы, но не когда люди перетаскивают элементы (сообщения или вложения) непосредственно из Outlook.

Это происходит потому, что Outlook хочет передать нашему приложению файлы через FileGroupDescriptorW и FileContents, но SWT включает только тип FileTransfer.(В FileTransfer передаются только пути к файлам, при условии, что получатель может найти и прочитать их. Подход FileGroupDescriptorW / FileContents может передавать файлы непосредственно из приложения в приложение без записи временных файлов вдиск.)

Мы попытались создать подкласс ByteArrayTransfer, который мог бы принимать FileGroupDescriptorW и FileContents.Основываясь на некоторых примерах в Интернете, мы смогли получить и проанализировать FileGroupDescriptorW, который (как следует из названия) описывает файлы, доступные для передачи.(См. Рисунок кода ниже.) Но мы не смогли принять FileContents.

. Это происходит потому, что Outlook предлагает данные FileContents только как TYMED_ISTREAM или TYMED_ISTORAGE, но только SWTпонимает, как обмениваться данными как TYMED_HGLOBAL.Из них представляется, что TYMED_ISTORAGE будет предпочтительнее, поскольку неясно, как TYMED_ISTREAM может обеспечить доступ к содержимому нескольких файлов.

(У нас также есть некоторые опасения по поводу желания SWT выбирать и конвертировать толькоодин тип TransferData, учитывая, что нам нужно обработать два, но мы думаем, что мы могли бы как-то обойти это в Java: кажется, что все TransferData доступны в других точках процесса.)

Вопросы

Мы на правильном пути?Кому-нибудь уже удалось принять FileContents в SWT?Есть ли вероятность, что мы сможем обработать данные TYMED_ISTORAGE, не покидая Java (даже если создадим фрагментный патч для 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 's 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;

Спасибо.

Ответы [ 2 ]

2 голосов
/ 24 октября 2011

Вы смотрели на https://bugs.eclipse.org/bugs/show_bug.cgi?id=132514?

К этой записи bugzilla прикреплен патч (против довольно старой версии SWT), который может представлять интерес.

1 голос
/ 05 декабря 2013

даже если

//public static final int TYMED_ISTREAM = 4;

Попробуйте приведенный ниже код .. он должен работать

package com.nagarro.jsag.poc.swtdrag;

imports ... 

public class MyTransfer extends ByteArrayTransfer {
private static int BYTES_COUNT = 592;
private static int SKIP_BYTES = 72;

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) {
    String[] result = null;

    if (!isSupportedType(transferData) || transferData.pIDataObject == 0)
        return null;

    IDataObject data = new IDataObject(transferData.pIDataObject);
    data.AddRef();
    // Check for descriptor format type
    try {
        FORMATETC formatetcFD = transferData.formatetc;
        STGMEDIUM stgmediumFD = new STGMEDIUM();
        stgmediumFD.tymed = COM.TYMED_HGLOBAL;
        transferData.result = data.GetData(formatetcFD, stgmediumFD);

        if (transferData.result == COM.S_OK) {
            // Check for contents format type
            long hMem = stgmediumFD.unionField;
            long fileDiscriptorPtr = OS.GlobalLock(hMem);
            int[] fileCount = new int[1];
            try {
                OS.MoveMemory(fileCount, fileDiscriptorPtr, 4);
                fileDiscriptorPtr += 4;
                result = new String[fileCount[0]];
                for (int i = 0; i < fileCount[0]; i++) {
                    String fileName = handleFile(fileDiscriptorPtr, data);
                    System.out.println("FileName : = " + fileName);
                    result[i] = fileName;
                    fileDiscriptorPtr += BYTES_COUNT;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                OS.GlobalFree(hMem);
            }
        }
    } finally {
        data.Release();
    }
    return result;
}

private String handleFile(long fileDiscriptorPtr, IDataObject data) throws Exception {

    // GetFileName
    char[] fileNameChars = new char[OS.MAX_PATH];
    byte[] fileNameBytes = new byte[OS.MAX_PATH];
    COM.MoveMemory(fileNameBytes, fileDiscriptorPtr, BYTES_COUNT);
    // Skip some bytes.
    fileNameBytes = Arrays.copyOfRange(fileNameBytes, SKIP_BYTES, fileNameBytes.length);
    String fileNameIncludingTrailingNulls = new String(fileNameBytes, "UTF-16LE");
    fileNameChars = fileNameIncludingTrailingNulls.toCharArray();
    StringBuilder builder = new StringBuilder(OS.MAX_PATH);
    for (int i = 0; fileNameChars[i] != 0 && i < fileNameChars.length; i++) {
        builder.append(fileNameChars[i]);
    }
    String name = builder.toString();

    try {
        File file = saveFileContent(name, data);
        if (file != null) {
            System.out.println("File Saved @ " + file.getAbsolutePath());
            ;
        }
    } catch (IOException e) {
        System.out.println("Count not save file content");
        ;
    }

    return name;
}

private File saveFileContent(String fileName, IDataObject data) throws IOException {
    File file = null;
    FORMATETC formatetc = new FORMATETC();
    formatetc.cfFormat = typeIds[1];
    formatetc.dwAspect = COM.DVASPECT_CONTENT;
    formatetc.lindex = 0;
    formatetc.tymed = 4; // content.

    STGMEDIUM stgmedium = new STGMEDIUM();
    stgmedium.tymed = 4;

    if (data.GetData(formatetc, stgmedium) == COM.S_OK) {
        file = new File(fileName);
        IStream iStream = new IStream(stgmedium.unionField);
        iStream.AddRef();

        try (FileOutputStream outputStream = new FileOutputStream(file)) {

            int increment = 1024 * 4;
            long pv = COM.CoTaskMemAlloc(increment);
            int[] pcbWritten = new int[1];
            while (iStream.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) {
                byte[] buffer = new byte[pcbWritten[0]];
                OS.MoveMemory(buffer, pv, pcbWritten[0]);
                outputStream.write(buffer);
            }
            COM.CoTaskMemFree(pv);

        } finally {
            iStream.Release();
        }
        return file;
    } else {
        return null;
    }
}
}
...