Как внедрить перетаскивание из Outlook Mail или Thunderbird в форму Delphi?

StackOverflow https://stackoverflow.com/questions/4756845

Вопрос

Кто -нибудь уже реализовал перетаскивание сообщений электронной почты из Outlook и/или Thunderbird (С этого момента "OT") в форму Delphi.

Мне нужно дать пользователю способ сохранить важные электронные письма в моей базе данных приложений без написания плагинов OT. В настоящее время они используют эту технику:

  1. от OT они нажимают по электронной почте,
  2. сохранить как...
  3. Сохранить на рабочем столе или папке Temp,
  4. Перетащите и оставьте сохраненный файл в форму Delphi.

В то время как после модификации я хочу сделать:

  1. от OT они нажимают по электронной почте,
  2. Перетащите и оставьте сохраненный файл в форму Delphi.

Так что в основном я реализовал Drag & Drop от Explorer. Мне нужен дополнительный слой, который позволяет моему приложению видеть электронную почту изначально на OT как обычный файл, поэтому я могу перетащить OT, как будто это было обычное окно Explorer.

Примечание: мне не нужно поддерживать все версии OT. Я могу не поддерживать Outlook 2003 (например), но не 2010 год. Поэтому в случае, если метод не будет работать автоматически для всех версий OT, я предпочелю тот, который работает с последними.

Заключительное примечание: в любом случае это очевидно, что я заинтересован только в перетаскивании и сбросе электронных писем (а не в календаре Outlook, например). Идея тоже будет перетаскивать и сбрасывать вложения. Но это может быть дополнительным улучшением на будущее.

Это было полезно?

Решение

Вы должны использовать Ole Drag & Drop, но тогда вы должны иметь возможность обрабатывать данные, которые вы получаете, потому что каждое приложение может хранить данные в своем собственном формате. Вы можете найти хорошую реализацию Delphi Ole Drag & Drop здесь

Outlook Express и Thunderbird должны перенести данные в формате RFC2822 или что -то в этом роде, Outlook, вероятно, будет передавать данные в своем собственном формате сообщения, он был задокументирован как часть Microsoft Open Specification Program

Другие советы

Прежде всего, если вы можете найти готовую библиотеку, которая делает это из коробки (как предложено Лдсандон) использовать его, потому что делая все это рукой больно и разочаровывает. Документация иногда неполна и может содержать ошибки: вы в конечном итоге будете делать что-то по пробным и ошибкам, и Google не спасет вас, потому что не многие люди введены в глубину перетаскивания OLE, и большинство из них Это, вероятно, будет использовать готовый код.

Как сделать это в простом Паскале

Теоретически API, который используется для того, чтобы ваше приложение было очень просто. Все, что вам нужно сделать, это предоставить реализацию IDropTarget интерфейс, который делает то, что вам нужно, и звонит RegisterDragDrop Предоставление ручки для окна вашего приложения и интерфейса.

Вот как выглядит моя реализация:

  TDropTargetImp = class(TInterfacedObject, IDropTarget)
  public
    function DragEnter(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
    function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
    function DragLeave: HResult; stdcall;
    function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
  end;

Реализация DragEnter, DragOver а также DragLeave это тривиально, подумайте, что я делаю это для эксперимента: я просто приму все:

function TDropTargetImp.DragEnter(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
begin
  dwEffect := DROPEFFECT_COPY;
  Result := S_OK;
end;

function TDropTargetImp.DragLeave: HResult;
begin
  Result := S_OK;
end;

function TDropTargetImp.DragOver(grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
begin
  dwEffect := DROPEFFECT_COPY;
  Result := S_OK;
end;

Настоящая работа будет сделана в TDropTargetImp.Drop.

function TDropTargetImp.Drop(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
var iEnum: IEnumFORMATETC;
    DidRead:LongInt;
    F: TFormatEtc;
    STG:STGMEDIUM;
    Response:Integer;

    Stream:IStream;

    Storage: IStorage;
    EnumStg: IEnumStatStg;
    ST_TAG: STATSTG;

    FileStream: TFileStream;
    Buff:array[0..1023] of Byte;
begin
  if dataObj.EnumFormatEtc(DATADIR_GET, iEnum) = S_OK then
  begin

    {
    while (iEnum.Next(1, F, @DidRead) = S_OK) and (DidRead > 0) do
    begin
      GetClipboardFormatName(F.cfFormat, FormatName, SizeOf(FormatName));
      ShowMessage(FormatName + ' : ' + IntToHex(F.cfFormat,4) + '; lindex=' + IntToStr(F.lindex));
    end;
    }

    ZeroMemory(@F, SizeOf(F));
    F.cfFormat := $C105; // CF_FILECONTENTS
    F.ptd := nil;
    F.dwAspect := DVASPECT_CONTENT;
    F.lindex := 0{-1}; // Documentation says -1, practice says "0"
    F.tymed := TYMED_ISTORAGE;

    Response := dataObj.GetData(F, STG);
    if Response = S_OK then
      begin
        case STG.tymed of
          TYMED_ISTORAGE:
            begin
              Storage := IStorage(STG.stg);
              if Storage.EnumElements(0, nil, 0, EnumStg) = S_OK then
              begin
                while (EnumStg.Next(1, ST_TAG, @DidRead) = S_OK) and (DidRead > 0) do
                begin
                  if ST_TAG.cbSize > 0 then
                  begin
                  Response := Storage.OpenStream(ST_TAG.pwcsName, nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, Stream);
                  if Response = S_OK then
                    begin
                      // Dump the stored stream to a file
                      FileStream := TFileStream.Create('C:\Temp\' + ST_TAG.pwcsName + '.bin', fmCreate);
                      try
                        while (Stream.Read(@Buff, SizeOf(Buff), @DidRead) = S_OK) and (DidRead > 0) do
                          FileStream.Write(Buff, DidRead);
                      finally FileStream.Free;
                      end;
                    end
                  else
                    case Response of
                      STG_E_ACCESSDENIED: ShowMessage('STG_E_ACCESSDENIED');
                      STG_E_FILENOTFOUND: ShowMessage('STG_E_FILENOTFOUND');
                      STG_E_INSUFFICIENTMEMORY: ShowMessage('STG_E_INSUFFICIENTMEMORY');
                      STG_E_INVALIDFLAG: ShowMessage('STG_E_INVALIDFLAG');
                      STG_E_INVALIDNAME: ShowMessage('STG_E_INVALIDNAME');
                      STG_E_INVALIDPOINTER: ShowMessage('STG_E_INVALIDPOINTER');
                      STG_E_INVALIDPARAMETER: ShowMessage('STG_E_INVALIDPARAMETER');
                      STG_E_REVERTED: ShowMessage('STG_E_REVERTED');
                      STG_E_TOOMANYOPENFILES: ShowMessage('STG_E_TOOMANYOPENFILES');
                      else
                        ShowMessage('Err: #' + IntToHex(Response, 4));
                    end;
                  end;
                end;
              end;
            end
          else
            ShowMessage('TYMED?');
        end;
      end
    else
      case Response of
        DV_E_LINDEX: ShowMessage('DV_E_LINDEX');
        DV_E_FORMATETC: ShowMessage('DV_E_FORMATETC');
        DV_E_TYMED: ShowMessage('DV_E_TYMED');
        DV_E_DVASPECT: ShowMessage('DV_E_DVASPECT');
        OLE_E_NOTRUNNING: ShowMessage('OLE_E_NOTRUNNING');
        STG_E_MEDIUMFULL: ShowMessage('STG_E_MEDIUMFULL');
        E_UNEXPECTED: ShowMessage('E_UNEXPECTED');
        E_INVALIDARG: ShowMessage('E_INVALIDARG');
        E_OUTOFMEMORY: ShowMessage('E_OUTOFMEMORY');
        else
         ShowMessage('Err = ' + IntToStr(Response));
      end;

  end;
  Result := S_OK;
end;

Этот код принимает «DROP», ищет некоторые CF_FILECONTENTERS, открывает его как TYMED_ISTORGE, бросает каждый поток в этом хранилище в файл в C:\Temp\<stream_name>.bin; Я попробовал это с Delphi 2010 и Outlook 2007, все работает хорошо: открытие этих сохраненных файлов (их много!) Я могу найти все из сообщения электронной почты, неожиданным образом. Я уверен, что где-то есть документация, которая точно объясняет, что должен содержать каждый из этих файлов, но мне не волнует принимать файлы перетаскивания из Outlook, поэтому я не смотрел далеко. Опять таки, Ldsandon's Ссылка выглядит многообещающе.

Эти коды выглядят довольно коротко, но это не источник трудностей. Документация для этого действительно отсутствовала; Я попадаю в дорожные блоки на каждом углу, начиная с этого:

F.lindex := 0{-1}; // Documentation says -1, practice says "0"

Документация MSDN Clear говорит, что единственное допустимое значение для «Lindex» --1: угадайте, что -1 не работает, 0 делает!

Тогда есть эта короткая строка кода:

Response := Storage.OpenStream(ST_TAG.pwcsName, nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, Stream);

В частности, эти два констапирования:

STGM_READ or STGM_SHARE_EXCLUSIVE

Получение этой комбинации было вопросом проб и ошибок. Я не люблю проб и ошибок: это оптимальная комбинация флагов для того, что я хочу? Будет ли это работать на каждой платформе? Я не знаю...

Тогда есть вопрос создания головок или хвоста фактического содержания, полученного от Outlook. Например, предмет электронной почты была найдена в этом потоке: __substg1.0_800A001F. Анкет Тело сообщения было найдено в этом потоке: __substg1.0_1000001F. Анкет Для простого сообщения электронной почты я получил 59 потоков ненулевого размера.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top