[COM] Help with the IDropSource and IDropTarget interfaces

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

18 Mar 2017, 11:52

Hi Just me,
Initial DragImage D&D implementation tries in my big app did get abrupted by a couple of errormsg followed by a nasty crash of AHK_H when dropping a file (with dragimage) at a in-treeview folder. To rule out any implicating factors in my script I tried to implement it into the demo I posted above, with same crashing result. Dont know if it might be an AHK_H bug or some of the COM coding.

The two error msgboxes says:
---------------------------
AutoHotkey
---------------------------
Error: CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Mouse and Keyboard hooks have been disabled.

- Press yes to exit thread and continue execution.
- Press no to continue thread (debug).
- Press cancel to exit application.

Exception was caused in thread id: 9352
Line: 14
LineFile: F:\TV\test\MediaLauncher\IDataObject.ahk
---------------------------
Ja Nej Avbryt
---------------------------
---------------------------
AutoHotkey
---------------------------
Error: CONTINUABLE EXCEPTION_ACCESS_VIOLATION

Mouse and Keyboard hooks have been disabled.

- Press yes to exit thread and continue execution.
- Press no to continue thread (debug).
- Press cancel to exit application.

Exception was caused in thread id: 9352
Line: 51
LineFile: F:\TV\test\MediaLauncher\SHDoDragDrop.ahk
---------------------------
Ja Nej Avbryt
---------------------------
Regards
zcooler
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [COM] Help with the IDropSource and IDropTarget interfaces

19 Mar 2017, 09:09

It seems to be a problem only occurring if the drag initiator and the drop target belong to the same thread. In this case, the pointer to the data object seems to be invalid when SHDoDragDrop returns, and the script crashes in AHK 1.1, too.

It might be related to 'unsave' thread interruption caused by RegisterCallback(), but I'm just guessing. It's a pitty.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

19 Mar 2017, 09:41

just me wrote:It's a pitty.
Just me, much kudos for the very nice try and the huge amount of time you must have spent on this mega complex "kick-ass" D&D interface. One cannot win em all ;) Hats off to you, Sir :salute:
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [COM] Help with the IDropSource and IDropTarget interfaces

19 Mar 2017, 10:08

Silly me! It seems to be a bug implemented since the first version of IDropTarget but without observable consequences until now. Remove the line

Code: Select all

      ObjRelease(pDataObj)
from IDropTarget.Drop(...) and try again, please.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

19 Mar 2017, 10:48

OHHH YES, now we are talking niiicee DragImages :mrgreen: Sooo, very cool :lol:
Hmm...Im not there yet but just to make sure for future references and while the great author is spending time on it :) I do wonder if the SHDoDragDrop() might accept md5 hash tags as DragImage (which the windows explorer thumbnails are stored in I think anyway (in the windows thumbnail cache folder .db files))?

These thumbnails are located in encrypted .db files and there is no way to access em what I know of. Trying to load the hash didnt work. Any better ideas?

Found some code for md5 hash generating:

Code: Select all

MediaFile4 := "F:\TV\Seen Recordings\20170317_17-59-02_Kanal 5_Ullared -.ts"
   FileGetSize,FileSize4,MediaFile4
   DDImage := Calc_MD5(&MediaFile4,FileSize4)
   DragImage := LoadPicture(DDImage, "GDI+ w72 h72")
   DropEffect := SHDoDragDrop(DragImage, , 2)

Calc_MD5(_VarAddress, _VarSize){
	Static Hex = "123456789ABCDEF0"
	Ptr := A_PtrSize ? "Ptr" : "Uint"
	, VarSetCapacity(MD5_CTX,104,0)
	, DllCall("advapi32\MD5Init",Ptr,&MD5_CTX)
	, DllCall("advapi32\MD5Update",Ptr,&MD5_CTX,Ptr,_VarAddress,"UInt",_VarSize)
	, DllCall("advapi32\MD5Final",Ptr,&MD5_CTX)
	Loop,16
		N := NumGet( MD5_CTX,87+A_Index,"Char"), MD5 .= SubStr(Hex,N>>4,1) . SubStr(Hex,N&15,1)
	Return MD5
}
Best regards
zcooler :wave:
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

19 Mar 2017, 12:12

Hi again just me :)

Tested quite extensively multiple ways to pass system assocciated icons to SHDoDragDrop(), which are loaded/added into the treeview GUI script, but everything I tried failed. The system's icons for the extensions are added using the default size SYSSMALL, so I could pick up which icon belongs to which extension on the fly, but when trying to pass them as a DragImage it doesnt work. Well, i dont wanna pass them as DragImages, instead I do wanna display the Icon blended into the otherwise empty DragImage, like the explorer way, but that doesnt seem to be possible, yes?

Regards
zcooler
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [COM] Help with the IDropSource and IDropTarget interfaces

20 Mar 2017, 03:56

Hi zcooler,

Code: Select all

;     DragImage            -  SHDoDragDrop (API) uses a default drag image if no other option is available. You may pass:
;                             -  a HWND of a window or control which will create an own drag image in response to the
;                                DI_GETDRAGIMAGE message (e.g. ListViews and TreeViews support this message).
;                             -  a HBITMAP handle of a bitmap to be used as the drag image.
  1. You cannot pass anything else than a HWND or HBITMAP handle.
  2. You cannot pass a handle to be combined with the shell's default drag image. It will be replaced.
zcooler wrote:Well, i dont wanna pass them as DragImages, instead I do wanna display the Icon blended into the otherwise empty DragImage, like the explorer way, but that doesnt seem to be possible, yes?
It might be possible, but afaik you'd have to do it by yourself. ;)
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [COM] Help with the IDropSource and IDropTarget interfaces

20 Mar 2017, 11:55

You might want to try this function:

Code: Select all

; ==================================================================================================================================
; Win Vista+
; Returns a HBITMAP handle of the thumbnail (if available) or the icon of the specified file.
; IconOnly - If set to true the function always returnes the icon.
; ==================================================================================================================================
GetThumbnailOrIcon(FilePath, IconOnly := False, Width := 96, Height := 96) {
   ; IShellItemImageFactory (Vista+) -> msdn.microsoft.com/en-us/library/windows/desktop/bb761084(v=vs.85).aspx
   Static IID_IShellItem := 0
          Init := VarSetCapacity(IID_IShellItem, 16, 0)
                  & DllCall("Ole32.dll\IIDFromString", "WStr", "{43826d1e-e718-42ee-bc55-a1e261c37bfe}", "Ptr", &IID_IShellItem)
   If !DllCall("Shell32.dll\SHCreateItemFromParsingName", "WStr", FilePath, "Ptr", 0, "Ptr", &IID_IShellItem, "PtrP", Item, "Int") {
      If (ISIIF := ComObjQuery(Item, "{bcc18b79-ba16-442f-80c4-8a59c30c463b}")) { ; IShellItemImageFactory
         GetImage := NumGet(NumGet(ISIIF + 0, "Uptr") + (A_PtrSize * 3), "UPtr")
         Flags := IconOnly ? 5 : 1
         HR := DllCall(GetImage, "Ptr", ISIIF, "Int64", (Height << 32) | Width, "Int", Flags, "PtrP", HBITMAP, "Int")
         ObjRelease(ISIIF)
      }
      ObjRelease(Item)
   }
   Return ((HR >= 0) && (HBITMAP) ? HBITMAP : 0)
}
It returnes a HBITMAP handle on success which can be passed to SHDoDragDrop() as a drag image. But it still won't look like 'the explorer way,'. ;)
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

21 Mar 2017, 14:24

just me wrote:You might want to try this function:
Yes, much obliged, Just me :D It returns most thumbnails and icons on the fly, but I had problems to get the video thumbnails. Turns out my explorer thumbnail generator for video files (not all video format thumbnails are supported by default in the explorer) didnt create the thumbnails as long as using the "detailed listview". I had to change it to either "medium sized icon" or "large sized icons" for all video folders and then the thumbnails are created in the explorer and turns up as dragimages using SHDoDragDrop() in the ahk app (before this move only the icon was returned for these video files). I have to find a new explorer thumnail generator cuz the old one does not create new thumbnails for recently added recordings, despite having large sized icons set for the folder. You have to browse the videofolder in the explorer in order to start thumbnail generation for the new files in there :( Took some time to figure it out.
just me wrote:It returns a HBITMAP handle on success which can be passed to SHDoDragDrop() as a drag image. But it still won't look like 'the explorer way,'. ;)
Well, it is pretty close though...but without the fancy stuff the explorer thumbnail generator adds (picture framing and minimized file extension icon) when creating the thumbnail ;) Unfortunately when using thumbnails and icons as dragimages, the drop-effect description text is lost. While the bluish drag-image without any content (passing NULL as drag source object) does have the drop-effect description text. Maybe you could explain in more detail why it is not possible to put the thumbnails and icons inside the bluish drag-image? That information could be very helpful for savvy folks in here who might wanna take a crack at filling the bluish drag-images with content, with working drop-effect description text.

Regards
zcooler
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [COM] Help with the IDropSource and IDropTarget interfaces

22 Mar 2017, 07:06

Hi zcooler,
Maybe you could explain in more detail why it is not possible to put the thumbnails and icons inside the bluish drag-image?
Because the shell will use either the bluish default drag image or the passed drag image. It should be possible to combine them, but the shell simply doesn't. If you want to have a default style drag image with additional content you have to create an appopriate bitmap and pass its handle.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

22 Mar 2017, 08:11

just me wrote:Hi zcooler,
Maybe you could explain in more detail why it is not possible to put the thumbnails and icons inside the bluish drag-image?
Because the shell will use either the bluish default drag image or the passed drag image. It should be possible to combine them, but the shell simply doesn't. If you want to have a default style drag image with additional content you have to create an appopriate bitmap and pass its handle.
Moin Just me,
Yes, it is obvious its not possible to combine them, had hoped for more technical COM details what happens when trying to combine according to MS regulations (maybe MS documentation about how to do here is non-existant), but its fine without them. No, I wont even try to create an appropiate bitmap and pass its handle, cuz the drop-effect description text wont work for those anyway (it might be interesting to do this at first when/if combining works). I will use the bluish empty default drag image all the time, with drop-effect description text working, cuz together they provide the best visible proof a drag operation has been initialized correctly (that was all along the main objective for me with these drag images, when starting posting about it in this thread). The fancy image stuff is just "icing on the cake". You will never know someone might, on a fine day, find a loophole and in that way manage to succeed with combining.

Thanks for your great work, Just me :wave:

Best regards
zcooler
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [COM] Help with the IDropSource and IDropTarget interfaces

14 Aug 2017, 11:54

Has anyone used this to emulate a drag-and-drop of a file into an external program?

E.g. this is a very small program for which WM_DROPFILES doesn't work, but where drag-and-drop does work, and where GetProp with OleDropTargetInterface does work:
Spek – Free Acoustic Spectrum Analyzer / Spectrogram Viewer
http://spek.cc/

Link:
Sending WM_DROPFILES with C++ and WinAPI to third-party application - Stack Overflow
https://stackoverflow.com/questions/222 ... pplication

Code: Select all

IDropTarget* GetRegisteredDropTargetFromWnd (HWND hWnd)
{
    IUnknown *pBuffer = (IUnknown *) GetProp (hWnd, TEXT("OleDropTargetInterface"));
    if (pBuffer != NULL)
    {
        IDropTarget *pRetVal = NULL;
        if (SUCCEEDED(pBuffer->QueryInterface(IID_IDropTarget, (void **) &pRetVal)))
            return pRetVal;
    }
    return NULL;
}
If an HWND has an IDropTarget, you can wrap your DROPFILES data with an IDataObject and pass it to the IDropTarget::Drop() method. If Drop() accepts the data, do not post a WM_DROPFILES message. The trick, however, is that the IDropTarget* pointer returned by GetProp() is relative to the process that owns the HWND, so you would have to marshal it into your process, or else inject your code into the HWND's process, in order to actually use the interface pointer.
Initial code:

Code: Select all

q::
WinGet, hWnd, ID, A
MsgBox, % pUnk := DllCall("user32\GetProp", Ptr,hWnd, Str,"OleDropTargetInterface", Ptr)
IID_IDropTarget := "{00000122-0000-0000-C000-000000000046}"

;the pointer is relative to the program
;at this point we need to inject AHK into the program (perhaps using AutoHotkeyMini.dll)

;idt := ComObjQuery(pUnk, IID_IDropTarget)
;DllCall(NumGet(NumGet(idt+0)+6*A_PtrSize), Ptr,idt, Ptr,pDataObj, UInt,0, Ptr,0, UInt,1) ;Drop
return
[some clipboard/hDrop functions that may be useful:]
GUI COMMANDS: COMPLETE RETHINK (latest: get/set system fonts, ComboBox choose string notify) - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 42#p144342

C:\Program Files (x86)\Windows Kits\8.1\Include\um\oleidl.h contains IDropTargetVtbl.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [COM] Help with the IDropSource and IDropTarget interfaces

20 Aug 2017, 00:37

Has anybody succeeded, using the library functions (in this thread) and/or additional code, in creating an IDataObject consisting of one file path (or multiple file paths), that could be used to simulate a drag-and-drop into a window. Thanks.

Btw some further links re. what I'm trying to do:
IDataObject::SetData method (COM)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
IDropTarget::Drop method (COM)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
RegisterDragDrop function (COM)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

[EDIT:] Another useful link:
SendTo mail recipient - CodeProject
https://www.codeproject.com/Articles/38 ... -recipient
Last edited by jeeswg on 20 Aug 2017, 11:09, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [COM] Help with the IDropSource and IDropTarget interfaces

20 Aug 2017, 10:57

I'm trying to put this code into IDropTarget_Sample.ahk to achieve a drag-and-drop programmatically, but the code isn't working, if anyone is able to fix this. Thanks for reading.

For IDropTarget_Sample.ahk see:
GitHub - AHK-just-me/DoDragDrop
https://github.com/AHK-just-me/DoDragDrop

Code: Select all

q::
WinGet, hWnd, ID, A
ControlGet, hCtl, Hwnd,, SysListView321, % "ahk_id " hWnd
;hWnd := hCtl

;note: on an external process:
;- we would omit these 2 lines
;- we would require dll injection to run this script
pDropTarget := IDT_LV.Ptr
DllCall("ole32\RegisterDragDrop", Ptr,hWnd, Ptr,pDropTarget)

pDropTarget := DllCall("user32\GetProp", Ptr,hWnd, Str,"OleDropTargetInterface", Ptr)
vPaths := A_ScriptFullPath

;==============================
;create hDrop

;GMEM_ZEROINIT := 0x40, GMEM_MOVEABLE := 0x2
hDrop := DllCall("GlobalAlloc", UInt,0x42, UPtr,20+(StrLen(vPaths)+2)*2, Ptr)
pDrop := DllCall("GlobalLock", Ptr,hDrop, Ptr)

;DROPFILES struct
NumPut(20, pDrop+0, 0, "UInt")
NumPut(1, pDrop+16, 0, "UInt")

;e.g. CF_HDROP with 3 paths: 'path1 null path2 null path3 null null'
vOffset := 20
Loop, Parse, vPaths, `n, `r
	if !(A_LoopField = "")
		vOffset += StrPut(A_LoopField, pDrop+vOffset, StrLen(A_LoopField)+1, "UTF-16")*2

;CF_HDROP := 0xF
DllCall("GlobalUnlock", Ptr,hDrop)
;==============================

;TYMED_HGLOBAL := 0x1
VarSetCapacity(STGMEDIUM, A_PtrSize=8?24:12, 0)
NumPut(0x1, STGMEDIUM, 0, "UInt") ;tymed
NumPut(hDrop, STGMEDIUM, A_PtrSize=8?8:4, "Ptr")
;NumPut(0, STGMEDIUM, A_PtrSize=8?16:8, "Ptr") ;*pUnkForRelease

;CF_HDROP := 0xF
;DVASPECT_CONTENT := 0x1
;TYMED_HGLOBAL := 0x1
VarSetCapacity(FORMATETC, A_PtrSize=8?32:20, 0)
NumPut(0xF, FORMATETC, 0, "UShort") ;cfFormat
NumPut(0, FORMATETC, 0, "Ptr") ;*ptd
NumPut(0x1, FORMATETC, 0, "UInt") ;dwAspect
NumPut(-1, FORMATETC, 0, "Int") ;lindex
NumPut(0x1, FORMATETC, 0, "UInt") ;tymed

IID_IDataObject := "{0000010e-0000-0000-C000-000000000046}"
VarSetCapacity(IID, 16, 0)
DllCall("ole32\IIDFromString", WStr,IID_IDataObject, Ptr,&IID)
DllCall("shell32\SHCreateDataObject", Ptr,0, UInt,0, Ptr,0, Ptr,0, Ptr,&IID, PtrP,pDataObj)

DllCall(NumGet(pDataObj+7*A_PtrSize, "UPtr"), Ptr,pDataObj, Ptr,&FORMATETC, Ptr,&STGMEDIUM, Int,0) ;SetData

;MK_LBUTTON := 0x1
DllCall(NumGet(pDropTarget+6*A_PtrSize, "UPtr"), Ptr,pDropTarget, Ptr,pDataObj, UInt,0x1, UInt64,0, UInt,0) ;Drop
VarSetCapacity(POINTL, 8, 0)
;DllCall(NumGet(pDropTarget+6*A_PtrSize, "UPtr"), Ptr,pDropTarget, Ptr,pDataObj, UInt,0x1, Ptr,&POINTL, UInt,0) ;Drop
MsgBox, % "done"
return
[post 1700]

==================================================

[EDIT:]
just me wrote:IDropTarget
Hi just me, I'm referencing you in case you were interested in this.

The idea is to drag-and-drop programmatically (instead of by physically dragging and dropping) and is based on:
Sending WM_DROPFILES with C++ and WinAPI to third-party application - Stack Overflow
https://stackoverflow.com/questions/222 ... pplication

For the programs listed below, WM_DROPFILES doesn't work, however, GetProp with OleDropTargetInterface returns a value, so drag-and-drop may be possible via IDropTarget:
- SPEK
- Explorer
- Adobe Reader
- Internet Explorer/Firefox/Chrome
- LibreOffice Writer/LibreOffice Calc

So this could be a useful general technique for dragging and dropping into programs. I'm starting off by trying to do a drag-and-drop within an AHK script, and then I would like to try to do a drag-and-drop to an external program via dll injection via InjectAhkDll. Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [COM] Help with the IDropSource and IDropTarget interfaces

08 Dec 2017, 09:27

I've managed to get the code working, to achieve a drag-and-drop programmatically. You add it to the bottom of IDropTarget_Sample.ahk.

Note: I had to replace:
#Include IDropTarget2.ahk
with:
#Include IDropTarget.ahk

Download the zip here:
GitHub - AHK-just-me/DoDragDrop
https://github.com/AHK-just-me/DoDragDrop

Code: Select all

q::
pDropTarget := IDT_LV.Ptr
pVTBLDT := NumGet(pDropTarget + 0, "UPtr")
vPaths := A_AhkPath "`n" A_ScriptFullPath

;===============
;create hDrop

;GMEM_ZEROINIT := 0x40, GMEM_MOVEABLE := 0x2
hDrop := DllCall("GlobalAlloc", UInt,0x42, UPtr,20+(StrLen(vPaths)+2)*2, Ptr)
pDrop := DllCall("GlobalLock", Ptr,hDrop, Ptr)

;DROPFILES struct
NumPut(20, pDrop+0, 0, "UInt")
NumPut(1, pDrop+16, 0, "UInt")

;e.g. CF_HDROP with 3 paths: 'path1 null path2 null path3 null null'
vOffset := 20
Loop, Parse, vPaths, `n, `r
	if !(A_LoopField = "")
		vOffset += StrPut(A_LoopField, pDrop+vOffset, StrLen(A_LoopField)+1, "UTF-16")*2

;CF_HDROP := 0xF
DllCall("GlobalUnlock", Ptr,hDrop)
;===============

;TYMED_HGLOBAL := 0x1
VarSetCapacity(STGMEDIUM, A_PtrSize=8?24:12, 0)
NumPut(0x1, &STGMEDIUM, 0, "UInt") ;tymed
NumPut(hDrop, &STGMEDIUM, A_PtrSize=8?8:4, "Ptr")
;NumPut(0, &STGMEDIUM, A_PtrSize=8?16:8, "Ptr") ;*pUnkForRelease

;CF_HDROP := 0xF
;DVASPECT_CONTENT := 0x1
;TYMED_HGLOBAL := 0x1
VarSetCapacity(FORMATETC, A_PtrSize=8?32:20, 0)
NumPut(0xF, &FORMATETC, 0, "UShort") ;cfFormat
NumPut(0, &FORMATETC, A_PtrSize=8?8:4, "Ptr") ;*ptd
NumPut(0x1, &FORMATETC, A_PtrSize=8?16:8, "UInt") ;dwAspect
NumPut(-1, &FORMATETC, A_PtrSize=8?20:12, "Int") ;lindex
NumPut(0x1, &FORMATETC, A_PtrSize=8?24:16, "UInt") ;tymed

IID_IDataObject := "{0000010e-0000-0000-C000-000000000046}"
VarSetCapacity(IID, 16, 0)
DllCall("ole32\IIDFromString", WStr,IID_IDataObject, Ptr,&IID)
DllCall("shell32\SHCreateDataObject", Ptr,0, UInt,0, Ptr,0, Ptr,0, Ptr,&IID, PtrP,pDataObj)
pVTBL := NumGet(pDataObj + 0, "UPtr")
DllCall(NumGet(pVTBL+7*A_PtrSize, "UPtr"), Ptr,pDataObj, Ptr,&FORMATETC, Ptr,&STGMEDIUM, Int,1) ;SetData

;MK_LBUTTON := 0x1
VarSetCapacity(POINTL, 8, 0)
;NumPut(200, &POINTL, 0, "UInt")
;NumPut(200, &POINTL, 4, "UInt")
;DllCall(NumGet(pVTBLDT+6*A_PtrSize, "UPtr"), Ptr,pDropTarget, Ptr,pDataObj, UInt,0x1, Ptr,&POINTL, UInt,0) ;Drop
DllCall(NumGet(pVTBLDT+6*A_PtrSize, "UPtr"), Ptr,pDropTarget, Ptr,pDataObj, UInt,0x1, UInt64,0, UInt,0) ;Drop
return
I tried to use the same code to drag-and-drop to an external program, namely the very small program spek.exe, but it didn't work. In case it's possible to fix this script, and drag-and-drop to external programs using AutoHotkeyMini.dll.

Code: Select all

#Persistent

;[get x64 and x32 versions of AutoHotkeyMini.dll]
;GitHub - HotKeyIt/ahkdll-v1-release: AutoHotkey_H v1 release
;https://github.com/HotKeyIt/ahkdll-v1-release

vDir := "C:\Program Files\AutoHotkey"
if (A_PtrSize = 8)
	vPathDll := vDir "\AutoHotkeyMiniU64.dll"
else
	vPathDll := vDir "\AutoHotkeyMiniU32.dll"

;==================================================

q::
WinGet, hWnd, ID, A
WinGet, vPID, PID, % "ahk_id " hWnd
WinGet, vPName, ProcessName, % "ahk_id " hWnd
if !(vPName = "spek.exe")
	return
pDropTarget := DllCall("user32\GetProp", Ptr,hWnd, Str,"OleDropTargetInterface", Ptr)
MsgBox, % pDropTarget
vPaths := A_ScriptFullPath

vScript = ;continuation section
(`
pDropTarget := %pDropTarget%
pVTBLDT := NumGet(pDropTarget + 0, "UPtr")
vPaths := "%vPaths%"

;===============
;create hDrop

;GMEM_ZEROINIT := 0x40, GMEM_MOVEABLE := 0x2
hDrop := DllCall("GlobalAlloc", UInt,0x42, UPtr,20+(StrLen(vPaths)+2)*2, Ptr)
pDrop := DllCall("GlobalLock", Ptr,hDrop, Ptr)

;DROPFILES struct
NumPut(20, pDrop+0, 0, "UInt")
NumPut(1, pDrop+16, 0, "UInt")

;e.g. CF_HDROP with 3 paths: 'path1 null path2 null path3 null null'
vOffset := 20
Loop, Parse, vPaths, `n, `r
	if !(A_LoopField = "")
		vOffset += StrPut(A_LoopField, pDrop+vOffset, StrLen(A_LoopField)+1, "UTF-16")*2

;CF_HDROP := 0xF
DllCall("GlobalUnlock", Ptr,hDrop)
;===============

;TYMED_HGLOBAL := 0x1
VarSetCapacity(STGMEDIUM, A_PtrSize=8?24:12, 0)
NumPut(0x1, &STGMEDIUM, 0, "UInt") ;tymed
NumPut(hDrop, &STGMEDIUM, A_PtrSize=8?8:4, "Ptr")
;NumPut(0, &STGMEDIUM, A_PtrSize=8?16:8, "Ptr") ;*pUnkForRelease

;CF_HDROP := 0xF
;DVASPECT_CONTENT := 0x1
;TYMED_HGLOBAL := 0x1
VarSetCapacity(FORMATETC, A_PtrSize=8?32:20, 0)
NumPut(0xF, &FORMATETC, 0, "UShort") ;cfFormat
NumPut(0, &FORMATETC, A_PtrSize=8?8:4, "Ptr") ;*ptd
NumPut(0x1, &FORMATETC, A_PtrSize=8?16:8, "UInt") ;dwAspect
NumPut(-1, &FORMATETC, A_PtrSize=8?20:12, "Int") ;lindex
NumPut(0x1, &FORMATETC, A_PtrSize=8?24:16, "UInt") ;tymed

IID_IDataObject := "{0000010e-0000-0000-C000-000000000046}"
VarSetCapacity(IID, 16, 0)
DllCall("ole32\IIDFromString", WStr,IID_IDataObject, Ptr,&IID)
DllCall("shell32\SHCreateDataObject", Ptr,0, UInt,0, Ptr,0, Ptr,0, Ptr,&IID, PtrP,pDataObj)
pVTBL := NumGet(pDataObj + 0, "UPtr")
DllCall(NumGet(pVTBL+7*A_PtrSize, "UPtr"), Ptr,pDataObj, Ptr,&FORMATETC, Ptr,&STGMEDIUM, Int,1) ;SetData

;MK_LBUTTON := 0x1
VarSetCapacity(POINTL, 8, 0)
;NumPut(200, &POINTL, 0, "UInt")
;NumPut(200, &POINTL, 4, "UInt")
;DllCall(NumGet(pVTBLDT+6*A_PtrSize, "UPtr"), Ptr,pDropTarget, Ptr,pDataObj, UInt,0x1, Ptr,&POINTL, UInt,0) ;Drop
DllCall(NumGet(pVTBLDT+6*A_PtrSize, "UPtr"), Ptr,pDropTarget, Ptr,pDataObj, UInt,0x1, UInt64,0, UInt,0) ;Drop
return
)
;Clipboard := vScript
;MsgBox

rThread := InjectAhkDll(vPID, vPathDll)
rThread.Exec(vScript)
rThread := ""
return

;==================================================

;[original version of InjectAhkDll]
;[SOLVED]get other process's working dir - Page 3 - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/85304-solvedget-other-processs-working-dir/page-3#entry544650

;[get x64 and x32 versions of AutoHotkeyMini.dll]
;GitHub - HotKeyIt/ahkdll-v1-release: AutoHotkey_H v1 release
;https://github.com/HotKeyIt/ahkdll-v1-release

;by HotKeyIt (modified version by jeeswg to not require _Struct.ahk)
InjectAhkDll(PID,dll="AutoHotkey.dll",script=0)
{
	static PROCESS_ALL_ACCESS:=0x1F0FFF,MEM_COMMIT := 0x1000,MEM_RELEASE:=0x8000,PAGE_EXECUTE_READWRITE:=64
	,hKernel32:=DllCall("LoadLibrary","Str","kernel32.dll","PTR"),LoadLibraryA:=DllCall("GetProcAddress","PTR",hKernel32,"AStr","LoadLibraryA","PTR")
	,base:={__Call:"InjectAhkDll",__Delete:"InjectAhkDll"},FreeLibrary:=DllCall("GetProcAddress","PTR",hKernel32,"AStr","FreeLibrary","PTR")
	static TH32CS_SNAPMODULE:=0x00000008,INVALID_HANDLE_VALUE:=-1
	,MAX_PATH:=260,MAX_MODULE_NAME32:=255,ModuleName:="",init:=VarSetCapacity(ModuleName,MAX_PATH*(A_IsUnicode?2:1))

	if IsObject(PID)
	{
		if (dll!="Exec" && script)
			return DllCall("MessageBox","PTR",0,"Str","Only Exec method can be used here!","STR","Error","UInt",0)

		hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int",0, "UInt", PID.PID,"PTR")
		if !hProc
			return DllCall("MessageBox","PTR",0,"Str","Could not open process for PID: " PID.PID,"STR","Error","UInt",0)

		if (!script) ; Free Library in remote process (object is being deleted)
		{
			; Terminate the thread in ahkdll
			hThread := DllCall("CreateRemoteThread", "PTR", hProc, "PTR", 0, "PTR", 0, "PTR", PID.ahkTerminate, "PTR", 0, "UInt", 0, "PTR", 0,"PTR")
			DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
			,DllCall("CloseHandle", "PTR", hThread)

			; Free library in remote process
			hThread := DllCall("CreateRemoteThread", "PTR", hProc, "UInt", 0, "UInt", 0, "PTR", FreeLibrary, "PTR", PID.hModule, "UInt", 0, "UInt", 0,"PTR")
			DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
			,DllCall("CloseHandle", "PTR", hThread),DllCall("CloseHandle", "PTR", hProc)
			return
		}

		nScriptLength := VarSetCapacity(nScript, (StrLen(script)+1)*(A_IsUnicode?2:1), 0)
		,StrPut(script,&nScript)

		; Reserve memory in remote process where our script will be saved
		if !pBufferRemote := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nScriptLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
			return DllCall("MessageBox","PTR",0,"Str","Could not reseve memory for process.","STR","Error","UInt",0)
		,DllCall("CloseHandle", "PTR", hProc)

		; Write script to remote process memory
		DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferRemote, "Ptr", &nScript, "PTR", nScriptLength, "Ptr", 0)

		; Start execution of code
		hThread := DllCall("CreateRemoteThread", "PTR", hProc, "PTR", 0, "PTR", 0, "PTR", PID.ahkExec, "PTR", pBufferRemote, "UInt", 0, "PTR", 0,"PTR")
		if !hThread
		{
			DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nScriptLength,MEM_RELEASE)
			,DllCall("CloseHandle", "PTR", hProc)
			return DllCall("MessageBox","PTR",0,"Str","Could not execute script in remote process.","STR","Error","UInt",0)
		}

		; Wait for thread to finish
		DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)

		; Get Exit code returned by ahkExec (1 = script could be executed / 0 = script could not be executed)
		DllCall("GetExitCodeThread", "PTR", hThread, "UIntP", lpExitCode)
		if !lpExitCode
			return DllCall("MessageBox","PTR",0,"Str","Could not execute script in remote process.","STR","Error","UInt",0)

		DllCall("CloseHandle", "PTR", hThread)
		,DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nScriptLength,MEM_RELEASE)
		,DllCall("CloseHandle", "PTR", hProc)
		return
	}
	else if !hDll:=DllCall("LoadLibrary","Str",dll,"PTR")
		return DllCall("MessageBox","PTR",0,"Str","Could not find " dll " library.","STR","Error","UInt",0),DllCall("CloseHandle", "PTR", hProc)
	else
	{
		hProc := DllCall("OpenProcess","UInt", PROCESS_ALL_ACCESS, "Int",0,"UInt", DllCall("GetCurrentProcessId"),"PTR")
		DllCall("GetModuleFileName","PTR",hDll,"PTR",&ModuleName,"UInt",MAX_PATH)
		DllCall("CloseHandle","PTR",hProc)
	}
	; Open Process to PID
	hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int",0, "UInt", PID,"PTR")
	if !hProc
		return DllCall("MessageBox","PTR",0,"Str","Could not open process for PID: " PID,"STR","Error","UInt",0)

	; Reserve some memory and write dll path (ANSI)
	nDirLength := VarSetCapacity(nDir, StrLen(dll)+1, 0)
	,StrPut(dll,&nDir,"CP0")

	; Reserve memory in remote process
	if !pBufferRemote := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nDirLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
		return DllCall("MessageBox","PTR",0,"Str","Could not reseve memory for process.","STR","Error","UInt",0),DllCall("CloseHandle", "PTR", hProc)

	; Write dll path to remote process memory
	DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferRemote, "Ptr", &nDir, "PTR", nDirLength, "Ptr", 0)

	; Start new thread loading our dll

	hThread:=DllCall("CreateRemoteThread","PTR",hProc,"PTR",0,"PTR",0,"PTR",LoadLibraryA,"PTR",pBufferRemote,"UInt",0,"PTR",0,"PTR")
	if !hThread
	{
		DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,"Uint",MEM_RELEASE)
		,DllCall("CloseHandle", "PTR", hProc)
		return DllCall("MessageBox","PTR",0,"Str","Could not load " dll " in remote process.","STR","Error","UInt",0)
	}
	; Wait for thread to finish
	DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)

	; Get Exit code returned by thread (HMODULE for our dll)
	DllCall("GetExitCodeThread", "PTR", hThread, "UInt*", hModule)

	; Close Thread
	DllCall("CloseHandle", "PTR", hThread)

	if (A_PtrSize=8)
	{ ; use different method to retrieve base address because GetExitCodeThread returns DWORD only
		hModule:=0,VarSetCapacity(me32, (A_PtrSize=8?48:32)+(A_IsUnicode?1032:516), 0) ;W:1080:1064, A:564:548
		;  Take a snapshot of all modules in the specified process.
		hModuleSnap := DllCall("CreateToolhelp32Snapshot","UInt", TH32CS_SNAPMODULE,"UInt", PID, "PTR" )
		if ( hModuleSnap != INVALID_HANDLE_VALUE )
		{
			; reset hModule and set the size of the structure before using it.
			NumPut((A_PtrSize=8?48:32)+(A_IsUnicode?1032:516), &me32, 0, "UInt") ;dwSize  ;W:1080:1064, A:564:548
			;  Retrieve information about the first module,
			;  and exit if unsuccessful
			if ( !DllCall("Module32First" (A_IsUnicode?"W":""),"PTR", hModuleSnap,"PTR", &me32 ) )
			{
				; Free memory used for passing dll path to remote thread
				DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
				,DllCall("CloseHandle","PTR", hModuleSnap ) ; Must clean up the snapshot object!
				return false
			}
			;  Now walk the module list of the process,and display information about each module
			while(A_Index=1 || DllCall("Module32Next" (A_IsUnicode?"W":""),"PTR",hModuleSnap,"PTR", &me32 ) )
				if (StrGet(&me32+(A_PtrSize=8?48:32)+(A_IsUnicode?512:256))=dll) ;szExePath ;W:560:544, A:304:288
				{
					hModule := NumGet(me32, A_PtrSize=8?40:28, "Ptr") ;hModule
					break
				}
			DllCall("CloseHandle","PTR",hModuleSnap) ; clean up
		}
	}

	hDll:=DllCall("LoadLibrary","Str",dll,"PTR")

	; Calculate pointer to ahkdll and ahkExec functions
	ahktextdll:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahktextdll","PTR")-hDll
	ahkExec:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahkExec","PTR")-hDll
	ahkTerminate:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahkTerminate","PTR")-hDll

	if script
	{
		nScriptLength := VarSetCapacity(nScript, (StrLen(script)+1)*(A_IsUnicode?2:1), 0)
		,StrPut(script,&nScript)
		; Reserve memory in remote process where our script will be saved
		if !pBufferScript := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nScriptLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
			return DllCall("MessageBox","PTR",0,"Str","Could not reseve memory for process.","STR","Error","UInt",0)
		,DllCall("CloseHandle", "PTR", hProc)

		; Write script to remote process memory
		DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferScript, "Ptr", &nScript, "PTR", nScriptLength, "Ptr", 0)
	}
	else
		pBufferScript:=0

	; Run ahkdll function in remote thread
	hThread := DllCall("CreateRemoteThread","PTR",hProc,"PTR",0,"PTR",0,"PTR",ahktextdll,"PTR",pBufferScript,"PTR",0,"UInt",0,"PTR")
	if !hThread
	{ ; could not start ahkdll in remote process
		; Free memory used for passing dll path to remote thread
		DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
		DllCall("CloseHandle", "PTR", hProc)
		return DllCall("MessageBox","PTR",0,"Str","Could not start ahkdll in remote process","STR","Error","UInt",0)
	}
	DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
	DllCall("GetExitCodeThread", "PTR", hThread, "UIntP", lpExitCode)

	; Release memory and handles
	DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
	DllCall("CloseHandle", "PTR", hThread)
	DllCall("CloseHandle", "PTR", hProc)

	if !lpExitCode ; thread could not be created.
		return DllCall("MessageBox","PTR",0,"Str","Could not create a thread in remote process","STR","Error","UInt",0)

	return {PID:PID,hModule:hModule,ahkExec:ahkExec,ahkTerminate:ahkTerminate,base:base}
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

08 Dec 2017, 10:02

jeeswg wrote:I tried to use the same code to drag-and-drop to an external program, namely the very small program spek.exe, but it didn't work. In case it's possible to fix this script, and drag-and-drop to external programs using AutoHotkeyMini.dll.
Yes, getting that to work would be amazing.
zcooler wrote:
just me wrote:
Maybe you could explain in more detail why it is not possible to put the thumbnails and icons inside the bluish drag-image?
Because the shell will use either the bluish default drag image or the passed drag image. It should be possible to combine them, but the shell simply doesn't. If you want to have a default style drag image with additional content you have to create an appopriate bitmap and pass its handle.
Pretty much on the same interest level getting this to work. However there are still no COM programmers using AHK, so I guess this one will remain unsolved eternally...
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: [COM] Help with the IDropSource and IDropTarget interfaces

11 Dec 2017, 06:14

zcooler wrote:
just me wrote:
Maybe you could explain in more detail why it is not possible to put the thumbnails and icons inside the bluish drag-image?
Because the shell will use either the bluish default drag image or the passed drag image. It should be possible to combine them, but the shell simply doesn't. If you want to have a default style drag image with additional content you have to create an appopriate bitmap and pass its handle.
Although this is too complex for me to grasp I was googeling how to do the "combining". There are no explicit instructions how to do it but merely clues on it. Maybe there is someone who wants to try to find something that works.

Raymond Chen (COM guru) replies to this question:
You use the IDragSourceHelper2::InitializeFromBitmap method, passing a custom bitmap. – Raymond Chen Sep 29 '12 at 18:44

Ok but how do I get a bitmap from the shell that looks like the one from Windows Explorer? Note also that explorer handles separately dragging one item vs several items (files or folders) by providing appropriate overlay drag image. – matori82 Sep 29 '12 at 18:49

SHGetFileInfo is one way. There are others. It wasn't clear to me whether the question was "How do I get this effect?" or "How do I get the bitmap so I can plug it into this effect that I know how to do?" – Raymond Chen Sep 29 '12 at 18:51
in this stockoverflow thread https://stackoverflow.com/questions/126 ... or-vista-7
Probably there are pointers to each "thumbnail" for media files or "Icon" for other files which could be retrieved from SHGetFileInfo. Dont know if just me tried that?

Here is a link which explains things more in detail (.NET), still no clear instructions though, but good info on how it all works and problematic parts and how to workaround them.
https://blogs.msdn.microsoft.com/adamro ... et-part-3/

The IDragSourceHelper2 interface has to be invoked and I dont know if just me did this either.

SHGetFileInfo function seems to be very promising if checking out what it has to offer.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: [COM] Help with the IDropSource and IDropTarget interfaces

17 Sep 2018, 21:39

jeeswg wrote:I tried to use the same code to drag-and-drop to an external program, namely the very small program spek.exe, but it didn't work. In case it's possible to fix this script, and drag-and-drop to external programs using AutoHotkeyMini.dll.
jeeswg, have You got it working?
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [COM] Help with the IDropSource and IDropTarget interfaces

17 Sep 2018, 21:46

I got something working with Spek I believe, mostly thanks to qwerty12. I will try and find it in the next few days. I had been intending to look into it again during the next few months.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], RussF, Spawnova and 136 guests