SEuBo Posted February 25, 2021 Share Posted February 25, 2021 (edited) Hi there, I am trying to save an email which was dragged from Outlook onto my AutoIt Application to file. Accepting files the regular way doesn't work since Outlook seems to be using the OLE Drag&Drop. I've found the DragDropEvent UDF by @Ward, but unfortunately, it can only retreive a limited amount of Formats, so I tried my luck. Here's the UDF in case you don't want to click the Link above. Spoiler expandcollapse popup; =============================================================================================================================== ; File : DragDropEvent.au3 (2012/3/15) ; Purpose : Convert OLE drag-and-drop event to "Windows Message" that AutoIt3 can handle by GUIRegisterMsg ; Provide 4 message: $WM_DRAGENTER, $WM_DRAGOVER, $WM_DRAGLEAVE, and $WM_DROP ; Author : Ward ; =============================================================================================================================== #Include-once #Include <Memory.au3> #Include <SendMessage.au3> #Include <WindowsConstants.au3> ; =============================================================================================================================== ; Public Functions: ; ; DragDropEvent_Startup() ; DragDropEvent UDF startup. ; ; DragDropEvent_Register($hWnd, $hWndToReceiveMsg = $hWnd) ; Register a window or control that can be the target. $hWndToReceiveMsg is needed only when $hWnd is a control. ; ; DragDropEvent_Revoke($hWnd) ; Revokes the registration of the specified window or control. ; ; DragDropEvent_GetHWnd($wParam) ; Can invoke by all message handler, to get what window or control is the target. ; ; DragDropEvent_GetX($wParam) ; DragDropEvent_GetY($wParam) ; DragDropEvent_GetKeyState($wParam) ; Can invoke by all message handler except $WM_DRAGLEAVE, to get the modifier keys and mouse position. ; ; DragDropEvent_IsText($wParam) ; DragDropEvent_IsFile($wParam) ; Can invoke by $WM_DRAGENTER or $WM_DROP, to check what data is being dragged. ; ; DragDropEvent_GetText($wParam) ; DragDropEvent_GetFile($wParam) ; Can invoke by $WM_DRAGENTER or $WM_DROP, to get related data. Path of file is splited by "|". ; ; =============================================================================================================================== ; =============================================================================================================================== ; Internal Functions: ; ; IDropTarget_QueryInterface($pSelf, $pRIID, $pObj) ; IDropTarget_AddRef($pSelf) ; IDropTarget_Release($pSelf) ; IDropTarget_DragEnter($pSelf, $DataObj, $KeyState, $X, $Y, $pEffect) ; IDropTarget_DragLeave($pSelf) ; IDropTarget_DragOver($pSelf, $KeyState, $X, $Y, $pEffect) ; IDropTarget_Drop($pSelf, $DataObj, $KeyState, $X, $Y, $pEffect) ; IDropTarget_SetEffect($pEffect, $Value) ; Methods of IDropTarget interface. ; ; IDropTarget_SetHWnd($pSelf, $hWnd, $hWndToReceiveMsg) ; IDropTarget_GetHWnd($pSelf, ByRef $hWnd, ByRef $hWndToReceiveMsg) ; To set and get data store in IDropTarget interface. ; ; DataObject_QueryText($DataObj) ; DataObject_QueryFile($DataObj) ; DataObject_GetText($DataObj) ; DataObject_GetFile($DataObj) ; Functions to handle DataObject. ; ; DragDropEvent_InfoCreate($hWnd, $DataObj, $KeyState, $X, $Y) ; DragDropEvent_Get($wParam, $Name) ; Functions to handle infomation data for DragDropEvent. ; ; __CreateCOMInterface($sFunctionPrefix, $dtagInterface, $fNoUnknown = False, $ExtraSpace = 0) ; To create vtable of a COM interface. Modify from _AutoItObject_ObjectFromDtag. ; ; =============================================================================================================================== ; =============================================================================================================================== ; Global const and variables ; =============================================================================================================================== ; Define 4 kind of DragDropEvent: DragEnter, DragOver, DragLeave, and Drop Global Enum $WM_DRAGENTER = $WM_USER + 0x1001, $WM_DRAGOVER, $WM_DRAGLEAVE, $WM_DROP ; Message handler return one of these flags, which indicates what the result of the drop operation would be. Global Enum $DROPEFFECT_NONE = 0, $DROPEFFECT_COPY = 1, $DROPEFFECT_MOVE = 2, $DROPEFFECT_LINK = 4, $DROPEFFECT_SCROLL = 0x80000000 Global Const $IID_IDataObject = "{0000010e-0000-0000-C000-000000000046}" Global Const $dtagIDropTarget = "DragEnter hresult(ptr;uint;uint64;ptr);DragOver hresult(uint;uint64;ptr);DragLeave hresult();Drop hresult(ptr;uint;uint64;ptr);" Global Const $dtagIDataObject = "GetData hresult(struct*;struct*);GetDataHere hresult(struct*;struct*);QueryGetData hresult(struct*);GetCanonicalFormatEtc hresult(struct*;struct*);SetData hresult(struct*;struct*;int);EnumFormatEtc hresult(uint;ptr);DAdvise hresult(struct*;uint;ptr;ptr);DUnadvise hresult(uint);EnumDAdvise hresult(ptr);" Global Const $tagFORMATETC = "struct;uint Format;ptr ptd;uint Aspect;int lindex;uint tymed;endstruct" Global Const $tagSTGMEDIUM = "struct;uint tymed;ptr hGlobal;ptr UnkForRelease;endstruct" Global Const $tagDragDropEventInfo = "ptr hwnd;ptr DataObj;uint KeyState;uint x;uint y" Global Const $__KERNEL32_DLL = DllOpen("kernel32.dll") Global Const $__OLE32_DLL = DllOpen("ole32.dll") Global $__IDropTargetLen = 0 ; =============================================================================================================================== ; DragDropEvent startup and register functions ; =============================================================================================================================== Func DragDropEvent_Startup() ; If @AutoItVersion < "3.3.8.0" Then Exit MsgBox(16, "DragDropEvent Fail", "Require AutoIt Version 3.3.8.0 at least") DllCall($__OLE32_DLL, "int", "OleInitialize", "ptr", 0) EndFunc Func DragDropEvent_Register($hWnd, $hWndToReceiveMsg = Default) If IsKeyword($hWndToReceiveMsg) Then $hWndToReceiveMsg = $hWnd Local $IDropTarget = __CreateCOMInterface("IDropTarget_", $dtagIDropTarget, True, 2) ; add 2 extra space to store hWnd If $IDropTarget Then $__IDropTargetLen = @Extended IDropTarget_SetHWnd($IDropTarget, $hWnd, $hWndToReceiveMsg) DllCall($__OLE32_DLL, "int", "RegisterDragDrop", "hwnd", $hWnd, "ptr", $IDropTarget) EndIf EndFunc Func DragDropEvent_Revoke($hWnd) DllCall($__OLE32_DLL, "int", "RevokeDragDrop", "hwnd", $hWnd) EndFunc ; =============================================================================================================================== ; Methods of IDropTarget interface ; =============================================================================================================================== Func IDropTarget_QueryInterface($pSelf, $pRIID, $pObj) Return 0x80004002 ; E_NOINTERFACE EndFunc Func IDropTarget_AddRef($pSelf) EndFunc Func IDropTarget_Release($pSelf) DllCall($__OLE32_DLL, "none", "CoTaskMemFree", "ptr", $pSelf) EndFunc Func IDropTarget_DragEnter($pSelf, $DataObj, $KeyState, $Point, $pEffect) Local $hWnd, $hWndToReceiveMsg IDropTarget_GetHWnd($pSelf, $hWnd, $hWndToReceiveMsg) Local $X = BitAND($Point, 0xFFFFFFFF), $Y = Dec(StringTrimRight(Hex($Point), 8)) Local $Info = DragDropEvent_InfoCreate($hWnd, $DataObj, $KeyState, $X, $Y) Local $Ret = _SendMessage($hWndToReceiveMsg, $WM_DRAGENTER, DllStructGetPtr($Info), 0) DllStructSetData(DllStructCreate("dword", $pEffect), 1, $Ret) EndFunc Func IDropTarget_DragLeave($pSelf) Local $hWnd, $hWndToReceiveMsg IDropTarget_GetHWnd($pSelf, $hWnd, $hWndToReceiveMsg) Local $Info = DragDropEvent_InfoCreate($hWnd, 0, 0, 0, 0) _SendMessage($hWndToReceiveMsg, $WM_DRAGLEAVE, DllStructGetPtr($Info), 0) EndFunc Func IDropTarget_DragOver($pSelf, $KeyState, $Point, $pEffect) Local $hWnd, $hWndToReceiveMsg IDropTarget_GetHWnd($pSelf, $hWnd, $hWndToReceiveMsg) Local $X = BitAND($Point, 0xFFFFFFFF), $Y = Dec(StringTrimRight(Hex($Point), 8)) Local $Info = DragDropEvent_InfoCreate($hWnd, 0, $KeyState, $X, $Y) Local $Ret = _SendMessage($hWndToReceiveMsg, $WM_DRAGOVER, DllStructGetPtr($Info), 0) DllStructSetData(DllStructCreate("dword", $pEffect), 1, $Ret) EndFunc Func IDropTarget_Drop($pSelf, $DataObj, $KeyState, $Point, $pEffect) Local $hWnd, $hWndToReceiveMsg IDropTarget_GetHWnd($pSelf, $hWnd, $hWndToReceiveMsg) Local $X = BitAND($Point, 0xFFFFFFFF), $Y = Dec(StringTrimRight(Hex($Point), 8)) Local $Info = DragDropEvent_InfoCreate($hWnd, $DataObj, $KeyState, $X, $Y) Local $Ret = _SendMessage($hWndToReceiveMsg, $WM_DROP, DllStructGetPtr($Info), 0) DllStructSetData(DllStructCreate("dword", $pEffect), 1, $Ret) EndFunc Func IDropTarget_SetHWnd($pSelf, $hWnd, $hWndToReceiveMsg) Local $Buffer = DllStructCreate("ptr[" & ($__IDropTargetLen + 2) & "]", $pSelf) DllStructSetData($Buffer, 1, $hWnd, $__IDropTargetLen + 1) DllStructSetData($Buffer, 1, $hWndToReceiveMsg, $__IDropTargetLen + 2) EndFunc Func IDropTarget_GetHWnd($pSelf, ByRef $hWnd, ByRef $hWndToReceiveMsg) Local $Buffer = DllStructCreate("ptr[" & ($__IDropTargetLen + 2) & "]", $pSelf) $hWnd = DllStructGetData($Buffer, 1, $__IDropTargetLen + 1) $hWndToReceiveMsg = DllStructGetData($Buffer, 1, $__IDropTargetLen + 2) EndFunc ; =============================================================================================================================== ; Functions to handle DataObject ; =============================================================================================================================== Func DataObject_QueryText($DataObj) Local $IDataObj = ObjCreateInterface($DataObj, $IID_IDataObject, $dtagIDataObject) If Not IsObj($IDataObj) Then Return -1 $IDataObj.AddRef() Local $FORMATETC = DllStructCreate($tagFORMATETC) DllStructSetData($FORMATETC, "Format", 13) ; 13 = CF_UNICODETEXT DllStructSetData($FORMATETC, "Aspect", 1) DllStructSetData($FORMATETC, "lindex", -1) DllStructSetData($FORMATETC, "tymed", 1) ; 1 = TYMED_HGLOBAL Local $Ret = $IDataObj.QueryGetData($FORMATETC) If $Ret <> 0 Then DllStructSetData($FORMATETC, "Format", 1) ; 1 = CF_TEXT $Ret = $IDataObj.QueryGetData($FORMATETC) EndIf Return $Ret EndFunc Func DataObject_QueryFile($DataObj) Local $IDataObj = ObjCreateInterface($DataObj, $IID_IDataObject, $dtagIDataObject) If Not IsObj($IDataObj) Then Return -1 $IDataObj.AddRef() Local $FORMATETC = DllStructCreate($tagFORMATETC) DllStructSetData($FORMATETC, "Format", 15) ; 15 = CF_HDROP DllStructSetData($FORMATETC, "Aspect", 1) DllStructSetData($FORMATETC, "lindex", -1) DllStructSetData($FORMATETC, "tymed", 1) ; 1 = TYMED_HGLOBAL Return $IDataObj.QueryGetData($FORMATETC) EndFunc Func DataObject_GetText($DataObj) Local $IDataObj = ObjCreateInterface($DataObj, $IID_IDataObject, $dtagIDataObject) If Not IsObj($IDataObj) Then Return -1 $IDataObj.AddRef() Local $FORMATETC = DllStructCreate($tagFORMATETC) Local $STGMEDIUM = DllStructCreate($tagSTGMEDIUM) DllStructSetData($FORMATETC, "Format", 13) ; 13 = CF_UNICODETEXT DllStructSetData($FORMATETC, "Aspect", 1) DllStructSetData($FORMATETC, "lindex", -1) DllStructSetData($FORMATETC, "tymed", 1) ; 1 = TYMED_HGLOBAL Local $IsUnicode = True Local $Ret = $IDataObj.QueryGetData($FORMATETC) If $Ret <> 0 Then $IsUnicode = False DllStructSetData($FORMATETC, "Format", 1) ; 1 = CF_TEXT $Ret = $IDataObj.QueryGetData($FORMATETC) EndIf If $Ret <> 0 Then Return SetError(1, 0, "") Local $Error = 1, $Text = "" If $IDataObj.GetData($FORMATETC, $STGMEDIUM) = 0 Then If DllStructGetData($STGMEDIUM, "tymed") = 1 Then Local $Ptr = _MemGlobalLock(DllStructGetData($STGMEDIUM, "hGlobal")) Local $Tag If $IsUnicode Then $Tag = "wchar[" & (_MemGlobalSize($Ptr) / 2) & "]" Else $Tag = "char[" & _MemGlobalSize($Ptr) & "]" EndIf $Text = DllStructGetData(DllStructCreate($Tag, $Ptr), 1) _MemGlobalUnlock($Ptr) If DllStructGetData($STGMEDIUM, "UnkForRelease") = 0 Then _MemGlobalFree($Ptr) $Error = 0 EndIf EndIf Return SetError($Error, 0, $Text) EndFunc Func DataObject_GetFile($DataObj) Local $IDataObj = ObjCreateInterface($DataObj, $IID_IDataObject, $dtagIDataObject) If Not IsObj($IDataObj) Then Return -1 $IDataObj.AddRef() Local $FORMATETC = DllStructCreate($tagFORMATETC) Local $STGMEDIUM = DllStructCreate($tagSTGMEDIUM) DllStructSetData($FORMATETC, "Format", 15) ; 15 = CF_HDROP DllStructSetData($FORMATETC, "Aspect", 1) DllStructSetData($FORMATETC, "lindex", -1) DllStructSetData($FORMATETC, "tymed", 1) ; 1 = TYMED_HGLOBAL Local $Error = 1, $FileList = "" If $IDataObj.GetData($FORMATETC, $STGMEDIUM) = 0 Then If DllStructGetData($STGMEDIUM, "tymed") = 1 Then Local $Ptr = _MemGlobalLock(DllStructGetData($STGMEDIUM, "hGlobal")) Local $StrPtr = $Ptr + DllStructGetData(DllStructCreate("dword", $Ptr), 1) Do Local $Ret = DllCall($__KERNEL32_DLL, "uint", "lstrlenW", "ptr", $StrPtr) Local $StrLen = $Ret[0] If $StrLen Then Local $Str = DllStructGetData(DllStructCreate("wchar[" & $StrLen & "]", $StrPtr), 1) $FileList &= $Str & "|" $StrPtr += $StrLen * 2 + 2 EndIf Until $StrLen = 0 If StringRight($FileList, 1) = "|" Then $FileList = StringTrimRight($FileList, 1) _MemGlobalUnlock($Ptr) If DllStructGetData($STGMEDIUM, "UnkForRelease") = 0 Then _MemGlobalFree($Ptr) $Error = 0 EndIf EndIf Return SetError($Error, 0, $FileList) EndFunc ; =============================================================================================================================== ; Functions to handle infomation data for DragDropEvent ; =============================================================================================================================== Func DragDropEvent_InfoCreate($hWnd, $DataObj, $KeyState, $X, $Y) Local $Info = DllStructCreate($tagDragDropEventInfo) DllStructSetData($Info, "hwnd", $hWnd) DllStructSetData($Info, "DataObj", $DataObj) DllStructSetData($Info, "KeyState", $KeyState) DllStructSetData($Info, "x", $X) DllStructSetData($Info, "y", $Y) Return $Info EndFunc Func DragDropEvent_Get($wParam, $Name) If Not $wParam Then Return SetError(1, 0, 0) Local $Info = DllStructCreate($tagDragDropEventInfo, $wParam) Return DllStructGetData($Info, $Name) EndFunc Func DragDropEvent_GetHWnd($wParam) Local $Ret = DragDropEvent_Get($wParam, "hwnd") Return SetError(@Error, 0, $Ret) EndFunc Func DragDropEvent_GetX($wParam) Local $Ret = DragDropEvent_Get($wParam, "x") Return SetError(@Error, 0, $Ret) EndFunc Func DragDropEvent_GetY($wParam) Local $Ret = DragDropEvent_Get($wParam, "y") Return SetError(@Error, 0, $Ret) EndFunc Func DragDropEvent_GetKeyState($wParam) Local $Ret = DragDropEvent_Get($wParam, "KeyState") Return SetError(@Error, 0, $Ret) EndFunc Func DragDropEvent_IsText($wParam) Local $DataObj = DragDropEvent_Get($wParam, "DataObj") Return DataObject_QueryText($DataObj) = 0 EndFunc Func DragDropEvent_IsFile($wParam) Local $DataObj = DragDropEvent_Get($wParam, "DataObj") Return DataObject_QueryFile($DataObj) = 0 EndFunc Func DragDropEvent_GetText($wParam) Local $DataObj = DragDropEvent_Get($wParam, "DataObj") Return DataObject_GetText($DataObj) EndFunc Func DragDropEvent_GetFile($wParam) Local $DataObj = DragDropEvent_Get($wParam, "DataObj") Return DataObject_GetFile($DataObj) EndFunc ; =============================================================================================================================== ; Functions to create COM interface ; =============================================================================================================================== Func __CreateCOMInterface($sFunctionPrefix, $dtagInterface, $fNoUnknown = False, $ExtraSpace = 0) ; Original is _AutoItObject_ObjectFromDtag in AutoItObject.au3 by AutoItObject-Team ; Modify by Ward Local Const $__PtrSize = DllStructGetSize(DllStructCreate('ptr', 1)) Local Const $dtagIUnknown = "QueryInterface hresult(ptr;ptr*);AddRef dword();Release dword();" If $fNoUnknown Then $dtagInterface = $dtagIUnknown & $dtagInterface Local $sMethods = StringReplace(StringRegExpReplace($dtagInterface, "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF) If $sMethods = $dtagInterface Then $sMethods = StringReplace(StringRegExpReplace($dtagInterface, "\h*(\w+)\h*(;|;*\z)", "$1\|" & @LF), ";" & @LF, @LF) $sMethods = StringTrimRight($sMethods, 1) $sMethods = StringReplace(StringReplace(StringReplace(StringReplace($sMethods, "object", "idispatch"), "variant*", "ptr"), "hresult", "long"), "bstr", "ptr") Local $aMethods = StringSplit($sMethods, @LF, 3) Local $iUbound = UBound($aMethods) Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback Local $AllocSize = $__PtrSize * ($iUbound + 1 + $ExtraSpace) Local $Ret = DllCall($__OLE32_DLL, "ptr", "CoTaskMemAlloc", "uint_ptr", $AllocSize) If @error Then Return SetError(1, 0, 0) Local $AllocPtr = $Ret[0] Local $tInterface = DllStructCreate("ptr[" & $iUbound + 1 & "]", $AllocPtr) If @error Then Return SetError(1, 0, 0) For $i = 0 To $iUbound - 1 $aSplit = StringSplit($aMethods[$i], "|", 2) If UBound($aSplit) <> 2 Then ReDim $aSplit[2] $sNamePart = $aSplit[0] $sTagPart = $aSplit[1] $sMethod = $sFunctionPrefix & $sNamePart $aTagPart = StringSplit($sTagPart, ";", 2) $sRet = $aTagPart[0] $sParams = StringReplace($sTagPart, $sRet, "", 1) $sParams = "ptr" & $sParams ; To avoid repeat allocate the same callback, a memory leakage $hCallback = Eval(":Callback:" & $sMethod) If Not $hCallback Then $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams) Assign(":Callback:" & $sMethod, $hCallback, 2) EndIf DllStructSetData($tInterface, 1, DllCallbackGetPtr($hCallback), $i + 2) Next DllStructSetData($tInterface, 1, $AllocPtr + $__PtrSize) ; Interface method pointers are actually pointer size away Return SetExtended($iUbound + 1, $AllocPtr) ; Return interface size as @Extended for access extra space EndFunc Based on the UDF, I created a new Function "DragDropEvent_GetVirtualFile" (see below), that is supposed to retreive the Filecontents. I managed to successfully retreive the amount of Mails and their titles (="filenames") but I am struggling to get to the filecontents. To test for yourself: Run The script, and Drag 1 or more mails from outlook onto the button. Then check the Console-Output. Spoiler expandcollapse popup; =============================================================================================================================== ; File : DragDropEvent_Example_2.au3 (2012/3/9) ; Purpose : Demonstrate the usage of DragDropEvent UDF ; Author : Ward ; =============================================================================================================================== #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include "DragDropEvent.au3" #include <WinAPI.au3> Global $Button3 DragDropEvent_Startup() Main() Exit Func Main() Local $MainWin = GUICreate("DragDropEvent Example", 460, 400, -1, -1, -1, $WS_EX_TOPMOST) GUISetFont(12, 900) $Button3 = GUICtrlCreateButton("Drop Outlook Mail", 20, 20, 420, 360) DragDropEvent_Register(GUICtrlGetHandle($Button3), $MainWin) GUIRegisterMsg($WM_DRAGENTER, "OnDragDrop") GUIRegisterMsg($WM_DRAGOVER, "OnDragDrop") GUIRegisterMsg($WM_DRAGLEAVE, "OnDragDrop") GUIRegisterMsg($WM_DROP, "OnDragDrop") GUISetState(@SW_SHOW) While 1 Local $Msg = GUIGetMsg() Switch $Msg Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd GUIDelete() EndFunc ;==>Main Func OnDragDrop($hWnd, $Msg, $wParam, $lParam) Static $DropAccept Switch $Msg Case $WM_DRAGLEAVE Return $DROPEFFECT_NONE Case $WM_DROP DragDropEvent_GetVirtualFile($wParam) ContinueCase Case Else Return $DROPEFFECT_COPY EndSwitch EndFunc ;==>OnDragDrop Func DragDropEvent_GetVirtualFile($wParam) Local Enum $TYMED_HGLOBAL = 1, _ $TYMED_FILE, _ $TYMED_ISTREAM, _ $TYMED_ISTORAGE, _ $TYMED_GDI, _ $TYMED_MFPICT, _ $TYMED_ENHMF, _ $TYMED_NULL Local $tagCLSID = $tagGUID Local $tagSIZEL = "struct;long cx;long cy;endstruct;" Local $tagPOINTL = "struct;long x;long y;endstruct;" Local $tagFILETIME = "struct;DWORD dwLowDateTime;DWORD dwHighDateTime;endstruct;" Local $tagFILEDESCRIPTORW = _ "struct;" & _ "DWORD dwFlags;" & _ "BYTE clsid[" & DllStructGetSize(DllStructCreate($tagCLSID)) & "];" & _ "BYTE sizel[" & DllStructGetSize(DllStructCreate($tagSIZEL)) & "];" & _ "BYTE pointl[" & DllStructGetSize(DllStructCreate($tagPOINTL)) & "];" & _ "DWORD dwFileAttributes;" & _ "BYTE ftFileCreationTime[" & DllStructGetSize(DllStructCreate($tagFILETIME)) & "];" & _ "BYTE ftLastAccessTime[" & DllStructGetSize(DllStructCreate($tagFILETIME)) & "];" & _ "BYTE ftLastWriteTime[" & DllStructGetSize(DllStructCreate($tagFILETIME)) & "];" & _ "dword nFileSizeHigh;" & _ "dword nFileSizeLow;" & _ "WCHAR cFileName[260];" & _ "endstruct;" Local $tagFILEDESCRIPTORA = _ "struct;" & _ "DWORD dwFlags;" & _ "BYTE clsid[" & DllStructGetSize(DllStructCreate($tagCLSID)) & "];" & _ "BYTE sizel[" & DllStructGetSize(DllStructCreate($tagSIZEL)) & "];" & _ "BYTE pointl[" & DllStructGetSize(DllStructCreate($tagPOINTL)) & "];" & _ "DWORD dwFileAttributes;" & _ "BYTE ftFileCreationTime[" & DllStructGetSize(DllStructCreate($tagFILETIME)) & "];" & _ "BYTE ftLastAccessTime[" & DllStructGetSize(DllStructCreate($tagFILETIME)) & "];" & _ "BYTE ftLastWriteTime[" & DllStructGetSize(DllStructCreate($tagFILETIME)) & "];" & _ "dword nFileSizeHigh;" & _ "dword nFileSizeLow;" & _ "CHAR cFileName[260];" & _ "endstruct;" Local $tagFILEGROUPDESCRIPTORW = _ "struct;" & _ "UINT cItems;" & _ "BYTE fgd[" & DllStructGetSize(DllStructCreate($tagFILEDESCRIPTORW)) & "];" & _ "endstruct;" Local $tagFILEGROUPDESCRIPTORA = _ "struct;" & _ "UINT cItems;" & _ "BYTE fgd[" & DllStructGetSize(DllStructCreate($tagFILEDESCRIPTORA)) & "];" & _ "endstruct;" Local $FORMATETC, $STGMEDIUM, $hMemGlobalPtr Local $tFileGroupDescriptorA, $tFileDescriptorA Local $tFileGroupDescriptorW, $tFileDescriptorW Local $IDataObj Local $iDropCount Local $DataObj = DragDropEvent_Get($wParam, "DataObj") $IDataObj = ObjCreateInterface($DataObj, $IID_IDataObject, $dtagIDataObject) If Not IsObj($IDataObj) Then Return -1 $IDataObj.AddRef() ; ---------------------------------------------------------------------------- ; Format FileGroupDescriptorW to query amount & mail titles (=filenames) from ; DataObject. ; ---------------------------------------------------------------------------- $FORMATETC = DllStructCreate($tagFORMATETC) $FORMATETC.Format = _RegisterFormat("FileGroupDescriptorW") $FORMATETC.Aspect = 1 $FORMATETC.lindex = -1 $FORMATETC.tymed = $TYMED_HGLOBAL ; ---------------------------------------------------------------------------- ; QueryGetData: Find out, if Outlook supports this Format "FileGroupDescriptorW" ; = Determine whether a subsequent call of "GetData" would likely succeed. ; ---------------------------------------------------------------------------- If $IDataObj.QueryGetData($FORMATETC) = 0 Then ; Create Result-Structure "STGMEDIUM" $STGMEDIUM = DllStructCreate($tagSTGMEDIUM) If $IDataObj.GetData($FORMATETC, $STGMEDIUM) = 0 Then ; we didn't leave Outlook much choice, so the resulting STGMEDIUM.tymed should match ; the one we supplied with FORMATETC If $STGMEDIUM.tymed = $TYMED_HGLOBAL Then ; $STGMEDIUM.hGlobal is a pointer to global Memory provided by Outlook, create FileGroupDescriptorW-Structure ; (which is what we requested) at that location so we can access the result data. Local $hMemGlobalPtr = _MemGlobalLock($STGMEDIUM.hGlobal) $tFileGroupDescriptorW = DllStructCreate($tagFILEGROUPDESCRIPTORW, $hMemGlobalPtr) If $tFileGroupDescriptorW <> 0 Then ; for each item, print out the data. ConsoleWrite("$tFileGroupDescriptor.cItems: " & $tFileGroupDescriptorW.cItems & @CRLF) $iDropCount = $tFileGroupDescriptorW.cItems For $i = 1 To $iDropCount ConsoleWrite(@CRLF & @CRLF) Local $iOffset = DllStructGetSize(DllStructCreate($tagFILEDESCRIPTORW)) * ($i - 1) $tFileDescriptorW = DllStructCreate($tagFILEDESCRIPTORW, DllStructGetPtr($tFileGroupDescriptorW, 'fgd') + $iOffset) If $tFileDescriptorW <> 0 Then ConsoleWrite("$tFileDescriptorW.dwFlags: " & $tFileDescriptorW.dwFlags & @CRLF) $tCLSID = DllStructCreate($tagCLSID, DllStructGetPtr($tFileDescriptorW, 'clsid')) ConsoleWrite("$tFileDescriptorW.clsid: " & _WinAPI_StringFromGUID($tCLSID) & @CRLF) $tSIZEL = DllStructCreate($tagSIZEL, DllStructGetPtr($tFileDescriptorW, 'sizel')) ConsoleWrite("$tFileDescriptorW.sizel: " & $tSIZEL.cx & ", " & $tSIZEL.cy & @CRLF) $tPOINTL = DllStructCreate($tagPOINTL, DllStructGetPtr($tFileDescriptorW, 'pointl')) ConsoleWrite("$tFileDescriptorW.pointl: " & $tPOINTL.x & ", " & $tPOINTL.y & @CRLF) ConsoleWrite("$tFileDescriptorW.dwFileAttributes: " & $tFileDescriptorW.dwFileAttributes & @CRLF) $tFILECREATIONTIME = DllStructCreate($tagFILETIME, DllStructGetPtr($tFileDescriptorW, 'ftFileCreationTime')) ConsoleWrite("$tFileDescriptorW.ftFileCreationTime: " & $tFILECREATIONTIME.dwLowDateTime & ", " & $tFILECREATIONTIME.dwHighDateTime & @CRLF) $tLASTACCESSTIME = DllStructCreate($tagFILETIME, DllStructGetPtr($tFileDescriptorW, 'ftLastAccessTime')) ConsoleWrite("$tFileDescriptorW.ftLastAccessTime: " & $tLASTACCESSTIME.dwLowDateTime & ", " & $tLASTACCESSTIME.dwHighDateTime & @CRLF) $tLASTWRITETIME = DllStructCreate($tagFILETIME, DllStructGetPtr($tFileDescriptorW, 'ftLastWriteTime')) ConsoleWrite("$tFileDescriptorW.ftLastWriteTime: " & $tLASTWRITETIME.dwLowDateTime & ", " & $tLASTWRITETIME.dwHighDateTime & @CRLF) ConsoleWrite("$tFileDescriptorW.nFileSizeHigh: " & $tFileDescriptorW.nFileSizeHigh & @CRLF) ConsoleWrite("$tFileDescriptorW.nFileSizeLow: " & $tFileDescriptorW.nFileSizeLow & @CRLF) ConsoleWrite("$tFileDescriptorW.cFileName: " & $tFileDescriptorW.cFileName & @CRLF) EndIf Next EndIf _MemGlobalUnlock($hMemGlobalPtr) If $STGMEDIUM.UnkForRelease = 0 Then _MemGlobalFree($hMemGlobalPtr) EndIf EndIf EndIf ; -------------------------------------------------------- ; now get the filecontents: For $i = 0 To $iDropCount - 1 $FORMATETC = DllStructCreate($tagFORMATETC) DllStructSetData($FORMATETC, "Format", _RegisterFormat("FileContents")) ; we want filecontents now! DllStructSetData($FORMATETC, "Aspect", 1) DllStructSetData($FORMATETC, "lindex", $i) ; specify index! DllStructSetData($FORMATETC, "tymed", BitOR($TYMED_HGLOBAL, $TYMED_ISTREAM, $TYMED_ISTORAGE)) ; doesnt work with just TYMED_HGLOBAL If $IDataObj.QueryGetData($FORMATETC) = 0 Then ConsoleWrite('+> QueryGetData(...) => success for FileContents' & @CRLF) ;### Debug Console Local $STGMEDIUM = DllStructCreate($tagSTGMEDIUM) $iResult = $IDataObj.GetData($FORMATETC, $STGMEDIUM) If $iResult = 0 Then ConsoleWrite('GetData(...) => success for FileContents' & @CRLF) ;### Debug Console If $STGMEDIUM.tymed = 1 Then Local $Ptr = _MemGlobalLock($STGMEDIUM.hGlobal) $tagFILECONTENTS = "byte[" & _MemGlobalSize($STGMEDIUM.hGlobal) & "];" ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $tagFILECONTENTS = ' & $tagFILECONTENTS & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console $tFileContents = DllStructCreate($tagFILECONTENTS, $Ptr) _MemGlobalUnlock($Ptr) If $STGMEDIUM.UnkForRelease = 0 Then _MemGlobalFree($Ptr) EndIf Else ConsoleWrite('!> $IDataObj.GetData(...) => Error 0x' & Hex($iResult) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console EndIf Else ConsoleWrite("FileContents error @index " & $i & @CRLF) EndIf Next ;~ Return $vReturn EndFunc ;==>DragDropEvent_GetVirtualFile Func _RegisterFormat($sFormat) ; copied from Clipboard.au3 Local $aResult = DllCall("user32.dll", "uint", "RegisterClipboardFormatW", "wstr", $sFormat) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] EndFunc ;==>_RegisterFormat The output I am getting is this (as you can see, it errors when calling IDataObj.GetData() ) $tFileGroupDescriptor.cItems: 1 $tFileDescriptorW.dwFlags: 0 $tFileDescriptorW.clsid: {00000000-0000-0000-0000-000000000000} $tFileDescriptorW.sizel: 0, 0 $tFileDescriptorW.pointl: 0, 0 $tFileDescriptorW.dwFileAttributes: 0 $tFileDescriptorW.ftFileCreationTime: 0, 0 $tFileDescriptorW.ftLastAccessTime: 0, 0 $tFileDescriptorW.ftLastWriteTime: 0, 0 $tFileDescriptorW.nFileSizeHigh: 0 $tFileDescriptorW.nFileSizeLow: 0 $tFileDescriptorW.cFileName: xxxyyyyzzzz this is a mail subject!.msg +> QueryGetData(...) => success for FileContents !> $IDataObj.GetData(...) => Error 0x80040064 I was following this example: https://www.codeproject.com/Articles/28209/Outlook-Drag-and-Drop-in-C Interestingly enough, the QueryGetData at line 226 also fails, if the format specified in line 224 does not contain TYMED_ISTORAGE. (hGLOBAL doesn't work at all). All the implementations I found online seem to work. https://github.com/tonyfederer/OutlookFileDrag https://stackoverflow.com/questions/4756845/how-to-implement-drag-drop-from-outlook-mail-or-thunderbird-to-a-delphi-form https://mycsharp.de/forum/threads/114641/outlook-anhang-per-dragdrop-in-wpf-ausle https://stackoverflow.com/questions/8709076/drag-and-drop-multiple-attached-file-from-outlook-to-c-sharp-window-form Also, this is a nice summary of the sender's perspective: https://devblogs.microsoft.com/oldnewthing/20080318-00/?p=23083 Can anyone try help me with this? I feel like I've tried everyhting. Best Regards, Edited February 26, 2021 by SEuBo Changed title / added links AutoIt Examples/UDF's:Generate Function at Runtime using IRunningObjectTable / AutoItObjectVery Simple Inter-Process Communication (using AutoItObject Pure AutoIt) Link to comment Share on other sites More sharing options...
water Posted February 25, 2021 Share Posted February 25, 2021 0x80040064 stands for Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC)) Unfortunately this is all I know My UDFs and Tutorials: Spoiler UDFs:Active Directory (NEW 2022-02-19 - Version 1.6.1.0) - Download - General Help & Support - Example Scripts - WikiExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example ScriptsOutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - WikiOutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - DownloadOutlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - WikiPowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - WikiTask Scheduler (NEW 2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki Standard UDFs:Excel - Example Scripts - WikiWord - Wiki Tutorials:ADO - WikiWebDriver - Wiki Link to comment Share on other sites More sharing options...
SEuBo Posted February 26, 2021 Author Share Posted February 26, 2021 Hey! Thanks for the reply. That's too bad - I actually was hoping for an outlook professional like you to jump in and solve this mystery. I know that I could work around the issue by using your OutlookEx UDF; on drop, I'd just save the active mails to a temp file. But it would be so awesome to build a more general solution which would support all kind of Drag&Drop Sources. Maybe someone else has a bright idea! I've read through the msdn documentation again and again, but I can't figure it out. CFSTR_FILECONTENTS Spoiler This format identifier is used with the CFSTR_FILEDESCRIPTOR format to transfer data as if it were a file, regardless of how it is actually stored. The data consists of an STGMEDIUM structure that represents the contents of one file. The file is normally represented as a stream object, which avoids having to place the contents of the file in memory. In that case, the tymed member of the STGMEDIUM structure is set to TYMED_ISTREAM, and the file is represented by an IStream interface. The file can also be a storage or global memory object (TYMED_ISTORAGE or TYMED_HGLOBAL). The associated CFSTR_FILEDESCRIPTOR format contains a FILEDESCRIPTOR structure for each file that specifies the file's name and attributes. The target treats the data associated with a CFSTR_FILECONTENTS format as if it were a file. When the target calls IDataObject::GetData to extract the data, it specifies a particular file by setting the lindex member of the FORMATETC structure to the zero-based index of the file's FILEDESCRIPTOR structure in the accompanying CFSTR_FILEDESCRIPTOR format. The target then uses the returned interface pointer or global memory handle to extract the data. CFSTR_FILEDESCRIPTOR Spoiler This format identifier is used with the CFSTR_FILECONTENTS format to transfer data as a group of files. These two formats are the preferred way to transfer Shell objects that are not stored as file-system files. For example, these formats can be used to transfer a group of email messages as individual files, even though each email is actually stored as a block of data in a database. The data consists of an STGMEDIUM structure that contains a global memory object. The structure's hGlobal member points to a FILEGROUPDESCRIPTOR structure that is followed by an array containing one FILEDESCRIPTOR structure for each file in the group. For each FILEDESCRIPTOR structure, there is a separate CFSTR_FILECONTENTS format that contains the contents of the file. To identify a particular file's CFSTR_FILECONTENTS format, set the lIndex value of the FORMATETC structure to the zero-based index of the file's FILEDESCRIPTOR structure. The CFSTR_FILEDESCRIPTOR format is commonly used to transfer data as if it were a group of files, regardless of how it is actually stored. From the target's perspective, each CFSTR_FILECONTENTS format represents a single file and is treated accordingly. However, the source can store the data in any way it chooses. While a CSFTR_FILECONTENTS format might correspond to a single file, it could also, for example, represent data extracted by the source from a database or text document. If anyone sees the issue in the code from the first post - feel free to speak up AutoIt Examples/UDF's:Generate Function at Runtime using IRunningObjectTable / AutoItObjectVery Simple Inter-Process Communication (using AutoItObject Pure AutoIt) Link to comment Share on other sites More sharing options...
SEuBo Posted February 28, 2021 Author Share Posted February 28, 2021 Does noone have an idea? I just can't understand why it's not working, because I figured if I drag and drop something from the explorer (instead of outlook), the GetData()-Method returns an IStorage-Interface (still to be implemented.). With outlook it just fails... Can't get my head around that. Ideas appreciated! AutoIt Examples/UDF's:Generate Function at Runtime using IRunningObjectTable / AutoItObjectVery Simple Inter-Process Communication (using AutoItObject Pure AutoIt) Link to comment Share on other sites More sharing options...
SEuBo Posted March 6, 2021 Author Share Posted March 6, 2021 Last try to push this. AutoIt Examples/UDF's:Generate Function at Runtime using IRunningObjectTable / AutoItObjectVery Simple Inter-Process Communication (using AutoItObject Pure AutoIt) Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now