Drag & Drop from AHK GUI [DAW Reaper]

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
MusoCity
Posts: 95
Joined: 24 Mar 2018, 20:45

Drag & Drop from AHK GUI [DAW Reaper]

Post by MusoCity » 03 Jan 2020, 00:32

Is there as way to drag and drop a file FROM the AHK GUI ?
Say it is link to a song.wav in a folder and I want to drag into an application, rather than dragging a song.wav onto the AHK GUI.
So if I process a wav file with the script then when it has been processed I can drag it from a point on th AHK GUI into an application.
Last edited by BoBo on 20 Jan 2022, 01:14, edited 1 time in total.
Reason: Added [DAW Reaper] to the subject line to "search-tag" the thread.

MusoCity
Posts: 95
Joined: 24 Mar 2018, 20:45

Re: Drag & Drop from AHK GUI

Post by MusoCity » 18 Jan 2022, 02:27

Say if I have a list box with the a list of files in a folder and I want to drag a file name from the list into an application using windows drag n drop function.
Any way to do this in a AHK GUI ?

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Drag & Drop from AHK GUI

Post by swagfag » 19 Jan 2022, 04:25

sure, u just need to:
  • determine when someone is "trying to drag away a listbox entry from the listbox"
  • implement an IDropSource interface and initiate DoDragDrop()
ur journey will closely resemble this
image.png
image.png (71.26 KiB) Viewed 779 times
https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop#remarks
https://github.com/AHK-just-me/DoDragDrop

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Drag & Drop from AHK GUI

Post by teadrinker » 19 Jan 2022, 05:24

I tried to implement dragging from the listbox:

Code: Select all

folderToParse := A_MyDocuments

FileArr := {}, fileNames := ""
Loop, Files, % folderToParse . "\*.*", FD
{
   fileNames .= (A_Index = 1 ? "" : "|") . A_LoopFileName
   FileArr[A_Index - 1] := A_LoopFileFullPath
}
Gui, Font, s10
Gui, Add, ListBox, r15 w300 hwndhLB, % fileNames
Gui, Show

DllCall("MakeDragList", "Ptr", hLB)
DRAGLISTMSGSTRING := "commctrl_DragListMsg"
msg := DllCall("RegisterWindowMessage", "Str", DRAGLISTMSGSTRING)
OnMessage(msg, Func("OnDrag").Bind(FileArr))
Return

OnDrag(FileArr, wp, lp) {
   static DL_BEGINDRAG := 1157, DROPEFFECT_COPY := 1
   
   notification := NumGet(lp + 0, "UInt")
   hListBox     := NumGet(lp + A_PtrSize)
   POINT        := NumGet(lp + A_PtrSize*2, "UInt64")
   item := DllCall("LBItemFromPt", "Ptr", hListBox, "UInt64", POINT, "UInt", true)
   
   if (notification = DL_BEGINDRAG) {
      DropSource := new IDropSource
      IDataObject := GetDataObject(FileArr[item])
      DllCall("Ole32\DoDragDrop", "Ptr", IDataObject, "Ptr", DropSource.ptr, "UInt", DROPEFFECT_COPY, "UIntP", effect)
      ObjRelease(IDataObject), DropSource := ""
      Return true
   }
}

GetDataObject(filePath) {
   static IID_IDataObject  := "{0000010e-0000-0000-c000-000000000046}"
        , IID_IShellFolder := "{000214E6-0000-0000-C000-000000000046}"
        , SFGAO_FOLDER := 0x20000000
   
   SplitPath, filePath, fileName, dir
   DllCall("shell32\SHGetDesktopFolder", "PtrP", IShellFolder)
   DllCall("shell32\SHParseDisplayName", "WStr", dir, "Ptr", 0, "PtrP", _pidl, "UInt", SFGAO_FOLDER, "Ptr", 0)
   ; IShellFolder::BindToObject
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*5), "Ptr", IShellFolder, "Ptr", _pidl, "Ptr", 0
                                                         , "Ptr", CLSIDFromString(IID_IShellFolder, _), "PtrP", pIShellFolder)
   ObjRelease(IShellFolder), IShellFolder := pIShellFolder
   ; IShellFolder::ParseDisplayName
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*3), "Ptr", IShellFolder, "Ptr", 0, "Ptr", 0
                                                         , "WStr", fileName, "Ptr", 0, "PtrP", pidl, "Ptr", 0)
   VarSetCapacity(parr, A_PtrSize)
   NumPut(pidl, parr)
   ; IShellFolder::GetUIObjectOf
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*10), "Ptr", IShellFolder, "Ptr", 0, "UInt", 1, "Ptr", &parr
                                                          , "Ptr", CLSIDFromString(IID_IDataObject, _), "Ptr", 0, "PtrP", IDataObject)
   DllCall("Ole32\CoTaskMemFree", "Ptr", pidl)
   ObjRelease(IShellFolder)
   Return IDataObject
}

GuiClose() {
   ExitApp
}

class IDropSource
{
   __New() {
      this._Create()
   }
   
   _Create() {
      static Methods := [ {name: "QueryInterface"   , paramCount: 3}
                        , {name: "AddRef"           , paramCount: 1}
                        , {name: "Release"          , paramCount: 1}
                        , {name: "QueryContinueDrag", paramCount: 3}
                        , {name: "GiveFeedback"     , paramCount: 2} ]
                        
      this.SetCapacity("memArea", A_PtrSize*(Methods.Count() + 1))
      pMemArea := this.GetAddress("memArea")
      this.SetCapacity("IUnknown", A_PtrSize)
      NumPut(pMemArea, this.ptr := this.GetAddress("IUnknown"))
      refOffset := A_PtrSize * Methods.Count()
      this.table := new this.VTable(refOffset)
      
      this.Callbacks := []
      for k, v in Methods {
         Callback := new BoundFuncCallback( ObjBindMethod(this.table, v.name), v.paramCount, "Fast" )
         NumPut(Callback.addr, pMemArea + A_PtrSize*(k - 1))
         this.Callbacks.Push(Callback)
      }
      NumPut(0, pMemArea + refOffset)
   }
   
   class VTable
   {
      __New(refOffset) {
         this.refOffset := refOffset
      }
      
      QueryInterface(pIDropSource, riid, ppvObject) {
         static IID_IUnknown    := "{00000000-0000-0000-C000-000000000046}"
              , IID_IDropSource := "{00000121-0000-0000-C000-000000000046}"
              , E_NOINTERFACE := 0x80004002, S_OK := 0, _, __
              , p1 := CLSIDFromString(IID_IUnknown   ,  _)
              , p2 := CLSIDFromString(IID_IDropSource, __)
              
         if !( DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p1)
            || DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p2) )
         { ; if riid doesn't match IID_IUnknown nor IID_IDropSource
            NumPut(0, ppvObject + 0)
            Return E_NOINTERFACE
         }
         else {
            NumPut(pIDropSource, ppvObject + 0)
            DllCall(NumGet(NumGet(ppvObject + 0) + A_PtrSize), "Ptr", ppvObject)
            Return S_OK
         }
      }
      
      AddRef(pIDropSource) {
         refAddr := NumGet(pIDropSource + 0) + this.refOffset
         NumPut(refCount := NumGet(refAddr + 0, "UInt") + 1, refAddr, "UInt")
         Return refCount
      }
      
      Release(pIDropSource) {
         refAddr := NumGet(pIDropSource + 0) + this.refOffset
         NumPut(refCount := NumGet(refAddr + 0, "UInt") - 1, refAddr, "UInt")
         Return refCount
      }
      
      QueryContinueDrag(pIDropSource, fEscapePressed, grfKeyState) {
         static DRAGDROP_S_DROP   := 0x00040100
              , DRAGDROP_S_CANCEL := 0x00040101
              , MK_LBUTTON        := 0x00000001
              , S_OK              := 0x00000000
         Return fEscapePressed ? DRAGDROP_S_CANCEL : !(grfKeyState & MK_LBUTTON) ? DRAGDROP_S_DROP : S_OK
      }
      
      GiveFeedback(pIDropSource, dwEffect) {
         Return DRAGDROP_S_USEDEFAULTCURSORS := 0x00040102
      }
   }
   
   __Delete() {
      this.Delete("Callbacks")
      this.SetCapacity("memArea", 0), this.Delete("memArea")
      this.Delete("table")
   }
}

class BoundFuncCallback
{
   __New(BoundFuncObj, paramCount, options := "") {
      this.pInfo := Object( {BoundObj: BoundFuncObj, paramCount: paramCount} )
      this.addr := RegisterCallback(this.__Class . "._Callback", options, paramCount, this.pInfo)
   }
   __Delete() {
      ObjRelease(this.pInfo)
      DllCall("GlobalFree", "Ptr", this.addr, "Ptr")
   }
   _Callback(Params*) {
      Info := Object(A_EventInfo), Args := []
      Loop % Info.paramCount
         Args.Push( NumGet(Params + A_PtrSize*(A_Index - 2)) )
      Return Info.BoundObj.Call(Args*)
   }
}

CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}
It seems to work, but there is an issue. DoDragDrop doesn't return until the mouse is reliesed, so it blocks all ListBox notifications, ListBox doesn't understand what's happening and behaves strangely. Maybe someone has ideas how to workaround this.

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

Re: Drag & Drop from AHK GUI

Post by just me » 19 Jan 2022, 10:17

teadrinker wrote:... ListBox doesn't understand what's happening and behaves strangely.
What exactly do you mean?

BTW: Good work!

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Drag & Drop from AHK GUI

Post by teadrinker » 19 Jan 2022, 10:52

just me wrote: What exactly do you mean?
The window doesn't recieve DL_DRAGGING and DL_DROPPED. After the object is dropped, ListBox behaves like the mouse is not released:
 
 Image
 
This is better:

Code: Select all

folderToParse := A_MyDocuments

FileArr := {}, fileNames := ""
Loop, Files, % folderToParse . "\*.*", FD
{
   fileNames .= (A_Index = 1 ? "" : "|") . A_LoopFileName
   FileArr[A_Index - 1] := A_LoopFileFullPath
}
Gui, Font, s10
Gui, Add, ListBox, r15 w300 hwndhLB, % fileNames
Gui, Show

OnMessage(0x201, Func("WM_LBUTTONDOWN").Bind(FileArr, hLB))
Return

WM_LBUTTONDOWN(FileArr, hLB, wp, lp, msg, hwnd) {
   if !(hLB = hwnd)
      Return
   
   DllCall("GetCursorPos", "UInt64P", POINT)
   item := DllCall("LBItemFromPt", "Ptr", hLB, "UInt64", POINT, "UInt", true)
   timer := Func("DoDragDrop").Bind(FileArr[item])
   SetTimer, % timer, -100
}

DoDragDrop(filePath) {
   DropSource := new IDropSource
   IDataObject := GetDataObject(filePath)
   DllCall("Ole32\DoDragDrop", "Ptr", IDataObject, "Ptr", DropSource.ptr, "UInt", DROPEFFECT_COPY := 1, "UIntP", effect)
   ObjRelease(IDataObject), DropSource := ""
}

GetDataObject(filePath) {
   static IID_IDataObject  := "{0000010e-0000-0000-c000-000000000046}"
        , IID_IShellFolder := "{000214E6-0000-0000-C000-000000000046}"
        , SFGAO_FOLDER := 0x20000000
   
   SplitPath, filePath, fileName, dir
   DllCall("shell32\SHGetDesktopFolder", "PtrP", IShellFolder)
   DllCall("shell32\SHParseDisplayName", "WStr", dir, "Ptr", 0, "PtrP", _pidl, "UInt", SFGAO_FOLDER, "Ptr", 0)
   ; IShellFolder::BindToObject
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*5), "Ptr", IShellFolder, "Ptr", _pidl, "Ptr", 0
                                                         , "Ptr", CLSIDFromString(IID_IShellFolder, _), "PtrP", pIShellFolder)
   ObjRelease(IShellFolder), IShellFolder := pIShellFolder
   ; IShellFolder::ParseDisplayName
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*3), "Ptr", IShellFolder, "Ptr", 0, "Ptr", 0
                                                         , "WStr", fileName, "Ptr", 0, "PtrP", pidl, "Ptr", 0)
   VarSetCapacity(parr, A_PtrSize)
   NumPut(pidl, parr)
   ; IShellFolder::GetUIObjectOf
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*10), "Ptr", IShellFolder, "Ptr", 0, "UInt", 1, "Ptr", &parr
                                                          , "Ptr", CLSIDFromString(IID_IDataObject, _), "Ptr", 0, "PtrP", IDataObject)
   DllCall("Ole32\CoTaskMemFree", "Ptr", pidl)
   ObjRelease(IShellFolder)
   Return IDataObject
}

GuiClose() {
   ExitApp
}

class IDropSource
{
   __New() {
      this._Create()
   }
   
   _Create() {
      static Methods := [ {name: "QueryInterface"   , paramCount: 3}
                        , {name: "AddRef"           , paramCount: 1}
                        , {name: "Release"          , paramCount: 1}
                        , {name: "QueryContinueDrag", paramCount: 3}
                        , {name: "GiveFeedback"     , paramCount: 2} ]
                        
      this.SetCapacity("memArea", A_PtrSize*(Methods.Count() + 1))
      pMemArea := this.GetAddress("memArea")
      this.SetCapacity("IUnknown", A_PtrSize)
      NumPut(pMemArea, this.ptr := this.GetAddress("IUnknown"))
      refOffset := A_PtrSize * Methods.Count()
      this.table := new this.VTable(refOffset)
      
      this.Callbacks := []
      for k, v in Methods {
         Callback := new BoundFuncCallback( ObjBindMethod(this.table, v.name), v.paramCount, "Fast" )
         NumPut(Callback.addr, pMemArea + A_PtrSize*(k - 1))
         this.Callbacks.Push(Callback)
      }
      NumPut(0, pMemArea + refOffset)
   }
   
   class VTable
   {
      __New(refOffset) {
         this.refOffset := refOffset
      }
      
      QueryInterface(pIDropSource, riid, ppvObject) {
         static IID_IUnknown    := "{00000000-0000-0000-C000-000000000046}"
              , IID_IDropSource := "{00000121-0000-0000-C000-000000000046}"
              , E_NOINTERFACE := 0x80004002, S_OK := 0, _, __
              , p1 := CLSIDFromString(IID_IUnknown   ,  _)
              , p2 := CLSIDFromString(IID_IDropSource, __)
              
         if !( DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p1)
            || DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p2) )
         { ; if riid doesn't match IID_IUnknown nor IID_IDropSource
            NumPut(0, ppvObject + 0)
            Return E_NOINTERFACE
         }
         else {
            NumPut(pIDropSource, ppvObject + 0)
            DllCall(NumGet(NumGet(ppvObject + 0) + A_PtrSize), "Ptr", ppvObject)
            Return S_OK
         }
      }
      
      AddRef(pIDropSource) {
         refAddr := NumGet(pIDropSource + 0) + this.refOffset
         NumPut(refCount := NumGet(refAddr + 0, "UInt") + 1, refAddr, "UInt")
         Return refCount
      }
      
      Release(pIDropSource) {
         refAddr := NumGet(pIDropSource + 0) + this.refOffset
         NumPut(refCount := NumGet(refAddr + 0, "UInt") - 1, refAddr, "UInt")
         Return refCount
      }
      
      QueryContinueDrag(pIDropSource, fEscapePressed, grfKeyState) {
         static DRAGDROP_S_DROP   := 0x00040100
              , DRAGDROP_S_CANCEL := 0x00040101
              , MK_LBUTTON        := 0x00000001
              , S_OK              := 0x00000000
         Return fEscapePressed ? DRAGDROP_S_CANCEL : !(grfKeyState & MK_LBUTTON) ? DRAGDROP_S_DROP : S_OK
      }
      
      GiveFeedback(pIDropSource, dwEffect) {
         Return DRAGDROP_S_USEDEFAULTCURSORS := 0x00040102
      }
   }
   
   __Delete() {
      this.Delete("Callbacks")
      this.SetCapacity("memArea", 0), this.Delete("memArea")
      this.Delete("table")
   }
}

class BoundFuncCallback
{
   __New(BoundFuncObj, paramCount, options := "") {
      this.pInfo := Object( {BoundObj: BoundFuncObj, paramCount: paramCount} )
      this.addr := RegisterCallback(this.__Class . "._Callback", options, paramCount, this.pInfo)
   }
   __Delete() {
      ObjRelease(this.pInfo)
      DllCall("GlobalFree", "Ptr", this.addr, "Ptr")
   }
   _Callback(Params*) {
      Info := Object(A_EventInfo), Args := []
      Loop % Info.paramCount
         Args.Push( NumGet(Params + A_PtrSize*(A_Index - 2)) )
      Return Info.BoundObj.Call(Args*)
   }
}

CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}
just me wrote:BTW: Good work!
Thanks, I learned from you! :)

MusoCity
Posts: 95
Joined: 24 Mar 2018, 20:45

Re: Drag & Drop from AHK GUI

Post by MusoCity » 19 Jan 2022, 11:57

Thank you ! works perfect.

Image

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Drag & Drop from AHK GUI

Post by teadrinker » 20 Jan 2022, 00:30

Added some improvements.

Code: Select all

folderToParse := A_MyDocuments

FileArr := {}, fileNames := ""
Loop, Files, % folderToParse . "\*.*", FD
{
   fileNames .= (A_Index = 1 ? "" : "|") . A_LoopFileName
   FileArr[A_Index - 1] := A_LoopFileFullPath
}
Gui, Font, s10
Gui, Add, ListBox, r15 w300 hwndhLB, % fileNames
Gui, Show,, Drag and Drop

OnMessage(0x200, Func("WM_MOUSEMOVE").Bind(FileArr, hLB))
Return

GuiClose() {
   ExitApp
}

WM_MOUSEMOVE(FileArr, hLB, wp, lp, msg, hwnd) {
   static MK_LBUTTON := 0x0001, DROPEFFECT_COPY := 1
   if !(hLB = hwnd && wp & MK_LBUTTON)
      Return

   DllCall("GetCursorPos", "UInt64P", POINT)
   item := DllCall("LBItemFromPt", "Ptr", hLB, "UInt64", POINT, "UInt", true)
   if (item = -1)
      Return
   
   filePath := FileArr[item]
   DropSource := new IDropSource
   IDataObject := GetDataObject(filePath)
   hBitmap := GetThumbnailOrIcon(filePath, 96, 96, true)
   DragSourceHelper := new IDragSourceHelper
   DragSourceHelper.InitializeFromBitmap(48, 96, hBitmap, 0xFFFFFFFF, IDataObject)
   DllCall("Ole32\DoDragDrop", "Ptr", IDataObject, "Ptr", DropSource.ptr, "UInt", DROPEFFECT_COPY, "UIntP", effect)
   DllCall("DeleteObject", "Ptr", hBitmap)
   ObjRelease(IDataObject), DropSource := DragSourceHelper := ""
}

GetDataObject(filePath) {
   static IID_IDataObject  := "{0000010e-0000-0000-c000-000000000046}"
        , IID_IShellFolder := "{000214E6-0000-0000-C000-000000000046}"
        , SFGAO_FOLDER := 0x20000000
   
   SplitPath, filePath, fileName, dir
   DllCall("shell32\SHGetDesktopFolder", "PtrP", IShellFolder)
   DllCall("shell32\SHParseDisplayName", "WStr", dir, "Ptr", 0, "PtrP", _pidl, "UInt", SFGAO_FOLDER, "Ptr", 0)
   ; IShellFolder::BindToObject
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*5), "Ptr", IShellFolder, "Ptr", _pidl, "Ptr", 0
                                                         , "Ptr", CLSIDFromString(IID_IShellFolder, _), "PtrP", pIShellFolder)
   ObjRelease(IShellFolder), IShellFolder := pIShellFolder
   ; IShellFolder::ParseDisplayName
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*3), "Ptr", IShellFolder, "Ptr", 0, "Ptr", 0
                                                         , "WStr", fileName, "Ptr", 0, "PtrP", pidl, "Ptr", 0)
   VarSetCapacity(parr, A_PtrSize)
   NumPut(pidl, parr)
   ; IShellFolder::GetUIObjectOf
   DllCall(NumGet(NumGet(IShellFolder + 0) + A_PtrSize*10), "Ptr", IShellFolder, "Ptr", 0, "UInt", 1, "Ptr", &parr
                                                          , "Ptr", CLSIDFromString(IID_IDataObject, _), "Ptr", 0, "PtrP", IDataObject)
   DllCall("Ole32\CoTaskMemFree", "Ptr", pidl)
   ObjRelease(IShellFolder)
   Return IDataObject
}

GetThumbnailOrIcon(filePath, width, height := 0, iconOnly := false) {
   static IID_IShellItemImageFactory := "{BCC18B79-BA16-442F-80C4-8A59C30C463B}"
        , SIIGBF_ICONONLY := 0x00000004
   
   CLSIDFromString(IID_IShellItemImageFactory, GUID)
   hr := DllCall("Shell32\SHCreateItemFromParsingName", "WStr", filePath, "Ptr", 0, "Ptr", &GUID, "PtrP", IShellItemImageFactory)
   if (hr != 0)
      Return 0
   flag := iconOnly ? SIIGBF_ICONONLY : 0
   ; IShellItemImageFactory::GetImage
   DllCall(NumGet(NumGet(IShellItemImageFactory+0) + A_PtrSize*3), "Ptr", IShellItemImageFactory
                                                                 , "Int64", height << 32 | width, "Int", flag, "PtrP", hBitmap, "Int")
   ObjRelease(IShellItemImageFactory)
   Return hBitmap
}

class IDragSourceHelper
{
   __New() {
      static CLSID_DragDropHelper   := "{4657278A-411B-11D2-839A-00C04FD918D0}"
           , IID_IDropSourcetHelper := "{DE5BF786-477A-11D2-839D-00C04FD918D0}"
      this.ptr := ComObjCreate(CLSID_DragDropHelper, IID_IDropSourcetHelper)
   }
   
   __Delete() {
      ObjRelease(this.ptr)
   }
   
   InitializeFromBitmap(x_offset, y_offset, hBitmap, BGR, IDataObject) {
      VarSetCapacity(BITMAP, size := 16 + A_PtrSize*2)
      DllCall("GetObject", "Ptr", hBitmap, "UInt", size, "Ptr", &BITMAP)
      width  := NumGet(BITMAP, 4, "UInt")
      height := NumGet(BITMAP, 8, "UInt")
      
      VarSetCapacity(SHDRAGIMAGE, size := 16 + A_PtrSize*2)
      NumPut(width   , SHDRAGIMAGE)
      NumPut(height  , SHDRAGIMAGE, 4)
      NumPut(x_offset, SHDRAGIMAGE, 8)
      NumPut(y_offset, SHDRAGIMAGE, 12)
      NumPut(hBitmap , SHDRAGIMAGE, 16)
      NumPut(BGR     , SHDRAGIMAGE, 16 + A_PtrSize)
      ; IDragSourceHelper::InitializeFromBitmap
      DllCall(NumGet(NumGet(this.ptr) + A_PtrSize*3), "Ptr", this.ptr, "Ptr", &SHDRAGIMAGE, "Ptr", IDataObject)
   }
}

class IDropSource
{
   __New() {
      static Methods := [ {name: "QueryInterface"   , paramCount: 3}
                        , {name: "AddRef"           , paramCount: 1}
                        , {name: "Release"          , paramCount: 1}
                        , {name: "QueryContinueDrag", paramCount: 3}
                        , {name: "GiveFeedback"     , paramCount: 2} ]
                        
      this.SetCapacity("memArea", A_PtrSize*(Methods.Count() + 1))
      pMemArea := this.GetAddress("memArea")
      this.SetCapacity("IUnknown", A_PtrSize)
      NumPut(pMemArea, this.ptr := this.GetAddress("IUnknown"))
      refOffset := A_PtrSize * Methods.Count()
      
      this.VTable.refOffset := refOffset
      this.Callbacks := []
      for k, v in Methods {
         Callback := new this.BoundFuncCallback( ObjBindMethod(this.VTable, v.name), v.paramCount, "Fast" )
         NumPut(Callback.addr, pMemArea + A_PtrSize*(k - 1))
         this.Callbacks.Push(Callback)
      }
      NumPut(0, pMemArea + refOffset)
   }
   
   __Delete() {
      this.Delete("Callbacks")
      this.SetCapacity("memArea", 0), this.Delete("memArea")
   }
   
   class VTable
   {
      QueryInterface(pIDropSource, riid, ppvObject) {
         static IID_IUnknown    := "{00000000-0000-0000-C000-000000000046}"
              , IID_IDropSource := "{00000121-0000-0000-C000-000000000046}"
              , E_NOINTERFACE := 0x80004002, S_OK := 0, _, __
              , p1 := CLSIDFromString(IID_IUnknown   ,  _)
              , p2 := CLSIDFromString(IID_IDropSource, __)
              
         if !( DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p1)
            || DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p2) )
         { ; if riid doesn't match IID_IUnknown nor IID_IDropSource
            NumPut(0, ppvObject + 0)
            Return E_NOINTERFACE
         }
         else {
            NumPut(pIDropSource, ppvObject + 0)
            DllCall(NumGet(NumGet(ppvObject + 0) + A_PtrSize), "Ptr", ppvObject)
            Return S_OK
         }
      }
      
      AddRef(pIDropSource) {
         refAddr := NumGet(pIDropSource + 0) + this.refOffset
         NumPut(refCount := NumGet(refAddr + 0, "UInt") + 1, refAddr, "UInt")
         Return refCount
      }
      
      Release(pIDropSource) {
         refAddr := NumGet(pIDropSource + 0) + this.refOffset
         NumPut(refCount := NumGet(refAddr + 0, "UInt") - 1, refAddr, "UInt")
         Return refCount
      }
      
      QueryContinueDrag(pIDropSource, fEscapePressed, grfKeyState) {
         static DRAGDROP_S_DROP   := 0x00040100
              , DRAGDROP_S_CANCEL := 0x00040101
              , MK_LBUTTON        := 0x00000001
              , S_OK              := 0x00000000
         Return fEscapePressed ? DRAGDROP_S_CANCEL : !(grfKeyState & MK_LBUTTON) ? DRAGDROP_S_DROP : S_OK
      }
      
      GiveFeedback(pIDropSource, dwEffect) {
         Return DRAGDROP_S_USEDEFAULTCURSORS := 0x00040102
      }
   }
   
   class BoundFuncCallback
   {
      __New(BoundFuncObj, paramCount, options := "") {
         this.pInfo := Object( {BoundObj: BoundFuncObj, paramCount: paramCount} )
         this.addr := RegisterCallback(this.__Class . "._Callback", options, paramCount, this.pInfo)
      }
      __Delete() {
         ObjRelease(this.pInfo)
         DllCall("GlobalFree", "Ptr", this.addr, "Ptr")
      }
      _Callback(Params*) {
         Info := Object(A_EventInfo), Args := []
         Loop % Info.paramCount
            Args.Push( NumGet(Params + A_PtrSize*(A_Index - 2)) )
         Return Info.BoundObj.Call(Args*)
      }
   }

}

CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}

MusoCity
Posts: 95
Joined: 24 Mar 2018, 20:45

Re: Drag & Drop from AHK GUI [DAW Reaper]

Post by MusoCity » 21 Jan 2022, 03:34

Thanks

Post Reply

Return to “Ask for Help (v1)”