新版Outlook邮件/附件拖拽至Delphi原生应用的处理方案求助
新版Outlook拖拽文件到Delphi应用的处理问题
问题背景
- 新版Outlook拖拽附件到Windows资源管理器时,文件会直接复制到目标位置;拖拽邮件时,确认安全警告后也会完成复制,文件来源为临时目录
%temp%\chrome_drag_<webview2进程pid>_<编号>\ - 在Delphi应用中实现
DropTarget后,枚举到拖拽数据包含以下格式:- DragContext
- DragImageBits
- chromium/x-renderer-taint
- CF_HDROP
- 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
相关产品推荐
相关产品推荐

