You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

新版Outlook邮件/附件拖拽至Delphi原生应用的处理方案求助

新版Outlook拖拽文件到Delphi应用的处理问题

问题背景

  • 新版Outlook拖拽附件到Windows资源管理器时,文件会直接复制到目标位置;拖拽邮件时,确认安全警告后也会完成复制,文件来源为临时目录 %temp%\chrome_drag_<webview2进程pid>_<编号>\
  • 在Delphi应用中实现DropTarget后,枚举到拖拽数据包含以下格式:
    1. DragContext
    2. DragImageBits
    3. chromium/x-renderer-taint
    4. CF_HDROP
    5. Chromium Web Custom MIME Data Format
  • 可解析最后一种格式获取ID,但不想通过Microsoft Graph下载文件,希望直接像资源管理器那样获取本地文件路径
  • 尝试使用CF_HDROP格式时,dataObj.GetData返回DV_E_FORMATETC错误,但dataObj.QueryGetData返回S_OK,说明格式本身被支持

原代码片段

procedure TSACustomDropTarget.DoDrop(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer);
var
  lFormat: TFormatEtc;
  lMedium: TStgMedium;
begin
  lFormat.cfFormat := CF_HDROP;
  lFormat.ptd := nil;
  lFormat.dwAspect := DVASPECT_CONTENT;
  lFormat.lindex := -1;
  lFormat.tymed := TYMED_HGLOBAL;

  if dataObj.GetData(lFormat, lMedium) = S_OK then // Returns DV_E_FORMATETC, but dataObj.GetQueryData(lFormat) returns S_OK
  begin
    // Get files using DragQuery...
  end;
end;

解决方案

核心问题分析

QueryGetData返回S_OK说明CF_HDROP格式被支持,但GetData失败,是因为手动指定的TFormatEtc参数(尤其是tymed字段)与Outlook实际提供的格式不匹配。新版Outlook基于WebView2,可能使用TYMED_ISTREAM而非TYMED_HGLOBAL,或者支持多种tymed类型。

修改后的实现代码

通过枚举支持的CF_HDROP格式参数,获取正确的tymed值后再调用GetData:

procedure TSACustomDropTarget.DoDrop(const dataObj: IDataObject; grfKeyState: Integer; pt: TPoint; var dwEffect: Integer);
var
  lFormat: TFormatEtc;
  lMedium: TStgMedium;
  EnumFmt: IEnumFORMATETC;
  Fetched: LongWord;
begin
  // 枚举所有支持的数据格式,筛选出CF_HDROP对应的正确参数
  if dataObj.EnumFormatEtc(DATADIR_GET, EnumFmt) = S_OK then
  begin
    while EnumFmt.Next(1, lFormat, Fetched) = S_OK do
    begin
      if lFormat.cfFormat = CF_HDROP then
      begin
        if dataObj.GetData(lFormat, lMedium) = S_OK then
        begin
          try
            case lMedium.tymed of
              // 处理HGLOBAL类型的CF_HDROP
              TYMED_HGLOBAL:
                begin
                  if lMedium.hGlobal <> 0 then
                  begin
                    const FileCount = DragQueryFile(lMedium.hGlobal, $FFFFFFFF, nil, 0);
                    for var I := 0 to FileCount - 1 do
                    begin
                      const BufLen = DragQueryFile(lMedium.hGlobal, I, nil, 0);
                      var FilePath := string.Create(BufLen, #0);
                      DragQueryFile(lMedium.hGlobal, I, PChar(FilePath), BufLen + 1);
                      // 这里处理获取到的文件路径
                    end;
                  end;
                end;
              // 处理IStream类型的CF_HDROP
              TYMED_ISTREAM:
                begin
                  var DropBuffer: array[0..MAX_PATH * 10] of Char;
                  var BytesRead: LongWord;
                  lMedium.pstm.Read(@DropBuffer, SizeOf(DropBuffer), BytesRead);
                  
                  const FileCount = DragQueryFile(THandle(@DropBuffer), $FFFFFFFF, nil, 0);
                  for var I := 0 to FileCount - 1 do
                  begin
                    const BufLen = DragQueryFile(THandle(@DropBuffer), I, nil, 0);
                    var FilePath := string.Create(BufLen, #0);
                    DragQueryFile(THandle(@DropBuffer), I, PChar(FilePath), BufLen + 1);
                    // 这里处理获取到的文件路径
                  end;
                end;
            end;
          finally
            // 释放StgMedium资源
            ReleaseStgMedium(lMedium);
          end;
          Break;
        end;
      end;
    end;
  end;
end;

备选思路

如果上述方法仍有问题,可尝试解析DragContext格式的数据,该格式可能包含临时文件的路径信息,但需要自行解析其内部结构;不过这种方式稳定性不如CF_HDROP,不推荐作为首选。

内容的提问来源于stack exchange,提问作者The_Fox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:31:10