Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 5988915
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 22, 20262026-05-22T23:01:33+00:00 2026-05-22T23:01:33+00:00

I need to react on the user drag an drop action on my form.

  • 0

I need to react on the user drag an drop action on my form.
Accepting files from explorer was not difficult, but acceping OLE objects (Outlook E-Mail) drop is to difficult to me to handle myself.

So far I have a Delphi form with implemented IDropTarget interface.

  IDropTarget = interface(IUnknown)
    ['{00000122-0000-0000-C000-000000000046}']
    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;

Here is the Drop method implementation:

function TForm1.Drop(const dataObj: IDataObject; grfKeyState: Integer;
  pt: TPoint; var dwEffect: Integer): HResult;
var
   aFmtEtc: TFORMATETC;
   aStgMed: TSTGMEDIUM;
   pData: PChar;
 begin


   if (dataObj = nil) then
     raise Exception.Create('IDataObject-Pointer is not valid!');
   with aFmtEtc do
   begin
     cfFormat := CF_UNICODETEXT;
     ptd := nil;
     dwAspect := DVASPECT_CONTENT;
     lindex := 0;
     tymed := TYMED_ISTREAM Or TYMED_ISTORAGE;
   end;

   {Get the data}
   OleCheck(dataObj.GetData(aFmtEtc, aStgMed));
   try

     {Lock the global memory handle to get a pointer to the data}
     pData := GlobalLock(aStgMed.hGlobal);
     { Replace Text }
     Memo1.Text := pData;
   finally
     {Finished with the pointer}
     GlobalUnlock(aStgMed.hGlobal);
     {Free the memory}
     ReleaseStgMedium(aStgMed);
   end;
   Result := S_OK;
 end;

This implementation adds the preview of the message to the Memo.

How to save message recieved from Outlook onto the hard drive? I would apreciate samples in any language, even in the pseudo code.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-22T23:01:34+00:00Added an answer on May 22, 2026 at 11:01 pm

    i dealt with Outlook IDataObjects before.

    Outlook exposes the same CFSTR_FILEDESCRIPTOR / CFSTR_FILECONTENTS as regular shell objects from Explorer.

    When i enumerate the formats in the IDataObject i see them:

    0:  cfFormat: TClipFormat = "RenPrivateSourceFolder" (49972)
        tymed: Longint = 4  (TYMED_ISTREAM)
    
    1:  cfFormat: TClipFormat = "RenPrivateMessages" (49587)
        tymed: Longint = 4  (TYMED_ISTREAM)
    
    2:  cfFormat: TClipFormat = "RenPrivateItem" (49812)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    
    3:  cfFormat: TClipFormat = CFSTR_FILEDESCRIPTOR ("FileGroupDescriptor", 49281)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    
    4:  cfFormat: TClipFormat = CFSTR_FILEDESCRIPTORW ("FileGroupDescriptorW", 49280)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    
    5:  cfFormat: TClipFormat = "FileNameW" (49159)
        tymed: Longint = 2  (TYMED_FILE)
    
    6:  cfFormat: TClipFormat = CFSTR_FILECONTENTS ("FileContents", 49282)
        tymed: Longint = 12  (TYMED_ISTREAM, TYMED_ISTORAGE)
    
    7:  cfFormat: TClipFormat = "Object Descriptor" (49166)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    
    8:  cfFormat: TClipFormat = CF_TEXT (1)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    
    9:  cfFormat: TClipFormat = CF_UNICODETEXT (13)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    
    10: cfFormat: TClipFormat = "CSV" (49993)
        tymed: Longint = 1  (TYMED_HGLOBAL)
    

    i use Drag-Drop when dropping onto a VirtualStringTree, but the shell concepts are the same. i can repost snippets of my code (which eventually saves the Outlook e-mails to a database)

    function HandleOleDrop(DataObject: IDataObject; var DropEffect: TDropEffect;
        FileContentsCallback: TFileContentsCallback;
        FilenameCallback: TFilenameCallback;
        BitmapCallback: TBitmapCallback;
        StreamCallback: TStreamCallback): HResult;
    var
        enum: IEnumFORMATETC;
        OLEFormat: TFormatEtc;
        Fetched: Integer;
    begin
        //Go through the formats list in order, looking for the formats we understand.
        //The order they are given to us is the order the original application's idea
        //of better to worse
       Result := DataObject.EnumFormatEtc(DATADIR_GET, enum);
       if Failed(Result) then
          Exit;
    
       Result := enum.Reset;
       if Failed(Result) then
          Exit;
    
        while enum.Next(1, OLEFormat, @Fetched) = S_OK do
       begin
            OutputDebugString(PChar('[HelpDeskShellOperations.HandleOleDrop] ClipboardFormat = ' + ClipFormatToStr(OleFormat.cfFormat)));
          if OLEFormat.cfFormat = CF_FILEDESCRIPTOR then
        begin
                //Transfer data as if it were a file, regardless of how it is actually stored
                //If CF_FILEDESCRIPTOR is present, then a CF_FILECONTENTS must also be
                //present later on in the formats list
                Result := GetFileContents(DataObject, DropEffect, FileContentsCallback);
                Break;
            end
            else if OleFormat.cfFormat = CF_HDROP then
            begin
                Result := GetHDropContents(DataObject, DropEffect, FilenameCallback);
                Break;
            end
            else if OleFormat.cfFormat = CF_BITMAP then
            begin
                Result := GetBitmapContents(DataObject, DropEffect, BitmapCallback);
                Break;
            end;
    {       else if OleFormat.cfFormat = CF_PNGStream then
            begin
                Result := GetStreamContents(OleFormat.cfFormat, DataObject, 'untitled.png', DropEffect, StreamCallback);
                Break;
            end
            else if OleFormat.cfFormat = CF_GIFStream then
            begin
                Result := GetStreamContents(OleFormat.cfFormat, DataObject, 'untitled.gif', DropEffect, StreamCallback);
                Break;
            end
            else if OleFormat.cfFormat = CF_JFIFStream then
            begin
                Result := GetStreamContents(OleFormat.cfFormat, DataObject, 'untitled.jpeg', DropEffect, StreamCallback);
                Break;
            end;}
    
            //      else if Formats[i] = ... then
            //      begin
             //Handle another format
          //            Break;
          //        end
       end;
    end;
    

    The trick above is we enumerate the formats that IDataObject contains. We like CF_FILEDESCRIPTOR, and handle it specially:

    function GetFileContents(DataObject: IDataObject; var Effect: TDropEffect; FileContentsCallback: TFileContentsCallback): HResult;
    var
       hr: HResult;
       format: TFormatEtc;
       FileDescriptorMedium: TStgMedium;
       fgdGlobal: hGlobal; //global memory object where the descriptors are
       fgd: PFileGroupDescriptor; //descriptors
       Descriptors: PFileDescriptorArray; //helper to access descriptors dynamic array
       nItem: Integer;
       FileContentsMedium: TStgMedium;
       //   stm: IStream;
    begin
       //1. Extract the CFSTR_FILEDESCRIPTOR format as a TYMED_HGLOBAL value.
       ZeroMemory(@format, SizeOf(TFormatEtc));
       format.cfFormat := CF_FILEDESCRIPTOR;
       format.dwAspect := DVASPECT_CONTENT;
       format.tymed := TYMED_HGLOBAL;
       ZeroMemory(@FileDescriptorMedium, SizeOf(FileDescriptorMedium));
    
       Result := DataObject.GetData(format, FileDescriptorMedium); //Free the medium at the end
       if Failed(Result) then
          Exit;
       try
          //2. The hGlobal member of the returned STGMEDIUM structure points to a
              //   global memory object. Lock that object by passing the hGlobal value
              //   to GlobalLock.
          fgdGlobal := FileDescriptorMedium.hGlobal;
          fgd := GlobalLock(fgdGlobal);
          try
             //3. Cast the pointer returned by GlobalLock to a FILEGROUPDESCRIPTOR pointer.
             //   It will point to a FILEGROUPDESCRIPTOR structure followed by one or more
             //   FILEDESCRIPTOR structures. Each FILEDESCRIPTOR structure contains a
             //   description of a file that is contained by one of the accompanying
             //   CFSTR_FILECONTENTS formats.
             Descriptors := Addr(fgd.fgd[0]);
    
             //4. Examine the FILEDESCRIPTOR structures to determine which one corresponds
             //   to the file you want to extract. The zero-based index of that
             //   FILEDESCRIPTOR structure is used to identify the file's
             //   CFSTR_FILECONTENTS format. Because the size of a global memory block is
             //   not byte-precise, use the structure's nFileSizeLow and nFileSizeHigh
             //   members to determine how many bytes represent the file in the global
             //   memory object.
             for nItem := 0 to fgd.cItems - 1 do
             begin
                //5. Call IDataObject::GetData with the cfFormat member of the FORMATETC
                //   structure set to the CFSTR_FILECONTENTS value and the lIndex member
                //   set to the index that you determined in the previous step. The tymed
                //   member is typically set to TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE.
                //   The data object can then choose its preferred data transfer mechanism.
                ZeroMemory(@format, SizeOf(TFormatEtc));
                format.cfFormat := CF_FILECONTENTS;
                format.dwAspect := DVASPECT_CONTENT;
                format.lindex := nItem;
                format.tymed := TYMED_HGLOBAL or TYMED_ISTREAM or TYMED_ISTORAGE;
                //NOTE: IStorage based files are a bitch to deal with
                ZeroMemory(@FileContentsMedium, SizeOf(FileContentsMedium));
    
                //Now get the file's contents
                hr := DataObject.GetData(format, FileContentsMedium);
                if Failed(hr) then
                   Continue;
                try
                   {
                    Progress: nItem+1 of fgd.cItems
                   }
    
                           //6. The STGMEDIUM structure that IDataObject::GetData returns will
                           //   contain a pointer to the file's data. Examine the tymed member of
                           //   the structure to determine the data transfer mechanism.
    
                   { i now have afile's name/size/date/etc in
                     Descriptors[nItem]
                    and the guts of file in
                     FileContentsMedium
                    (which could actually be in an hGlobal, IStream or IStorage.
                    Use ConvertStorageMediumToIStream to convert whatever it is into an IStream)
                   }
              //  stm := ConvertStorageMediumToIStream(FileContentsMedium);
    
                   if Assigned(FileContentsCallback) then
                      FileContentsCallback(Descriptors[nItem], FileContentsMedium, nItem + 1, fgd.cItems);
                finally
                   //Call ReleaseStgMedium to release the global memory object.
                   ReleaseStgMedium(FileContentsMedium);
                end;
             end;
          finally
             GlobalUnlock(fgdGlobal);
          end;
       finally
          ReleaseStgMedium(FileDescriptorMedium);
       end;
    end;
    

    And my application has a callback so it can put the file where it wants. In my case i stuff the file into an object so it can be saved to a database later:

    function TfrmTicketDetail.FileContentsCallback(const Descriptor: TFileDescriptor; const medium: TStgMedium; Progress: Integer; MaxProgress: Integer): Boolean;
    var
        szFilename: string;
        Attachment: TAttachment;
        ListItem: TVirtualListItem;
        stm: IStream;
        vclStream: TStream;
    begin
        try
            szFilename := descriptor.cFilename;
    
            Attachment := TAttachment.Create(dmodGlobal.ADOConnection);
    
            Attachment.RecordID := ToolKit.CreateGUID;
            Attachment.AutoGenerateRecordID := false;
            Attachment.setStateNew;
            Attachment.PrevState := Attachment.State;
            Attachment.IsLink := False;
    
            {Helpdesk attachments don't let you save the "date" of the attachment}
            if (descriptor.dwFlags and FD_CREATETIME) = FD_CREATETIME then
                Attachment.CreationDate := FileTimeToDateTime(descriptor.ftCreationTime)
            else if (descriptor.dwFlags and FD_WRITESTIME) = FD_WRITESTIME then
                Attachment.CreationDate := FileTimetoDateTime(descriptor.ftLastWriteTime)
            else
                Attachment.CreationDate := Now;
    
            Attachment.FileName := ExtractFileName(szFilename);
            Attachment.FullPath := ExtractFilePath(szFilename);
            Attachment.Extension := ExtractFileExt(szFilename);
            Attachment.Version := '';
            Attachment.Comment := '';
            Attachment.TicketGUID := StringToGUID(GUID);
            Attachment.UserGUID := StringToGUID(dmodGlobal.UserGUID);
    
            stm := ConvertStorageMediumToIStream(medium);
            vclStream := TOleStream.Create(stm);
            Attachment.FileStream := vclStream;
    
            {Most often they don't fill in the file size structure member,
             so we have to go to the contents to find the size.}
            if (descriptor.dwFlags and FD_FILESIZE) = FD_FILESIZE then
            begin
                Attachment.Size := (Descriptor.nFileSizeLow and $7FFFFFFF);
                //Yes, i know, ignoring the upper 32-bits, and throwing away the "sign" bit.
                //If you wanna change the database to handle an unsigned 64-bit, you BE MY GUEST - jerk wad
                //descriptor.nFileSizeLow or (descriptor.nFileSizeHigh shl 32)
            end
            else
            begin
                Attachment.Size := Integer(GetIStreamSize(stm) and $7FFFFFFF);
            end;
    
            //Add it to the listivew
            ListItem := lvAttachments.Items.Add;
            ListItem.Data := Attachment;
            olAttachments.Add(Attachment);
            TAttachment.RefreshAttachmentListItem(ListItem);
            FFlags.SetFlags(FLAG_MODIFIED);
            SetStatus(STATUS_MODIFIED);
    
            Result := True;
        except
            on e: Exception do
            begin
                Result := False;
                MessageDlg('New Attachment Failed: ' + ToolKit.CRLF + e.Message, mtError, [mbOK], 0);
            end;
        end;
    end;
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I am accepting user text in a form and echoing it back on the
I need to copy hundreds of gigs of random files around on my computer
I need to send hundreds of newsletters, but would like to check first if
I have a user control with several child controls. I need the user interface
i have a input tag which is non editable, but some times i need
Basically I need something to determine if the user in the process of a
Need a function that takes a character as a parameter and returns true if
Need a way to allow sorting except for last item with in a list.
Need to an expression that returns only things with an I followed by either
Need to locate the following pattern: The letter I followed by a space then

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.