Dragging and dropping mails from outlook to VB form

Posted on 2007-04-06
Last Modified: 2013-12-20
I am using VB 6 to develop applications. Here's what i want:

Need to drag mails directly from outlook and drop it in a vb form which extracts its attachments if any and other details.

The problem is i can do a drag drop of any other file on drive. Even if i save the mail as a mail item i can do following things, But i need to do this directly from outlook .  So i have my outlook open i just want to drag a mail from  there and drop it in my form
Question by:mandelia
  • 4
  • 4

Author Comment

ID: 18862725
Please do not give me solutions to read .msg files in VB.  I can do that already.  All i nead is to read mails directly from outlook dragged and dropped in a form.  The form then will organise its attachments.

These attachments again contain mail items. The program will ten open all these attachments and read any attachment each mail item ha. Havnt tried but i think i know a solution to that.
LVL 16

Expert Comment

ID: 18863762
Right.  The outlook dropped object is NOT the standard DataObject, but it DOES contain a pointer to the object.  I do not believe it is possible in VB6 to get at it, so I wrote a DLL in VC++ to do it, which I call from VB 6.  My VB 6 code is

Private Sub imgEMail_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
    Dim DObjectHelper As New DATAOBJECTHELPERLib.DObject
    Dim DObjectAddr As Long
    Dim Contents As String * 2000

    If (Data.GetFormat(-16370) = True) Then 'e-mail attachment
        MoveMemory DObjectAddr, ByVal ObjPtr(Data) + 16, 4
        Call DObjectHelper.SaveToFile(DObjectAddr, "C:\TempMsg.msg", Contents)
    End If
End Sub

DObjectHelper extracts the .msg content from the dropped object AND returns the first part of the body in the string Contents, C++ code forthcoming.
LVL 16

Expert Comment

ID: 18863801
This is the VC++6 code for the "SaveToFile" function.  I left my notes, comments, and false starts in, they might help you.

STDMETHODIMP CDObject::SaveToFile(long pDataObjectAddress, BSTR FileName, BSTR Contents)

      //Copies the passed DataObject to a file
      HRESULT   hr = S_OK;
      IDataObject *pDataObject;
      pDataObject = (IDataObject*) pDataObjectAddress; //cast the passed pointer into a *IDataObject

      //Get the clipboard
      // Important: these strings need to be non-Unicode (don't compile UNICODE)
      unsigned short cp_format_descriptor = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); //C0A7
      unsigned short cp_format_contents   = RegisterClipboardFormat(CFSTR_FILECONTENTS); //C0A6

      //Set up format structure for the descriptor and contents
      FORMATETC descriptor_format =
       {cp_format_descriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
      FORMATETC contents_format =  
       {cp_format_contents,   NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};

      // Check for contents format type
      hr = pDataObject->QueryGetData(&contents_format);
      if (hr != S_OK) return hr;

      // Get the descriptor information
      STGMEDIUM storage= {0,0,0};
      STGMEDIUM ContentStorage = {0,0,0};

      contents_format.lindex = 0;
      contents_format.tymed = TYMED_ISTORAGE;
      hr = pDataObject->GetData(&contents_format, &ContentStorage);
      if (hr != S_OK) return hr;

      //Save the passed storage to the filename passed
      IStorage *FileStg;
      //hr = StgCreateDocfile(L"C:\\mtest\\MyMsg.msg",STGM_READWRITE + STGM_CREATE +STGM_SHARE_EXCLUSIVE ,0,&FileStg);
      hr = StgCreateStorageEx(FileName,STGM_READWRITE + STGM_CREATE + STGM_SHARE_EXCLUSIVE ,STGFMT_STORAGE ,0,0,0,IID_IStorage ,(void**)&FileStg);
      //hr = StgCreateStorageEx(FileName,STGM_READWRITE + STGM_CREATE + STGM_TRANSACTED  ,STGFMT_STORAGE ,0,0,0,IID_IStorage ,(void**)&FileStg);
      if (hr != S_OK) return hr;

      hr = ContentStorage.pstg->CopyTo(0,0,0,FileStg);
      if (hr != S_OK) return hr;
      hr = FileStg->Commit(STGC_DEFAULT );
      if (hr != S_OK) return hr;


      //Find the contents of the mail message and return it.

      IEnumSTATSTG* enumstg;
      STATSTG stg;
      ULONG fetched;
      ULONG bRead;
      char buffer[2000];
      IStream* stream;

      //Go through every sub storage, MAPI names them according to the MAPI Tags in MAPITags.h
      //We want the want for PR_BODY_A (plain text body), which is named 1000001E, the value for PR_BODY_A
      fetched = PR_BODY; //1000001E
      fetched = PR_BODY_A; //1000001E
      fetched = PR_BODY_W; //1000001F

      while (fetched == 1)
            OutputDebugString("type: "); OutputDebugString(itoa(stg.type,buffer,10 ));
            OutputDebugString(" Size: "); OutputDebugString(_i64toa(stg.cbSize.QuadPart ,buffer,10));
            OutputDebugStringW( stg.pwcsName);
            if (wcsstr(stg.pwcsName,L"1000001E"))
                  OutputDebugString(" Body ASCII ");
            if (stg.type == STGTY_STREAM)
                  if (wcsstr(stg.pwcsName,L"1000001E"))
                        //We found the right substorage, open it and read it into a buffer
                        OutputDebugString(" contents: ");
                        hr = FileStg->OpenStream(stg.pwcsName,NULL,STGM_READ + STGM_SHARE_EXCLUSIVE,0,&stream);
                        if (hr != S_OK) return hr;
                        if (hr != S_OK) return hr;

                        hr = stream->Release();
                        if (hr != S_OK) return hr;

                        //BSTR blah = BSTR(buffer);

                        //now save it to the passed VB String space

                        //pContents = SysAllocString( blah);
                        //pContents = &blah;

                        //pContents = blah.Copy();
                        //pContents = BSTR(buffer).Copy();
      hr = enumstg->Release();
      if (hr != S_OK) return hr;
      hr = FileStg->Commit(STGC_DEFAULT );
      if (hr != S_OK) return hr;

      hr = FileStg->Release();
      return hr;

//I tried to open a IMessage on the IStorage to use the MAPI functions to get the contents, but
//MAPIInitialize was so slow, I figured out which substg was the contents and returned that
//      hr = ::MAPIInitialize(NULL);
//      CoGetMalloc(MEMCTX_TASK, &g_pIMalloc);
//      LPMALLOC pMalloc = MAPIGetDefaultMalloc();

//      hr = FileStg->Commit(STGC_DEFAULT );

//      hr = ::OpenIMsgOnIStg(NULL,::MAPIAllocateBuffer,::MAPIAllocateMore,::MAPIFreeBuffer,pMalloc,NULL,FileStg,NULL,0,IMSG_NO_ISTG_COMMIT,&pimsg);

      //BSTR* MyBstr;
//      pimsg -> SaveChanges(KEEP_OPEN_READWRITE );
//SPropTagArray PropArray[0];
//      PropArray[0].aulPropTag = PR_BODY_A;
//      PropArray[0].aulPropTag
//      pimsg -> GetProps(
      //LPMESSAGE pMessage;
      //pMessage = pimsg->IMAPIProp;

//typedef enum tagSTGTY
//  STGTY_STORAGE      = 1,
//  STGTY_STREAM       = 2,
//  STGTY_PROPERTY     = 4
//} STGTY;


3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

LVL 16

Expert Comment

ID: 18863818
>>Please do not give me solutions to read .msg files in VB.  I can do that already.

How do you do it?  My way is a pain in the butt.

Author Comment

ID: 18865232
well mine is not that big.  will paste it here tuesday. Does not have it handy

Author Comment

ID: 18865254
Thanks for this let me try it out. Really need to get this. If this works out Dinner is due

Author Comment

ID: 18944546
Hi,  Sorry was  a bit stuck up.  Dude can you do me a favour. Is there a way you can send the compiled  "DLL"  to me.

By the way here's my code to process .mg Files:

Private Function getMailMessage(ByVal FileName As String) As String

    If Dir$(FileName) = "" Then
        'nothing to read
        getMailMessage = "File " & FileName & " not found"
        Exit Function
    End If
    Set outDraftFolder = OutApp.GetNamespace("MAPI").GetDefaultFolder(olFolderDrafts)
    Set outMsg = OutApp.CreateItemFromTemplate(FileName)
    Dim sText As String
    If TypeOf outMsg Is Outlook.MailItem Then
        With outMsg
            sText = "A mailItem:"
            sText = sText & vbCrLf & "sender =" & .SenderName
            sText = sText & vbCrLf & "Received = " & .ReceivedTime
            sText = sText & vbCrLf & "Created = " & .CreationTime
            sText = sText & vbCrLf & "subject = " & .Subject
            sText = sText & vbCrLf & "Body:" & vbCrLf
            sText = sText & vbCrLf & .Body
            outMsg.Attachments.Item(1).SaveAsFile "c:\abc"
         End With
    End If
    getMailMessage = sText
    Set outMsg = Nothing
    Set outDraftFolder = Nothing
End Function
LVL 16

Accepted Solution

JohnBPrice earned 500 total points
ID: 18945650
OK, source is in
Compiled DLL is in

BTW EE-Stuff is a site associated with Experts-Exchange for posting files.  Login with your EE login to get the files.  They are both zips.  There are probably extra files in the source, it's been years since I touched this code, so I just grabbed everything in my project top level folder.

Thanks for the code.  I tried the MAPI route, but the "Another App is trying to access your email" warning with the forced delay was unacceptable.  My emails were still in the User's outlook, dragged & dropped into VB6,  and not a separate .msg file.  Thus I abandoned the MAPI route and didn't think about using MAPI after I extracted the .msg file since I was already into the structured storage.  Maybe I should replace the second half which pulls the .msg and replace it with MAPI, at that point Outlook should no longer complain.

Featured Post

What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
MS Access - Capture pressed key onclick 4 33
Notepad++ how to remove delimiter : from beggning of the line? 3 105
Passing a Text Box name to a Sub 6 96
Child Form in front 4 47
Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question