First of all, if you can find a ready-made library that does this out of the box (for example, suggested by ldsandon ), use it because it all becomes painful and frustrating by hand. The documentation is sometimes incomplete and may contain errors: you will end up working with trial versions and an error, and Google will not save you, because not many people are immersed in the depth of dragging Ole, and most of them who are likely to use ready-made code.
How to do it in regular Pascal
Theoretically, the API that used to make your application process OLE drops is very simple. All you have to do is provide an implementation of the IDropTarget interface that does what you need and call RegisterDragDrop , providing a handle for your application window and interface.
This is what my implementation looks like:
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;
The implementation of DragEnter , DragOver and DragLeave trivial, assuming that I am doing this for an experiment: I will just accept everything:
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;
The real work will be done in 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;
This code accepts "Drop", searches for some CF_FILECONTENTS, opens it as TYMED_ISTORAGE, transfers each stream in this repository to a file in C:\Temp\<stream_name>.bin ; I tried this with Delphi 2010 and Outlook 2007, it works fine: opening these saved files (there are a lot of them!) I can find everything from the email in an unexpected way. I'm sure there is somewhere documentation that explains what each of these files should contain, but I really don't care about accepting dragged files from Outlook, so I did not look far. Again, the ldsandon link looks promising.
These codes look pretty short, but this is not a source of difficulty. The documentation for this was really not enough; I hit road blocks at every corner, starting with this:
F.lindex := 0{-1}; // Documentation says -1, practice says "0"
Msdn document clear says that the only valid value for "lindex" is -1: guess what, -1 doesn't work, 0 does!
Then there is this short line of code:
Response := Storage.OpenStream(ST_TAG.pwcsName, nil, STGM_READ or STGM_SHARE_EXCLUSIVE, 0, Stream);
in particular, these two constants:
STGM_READ or STGM_SHARE_EXCLUSIVE
getting this combination was a matter of trial and error. I donβt like trial and error: is this the optimal combination of flags for what I want? Will this work on every platform? I dont know...
The question then becomes about making the headers or tails of the actual content obtained from Outlook. For example, a SUBJECT email message was found in this stream: __substg1.0_800A001F . The body of the message was found in this thread: __substg1.0_1000001F . For a simple email message, I received 59 non-zero sized threads.