Drag&Drop from Outlook (Express) - Possible?

Just starting out? Need help? Post your questions and find answers here.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Drag&Drop from Outlook (Express) - Possible?

Post by srod »

Yes you can customise the drop completely via the DropTarget_SetEffects() function. Examine the value of *pdwEffect\l to see what drag actions the source allows; it will usually be a combination of #DROPEFFECT_COPY and #DROPEFFECT_MOVE (though there are others) which are the same as #PB_Drag_Copy and #PB_Drag_Move.

In the case of Outlook then, it sounds as if it always sets this value to #DROPEFFECT_COPY on entry to the various drop methods. No reason why you can't allow the drop even if the control key is not down, just ignore the value of grfKeyState.
I may look like a mule, but I'm not a complete ass.
User avatar
captain_skank
Enthusiast
Enthusiast
Posts: 639
Joined: Fri Oct 06, 2006 3:57 pm
Location: England

Re: Drag&Drop from Outlook (Express) - Possible?

Post by captain_skank »

Thunder93 wrote:To drag an email entry from the email program and drop onto the Desktop or Windows Explorer screen, the saved file is of the raw data. As already stated, the attachments data resides in the raw data.

When an email is dropped, it is using a name just extracted from the email subject (except illegal characters usually replaced with something.)


Less is more.

Code: Select all

If OpenWindow(0, 0, 0, 700, 500, "Drag 'n' drop Outlook Express",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  ListIconGadget(1,  10, 10, 680, 100, "Drop Email here", 730)
  cf_email = RegisterClipboardFormat_("Internet Message (rfc822/rfc1522)")
  EnableGadgetDrop(1, cf_email, #PB_Drag_Copy)

  EnableGadgetDrop(1, #PB_Drop_Files, #PB_Drag_Copy)
  EditorGadget(2, 10, 120, 680, 370) 
  Repeat 
    event = WaitWindowEvent()
    If event = #PB_Event_GadgetDrop And EventGadget() = 1
      Select EventDropType()
        Case cf_email
          ClearGadgetItems(1)
          ClearGadgetItems(2)
          *buffer = EventDropBuffer()
          msg$ = PeekS(*buffer)
          subject = FindString(msg$, "Subject: ", 1) + 9
          eos = FindString(msg$, #CRLF$, subject)
          subject$ = Mid(msg$, subject, eos - subject)          
          AddGadgetItem(1, 0, "Subject: " +subject$)
          AddGadgetItem(2, 0, msg$)          
          
      EndSelect
    EndIf
  Until event = #PB_Event_CloseWindow
EndIf 
End

I couldn't get this to work as expected, but perhaps I misunderstood what it was supposed to do.

Using Outlook 2010, I can drag message from the Mailbox listing pane in outlook directly to the editor gadget but not the listicongadget, and it will only get the message info as listed in the listing pane - not the message text.

If i drag the message to the desktop and then try and drag that to this window then that wont work at all.

What i'd like to do is drag a message from Outlook's mail box listing pane and drop it into a pb window but retreive the header and body of the mail as text ( I don't need the attachments ), anyone know if this is possible ? if not then I'm ging to have to look at oulook's VBA and I'd rather cut off my left testicle :(
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: Drag&Drop from Outlook (Express) - Possible?

Post by Thunder93 »

Hi captain_skank.

That was just demonstration code that worked for Outlook Express.., but as it was already determined, Microsoft Outlook (non-Express) does things differently.

I don't use Microsoft Outlook. But with PB DragDrop Lib, and with the right registers, it should work.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Drag&Drop from Outlook (Express) - Possible?

Post by mesozorn »

Well, I have made a bit more progress with regular Outlook, but at this point am getting some extremely odd results. I'm sure I'm doing something wrong but cannot for the life of me figure out what.

I am able to detect and successfully process a FileGroupDescriptor and extract it to a StgMedium using the following code:

Code: Select all

With thisFormatEtc
            \cfFormat = CF_FILEGROUPDESCRIPTOR
            \ptd =#Null
            \dwAspect = #DVASPECT_CONTENT
            \lindex = -1
            \tymed = #TYMED_HGLOBAL
EndWith
If Dataobject\GetData(thisFormatEtc, thisStgMedium) = #S_OK
   Debug "Okay"
EndIf
From that point I can also correctly test for the number of items/files/attachments dropped, using this:

Code: Select all

*fgdbuffer.filegroupdescriptor=thisStgMedium\hGlobal
Debug PeekU(*fgdbuffer\cItems)
...and indeed if I have dropped 1 attachment, it will show 1. If I drop 3 I'll get 3 as the value the cItems member of the FileGroupDescriptor structure, just as expected.

The odd parts come next, when first I try:

Code: Select all

Debug PeekS(@*fgdbuffer\fgd[0]\cFileName)
...to examine the filename of the first file dropped. It is always blank and never shows anything. Even stranger, if I examine another member of the FileDescriptor array such as:

Code: Select all

Debug PeekL(@*fgdbuffer\fgd[0]\nFileSizeHigh)
...I get a number.. okay fine. Now if I drop the exact same file attachment onto the window again, during the same instance/session, this number (nFileSizeHigh) increases by exactly 8, every single time. So that if the first time it's 4567354, the second time it will be 4567362, and then 4567370, and so on... If this were truly information about the file itself, I'd expect it to remain the same each time, rather than increasing by increments of 8. This is also true with any other numerical member of the filedescriptor array that I test. It's always a number that increases by 8 every time I repeat the drop during the same session.

What's equally bizarre, this number will keep increasing by 8 even if I select a totally different file attachment and drop it onto the window. It's the same number as before, increased by 8, even though it was a different file altogether last time.

Any idea what I'm doing wrong to retrieve both the filename and these other properties?
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Drag&Drop from Outlook (Express) - Possible?

Post by srod »

Your problem is here :

Code: Select all

*fgdbuffer.filegroupdescriptor=thisStgMedium\hGlobal
Debug PeekU(*fgdbuffer\cItems)
The hGlobal handle is not a memory pointer!

You have to use GlobalLock_() on the hGlobal which will return a memory pointer. Once you have this, copy it to some allocated memory. Something like :

Code: Select all

numBytes = GlobalSize_(thisStgMedium\hGlobal)
If numBytes 
  *ptr.FILEGROUPDESCRIPTOR = AllocateMemory(numBytes)
  If *ptr
    *ptrMem = GlobalLock_(thisStgMedium\hGlobal)
    If *ptrMem
      CopyMemory(*ptrMem, *ptr, numBytes)
      GlobalUnlock_(thisStgMedium\hGlobal)

      ;Now access the *ptr memory block which contains your FileGroupDescriptor structure.
        Debug "Num file descriptors = " + Str(*ptr\cItems)
        ;ETC.
    EndIf
    FreeMemory(*ptr)
  EndIf
EndIf
I may look like a mule, but I'm not a complete ass.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Drag&Drop from Outlook (Express) - Possible?

Post by mesozorn »

Ah okay I see, thanks... to me "memory handle" sounds extremely similar to "memory address/pointer" and the MSDN documentation under StgMedium doesn't exactly specify differently, so I was attacking it as such incorrectly. I was also thrown off by the fact that despite my erroneous approach, I was still always getting the correct value returned for the cItems member of FileGroupDescriptor, whenever I'd test by dropping multiple attachments onto the window.

I have used Globallock_() before when dealing with the creation and retrieval of Registry entries, but it's been a while and not something that jumped immediately to mind when working with an HGlobal.

On a completely separate note, I find it odd that:

Code: Select all

Structure FILEDESCRIPTOR
  dwFlags.l
  clsid.CLSID
  sizel.SIZE
  pointl.POINT
  dwFileAttributes.l
  ftCreationTime.FILETIME
  ftLastAccessTime.FILETIME
  ftLastWriteTime.FILETIME
  nFileSizeHigh.l
  nFileSizeLow.l
  cFileName.c[#MAX_PATH]
EndStructure

Structure FILEGROUPDESCRIPTOR
  cItems.l
  fgd.FILEDESCRIPTOR[0]
EndStructure

Debug SizeOf(filegroupdescriptor)
...only returns 4 as the total size of the FileGroupDescriptor structure, even when only static arrays are being used in each structure. But that's another matter with its own tangential explanation I suppose...
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Drag&Drop from Outlook (Express) - Possible?

Post by srod »

No 4 is absolutely right. The fgd[] static array field is literally a place-holder (depending on how you've defined the structure) because you don't know in advance how many elements will be placed into the array.

You can see it with a simple example :

Code: Select all

Structure test
  a.l
  b.POINT[0]
EndStructure

Debug SizeOf(test)  ;Outputs 4.
When you put a PB dynamic array into a structure (using ARRAY) then you are just throwing a pointer to an array into the structure which will of course add 4 bytes (x86) to the size of the structure. With a static array you physically allocate the memory for every array element in the structure itself. With [0] elements then, you allocate 0 bytes! :)

In your case, the method you call to retrieve the data returns a FILEGROUPDESCRIPTOR structure with a certain number of FILEDESCRIPTOR structures pasted on the end, and this is the key. No pointers are involved, the structures really are thrown on the end of our FILEGROUPDESCRIPTOR structure which we access through the fgd[] field.
I may look like a mule, but I'm not a complete ass.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Drag&Drop from Outlook (Express) - Possible?

Post by mesozorn »

I notice that if I do:

Code: Select all

Structure one
  a.l
  b.l
EndStructure

Structure two
  c.l
  d.one[0]
EndStructure

e.two\d[7]\a=55

...It does not throw an "Array Index Out of Bounds" error, even though I've specified zero elements in the static array yet am assigning a value to element number 7. But if I specify 2 or 3 elements, and then try to assign a value to element 7 as above, it gives that error. So I guess PB must treat a [0] element declaration within a structure as though it were a linked list, with an unspecified/unlimited number of elements.

Useful and interesting to know, even if not covered in the documentation.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Drag&Drop from Outlook (Express) - Possible?

Post by srod »

That is deliberate because Windows uses static arrays in exactly the scenarios you have encountered. Whenever an array is needed to be passed or returned, Windows will specify the use of a static array with zero elements (though it will use an index of [1] whereas PB uses [0]) because it cannot know the number of elements it will need / or return before hand. Hence, in these cases, we cannot have the compiler throwing element out of bounds errors. The PB manual does make mention of this in a roundabout way; referring to them as C++ structures etc.

Have a look at the following where such a construct is used to access the individual characters of a string. You can see with this why you don't want an array index out of bounds error. :)

Code: Select all

Structure byteReader
  c.c[0]
EndStructure

a$ = "Hello!"

*ptr.ByteReader = @a$

For i = 0 To Len(a$) - 1
  Debug Chr(*ptr\c[i])
Next
Last edited by srod on Thu Feb 20, 2014 8:46 pm, edited 1 time in total.
I may look like a mule, but I'm not a complete ass.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Drag&Drop from Outlook (Express) - Possible?

Post by davido »

@srod,

Thank you for the explanation. :D
I found the manual rather confusing on this point. Don't use C. Never will now I've found PureBasic!
DE AA EB
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Drag&Drop from Outlook (Express) - Possible?

Post by srod »

You're welcome Davido. :)
I may look like a mule, but I'm not a complete ass.
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: Drag&Drop from Outlook (Express) - Possible?

Post by Thunder93 »

To grab names of multiple dropped Outlook messages is the easy part.

Retrieving the data is the tricky part.

If you can't figure it out, you might be-able to work with the the location of the .PST file you can get from the clipboard and with the message name, get the msg contents you are seeking.


Regards to grabbing the names of multiple dropped Outlook messages. Nothing fancy but here's the code:

Code: Select all

Structure _FILEDESCRIPTOR
  dwFlags.l
  clsid.CLSID
  sizel.SIZE
  pointl.POINT
  dwFileAttributes.l
  ftCreationTime.FILETIME
  ftLastAccessTime.FILETIME
  ftLastWriteTime.FILETIME
  nFileSizeHigh.l
  nFileSizeLow.l
  cFileName.c[#MAX_PATH]
EndStructure

Structure _FILEGROUPDESCRIPTOR
  cItems.l
  fgd._FILEDESCRIPTOR[0]
EndStructure

Enumeration
  #ImageSource
  #ImageTarget
EndEnumeration

Enumeration
  #Window = 0
  #TargetOutlook
  #TargetMsG
EndEnumeration


If OpenWindow(#Window, 0, 0, 760, 510, "Drag & Drop", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  
  CreateImage(#ImageSource, 136, 136)
  If StartDrawing(ImageOutput(#ImageSource))
    Box(0, 0, 136, 136, $FFFFFF)
    DrawText(5, 5, "Drag this image", $000000, $FFFFFF)        
    For i = 45 To 1 Step -1
      Circle(70, 80, i, Random($FFFFFF))
    Next i        
    
    StopDrawing()
  EndIf  
  
  CreateImage(#ImageTarget, 136, 136)
  If StartDrawing(ImageOutput(#ImageTarget))
    Box(0, 0, 136, 136, $FFFFFF)
    DrawText(5, 5, "Drop images here", $000000, $FFFFFF)
    StopDrawing()
  EndIf  
  
  
  ListIconGadget(#TargetOutlook, 0, 0, 760, 150, "Outlook Messages" , 749)
  EditorGadget(#TargetMsG, 20, 160, 720, 330)
  
  ; rfc822_rfc1522 = RegisterClipboardFormat_("Internet Message (rfc822/rfc1522)")
  HTML = RegisterClipboardFormat_("HTML Format")
  
  cf_descrip = RegisterClipboardFormat_(#CFSTR_FILEDESCRIPTOR)
  cf_content = RegisterClipboardFormat_(#CFSTR_FILECONTENTS)
  
  EnableGadgetDrop(#TargetOutlook, #PB_Drop_Text, #PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link)
  EnableGadgetDrop(#TargetOutlook, #PB_Drop_Files, #PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link)
  ; EnableGadgetDrop(#TargetOutlook, #PB_Drop_Image, #PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link)
  
  If HTML : iFormat = HTML : ElseIf rfc822_rfc1522 : iFormat = rfc822_rfc1522 : EndIf
  
  If iFormat : EnableGadgetDrop(#TargetOutlook, iFormat, #PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link) : EndIf
  If cf_content : EnableGadgetDrop(#TargetOutlook, cf_content, #PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link) : EndIf
  If cf_descrip : EnableGadgetDrop(#TargetOutlook, cf_descrip, #PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link) : EndIf 
  
  
  dFormat.s
  
  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_Gadget And EventType() = #PB_EventType_DragStart
      
    ElseIf Event = #PB_Event_GadgetDrop
      Select EventGadget() 
        Case #TargetOutlook            
          
          Select EventDropType()
            Case #PB_Drop_Text
              ClearGadgetItems(#TargetOutlook) : ClearGadgetItems(#TargetMsG) 
              AddGadgetItem(EventGadget(), -1, "#PB_Drop_Text["+Str(EventDropSize())+"]: " + EventDropText())
              AddGadgetItem(#TargetMsG, -1,  EventDropText())
              
            Case #PB_Drop_Files : i = 0
              Files$ = EventDropFiles() : 
              Count  = CountString(Files$, Chr(10)) + 1
              For i = 1 To Count
                AddGadgetItem(EventGadget(), -1, "#PB_Drop_Files: " + StringField(Files$, i, Chr(10)))
              Next i           
              
            Case iFormat 
              ClearGadgetItems(#TargetOutlook) : ClearGadgetItems(#TargetMsG)              
              *Buffer = EventDropBuffer()  
              
              If HTML                          
                AddGadgetItem(EventGadget(), -1, "[iFormat]: HTML: " + PeekS(*Buffer, 30, #PB_Ascii))              
                AddGadgetItem(#TargetMsG, -1,  PeekS(*Buffer, EventDropSize(), #PB_Ascii))             
                
                
              ElseIf rfc822_rfc1522
                msg$ = PeekS(*Buffer, EventDropSize(), #PB_Ascii)
                Subject = FindString(msg$, "Subject: ", 1) + 9
                eos = FindString(msg$, #CRLF$, Subject)
                subject$ = Mid(msg$, Subject, eos - Subject)
                AddGadgetItem(EventGadget(), -1, "[iFormat]: rfc822_rfc1522: " + subject$)              
                AddGadgetItem(#TargetMsG, -1,  PeekS(*Buffer, EventDropSize(), #PB_Ascii))              
              Else
                MessageRequester("Default", "[iFormat]: Not Handled: "+Str(EventDropType()), #PB_MessageRequester_Ok | #MB_ICONINFORMATION)              
                If *Buffer : AddGadgetItem(#TargetMsG, -1,  PeekS(*Buffer, EventDropSize(), #PB_Ascii)) : EndIf              
              EndIf
              
              
            Case cf_content : MessageRequester("Hey", "#CFSTR_FILECONTENTS is working", #PB_MessageRequester_Ok | #MB_ICONINFORMATION)
              
            Case cf_descrip
              dFormat = "[cf_descrip]: "
              If HTML : dFormat  + "HTML: "
              ElseIf rfc822_rfc1522 : dFormat + "rfc822_rfc1522: "
              Else : dFormat + "???: "                
              EndIf
              
              *fgdBuffer._FILEGROUPDESCRIPTOR = EventDropBuffer()
              ItemsCount = *fgdBuffer\cItems
              
              ClearGadgetItems(#TargetOutlook) : ClearGadgetItems(#TargetMsG)
              
              For i=0 To ItemsCount-1
                eml$ = PeekS(@*fgdBuffer\fgd[i]\cFileName)
                AddGadgetItem(EventGadget(), -1, dFormat+eml$)
              Next
              
            Default              
              MessageRequester("Drag&Drop:", "Not Handled: "+Str(EventDropType()), #PB_MessageRequester_Ok | #MB_ICONINFORMATION)                
          EndSelect
          
      EndSelect
      
    EndIf
    
  Until Event = #PB_Event_CloseWindow
EndIf

End

Edited: Added another line of code under Case #PB_Drop_Text
Last edited by Thunder93 on Fri Feb 21, 2014 1:12 pm, edited 1 time in total.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Drag&Drop from Outlook (Express) - Possible?

Post by mesozorn »

Well, I am almost the whole way there and just one step away from saving the Outlook attachment data to a file on disk. The last stumbling block is how to invoke the READ operation on the iStream data object.

In regular WinAPI the function istream_read() is implemented as an independent function that takes three arguments:

http://msdn.microsoft.com/en-us/library ... 85%29.aspx

...the first argument being a pointer to the istream interface which is to act as the source and have its data read. However in PB, the \Read method is baked into the istream interface directly, so that you can use:

mystream.istream\Read(a,b,c)

But as you see it still accepts three arguments, and I'm not sure what it wants as the first one. It certainly doesn't accept a pointer or reference to itself as the read-source, so that argument placeholder almost seems superfluous and unnecessary. I've tried using self-pointers just to test, with no luck. I've tried other values like #Null but still no dice.

Again I'm sure it's something rather obvious but I'm still trying to figure it out...
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Drag&Drop from Outlook (Express) - Possible?

Post by srod »

The first argument points to a buffer into which the data will be placed. The second argument is the size of the buffer in bytes. The 3rd parameter is the address of a variable which will receive the actual number of bytes read. You can pass 0 for this last parameter.

You might want to take a look at the source to my COMdoc utility if you wish to see some iStorage and iStream objects in use.
I may look like a mule, but I'm not a complete ass.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Drag&Drop from Outlook (Express) - Possible?

Post by mesozorn »

srod wrote:The first argument points to a buffer into which the data will be placed. The second argument is the size of the buffer in bytes. The 3rd parameter is the address of a variable which will receive the actual number of bytes read. You can pass 0 for this last parameter.
Ah right thanks okay I see... didn't realize it was inheriting the Read method from a parent class, ISequentialStream:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

I have it working now and successfully accepting file attachment dragdrops from Outlook for saving to disk (or any other operation). I will post the final code shortly once I've cleaned up all of my debug tests and the like. Thanks again for all the valued assistance.
Post Reply