[COM] Help with the IDropSource and IDropTarget interfaces

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

[COM] Help with the IDropSource and IDropTarget interfaces

13 Jul 2015, 16:25

I'm not sure if this is really a Help forum topic but this is the only place for it so here goes...

I've been working on converting Sean's DragDrop script so that it works on x64. I've got it working (it works great (for the most part)) but the IDropSource and IDropTarget interfaces are created using pseudo objects and the methods are triggered using a manual approach that requires the use of the RegisterCallback command.

I would really like to get it working by creating interfaces using the built-in ComObjCreate command and if possible, controlling the events by using the ComObjConnect command but unfortunately the documentation for these interfaces are incomplete, at least for the ComObjCreate command. The interface identifier (IID) is documented but the associated COM class ID (CLSID) (if any) is nowhere to found so I can't even try to get it working.

Does anyone have any experience with these interfaces and if so, have you been able get them working using the built-in COM commands and if so, would you be willing to share your code and/or your experience?

Thanks.

Edit:
Link to Sean's version and mine (full debug mode, sorry):
https://dl.dropboxusercontent.com/u/225 ... agDrop.zip
Last edited by jballi on 13 Jul 2015, 17:45, edited 1 time in total.
User avatar
BGM
Posts: 507
Joined: 20 Nov 2013, 20:56
Contact:

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

13 Jul 2015, 16:46

I don't have any experience here, but I am sure interested, as I have a couple of projects wherein I need to do this.

Here are a few threads that I had open (not sure if they will help any):
- How to Implement Drag and Drop
- Drop Files simulation (ahk)
- Peeking Drag-n-Drop clipboard data (ahk)
- Display and Use a File's Icon during Drag-n-Drop

Do you have a copy of Sean's Drag-n-Drop script?
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

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

13 Jul 2015, 16:50

I'm also interested in this subject as I've been trying to access certain interfaces without class names..
I originally used ole/com object viewer but you can also find most CLSID's by searching HKEY_CLASSES_ROOT\Interface
Funny enough there's one for IDropTarget {00000320-0000-0000-C000-000000000046} but not one for IDropSource, not sure why.
Hope this helps at least a bit..

btw OLE/COM Object viewer is in the Resource Kit Tools found here: https://www.microsoft.com/en-ca/downloa ... x?id=17657
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

13 Jul 2015, 17:04

Thank you for your reply.
BGM wrote:Do you have a copy of Sean's Drag-n-Drop script?
Yes. My original objective was to convert that script to work on x64. I've got that working. My objective now is to get it working using the built-in AutoHotkey COM commands. That's where I need the help.
User avatar
BGM
Posts: 507
Joined: 20 Nov 2013, 20:56
Contact:

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

13 Jul 2015, 17:08

I can't download the script from the link and I'd like to look at it.
Or maybe you can share what you have come up with? (gist is a good way)
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

13 Jul 2015, 17:09

TLM wrote:I'm also interested in this subject as I've been trying to access certain interfaces without class names..
I originally used ole/com object viewer but you can also find most CLSID's by searching HKEY_CLASSES_ROOT\Interface
Funny enough there's one for IDropTarget {00000320-0000-0000-C000-000000000046} but not one for IDropSource, not sure why.
Hope this helps at least a bit..

btw OLE/COM Object viewer is in the Resource Kit Tools found here: https://www.microsoft.com/en-ca/downloa ... x?id=17657
I've looked for the appropriate classes in all of these place and in the OLE headers. But there is nothing out there (that I could find) that attaches the IDropSource and/or IDropTarget interaces to a particular CLSID. And there are thousands of them so trying them all is not very practical.
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

13 Jul 2015, 17:12

BGM wrote:I can't download the script from the link and I'd like to look at it.
Or maybe you can share what you have come up with? (gist is a good way)
I'll try to attach them both to this post. If I can't then I'll post a link to gist or similar.
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

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

13 Jul 2015, 17:22

jballi wrote:I've looked for the appropriate classes in all of these place and in the OLE headers. But there is nothing out there (that I could find) that attaches the IDropSource and/or IDropTarget interaces to a particular CLSID. And there are thousands of them so trying them all is not very practical.
You don't have to try them all.. OLE / COM Object Viewer lists IDropTarget's CLSID under interfaces for me... Image
going to the key I posted in regedit and searching for either one of them by name or IID will search for the specific interface.
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

13 Jul 2015, 17:42

TLM wrote:
jballi wrote:I've looked for the appropriate classes in all of these place and in the OLE headers. But there is nothing out there (that I could find) that attaches the IDropSource and/or IDropTarget interaces to a particular CLSID. And there are thousands of them so trying them all is not very practical.
You don't have to try them all.. OLE / COM Object Viewer lists IDropTarget's CLSID under interfaces for me... Image
going to the key I posted in regedit and searching for either one of them by name or IID will search for the specific interface.
I tried that CLSID a while back and again when you posted this and I get an AutoHotkey runtime error - No such interface supported. I don't know if it's the wrong/inappropriate CLSID for that interface or if that interface cannot be created using that command. I don't have enough COM experience to know which or to know what to try next.
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

13 Jul 2015, 17:48

I'm sorry, the only version of mine that I could pull together on short notice was full of debug code/documentation and included other functions for the test that I wrote. I included the whole thing in a zip project for your review. I've added the link to the first post.
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

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

13 Jul 2015, 17:58

I pretty much have the same issue as you but with a different interface
Have you tried the old COM_L library with this? The reason I ask is because I got as far as DllCall("ole32\CoInitialize",... and then DllCall("ole32\**the interface**",
but then realized this is what COM_L lib does already
.. going to give it a try a little later and will report back if I get it working..
would be nice if this was easier tbh.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

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

14 Jul 2015, 03:15

Great stuff so far. I'm curious whether it can be simplified.
jballi wrote: Possible AutoHotkey bugs:

* When the IDropTarget function is used with standard parameters,
Ex: IDropTarget(A,B=0,C=0,D=False,E=Whatever), it works fine on a 32-bit
version of AutoHotkey. However, on the 64-bit version of AutoHotkey,
values for many of the later variables on the list (for the example: C,
D, etc.) are invalid for some of the methods. The only way to get this
to work is to use variadic parameters and extract the parameter
values by address. Since the AutoHotkey documentation does not explain
how to do this for parameters other than the first, the exact address
offsets were discovered by trial and error. For the 32-bit version of
AutoHotkey, offsets were always 4 bytes apart. This makes sense since
most 32-bit data types are 4 bytes. For the 64-bit version of
AutoHotkey however, the offsets were sometimes 8-bytes apart but
sometimes 4 bytes. The previous parmeter's data type appears to be the
guide for what the offsets for each parameter would be. This might
somewhat explain why regular parameters don't work on the 64-bit version
of AutoHotkey. Not sure if this is an AutoHotkey bug or if this what
developers can expect to deal with writing callback functions for the
64-bit version of AutoHotkey.
I'd call it a 32-bit issue rather than a bug. Your observations are most likely related to the POINTL parameter. The size of this parameter is 8 bytes, but the size of parameters used with DllCall() or callback functions is limited to 4 bytes for AHK32. So the POINTL parameter has to be passed as two parameters. On AHK64 it fits the parameter size of 8 bytes and only one parameter is passed.
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

14 Jul 2015, 18:35

I was hoping to find someone who had some experience creating these interfaces using built-in AutoHotkey commands but either 1) no one has done it successfully yet or 2) these interfaces cannot be created using the ComObjCreate command which I assume runs the "ole32\CoCreateInstance" function. The run-time error that you get when trying to create the IDropTarget interface is 0x80004002 (E_NOINTERFACE). The Microsoft description of this error is "The specified class does not implement the requested interface, or the controlling IUnknown does not expose the requested interface." As I stated earlier, I don't know if it's the wrong/inappropriate CLSID for these interfaces or if the IDropSource and/or IDropTarget interfaces can be created using the ComObjCreate command.

If anyone has any additional information or anything else to try, please post it!
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

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

16 Aug 2015, 02:54

I've spent some time on this stuff because I wanted to use drag-and-drop. I couldn't manage to create AHK COM objects, too. But I restructured the interface wrappers to make them easier to use and added a few new features to IDropTarget.

You can find the whole result on GitHub. I add only the IDropTarget related scripts here for the forum search:

Code: Select all

; ==================================================================================================================================
; IDropTarget interface -> msdn.microsoft.com/en-us/library/ms679679(v=vs.85).aspx
; Requires: IDataObject.ahk
; ==================================================================================================================================
; Creates a new instance of the IDropTarget object.
; Parameters:
;     HWND              -  HWND of the Gui window or control which shall be used as a drop target.
;     UserFuncSuffix    -  The suffix for the names of the user-defined functions which will be called on events (see Remarks).
;     RequiredFormats   -  An array containing the numeric clipboard formats required to permit drop.
;                          If omitted, only 15 (CF_HDROP) used for dropping files will be required.
;     Register          -  If set to True the target will be registered as a drop target on creation.
;                          Otherwise you have to call the RegisterDragDrop() method manually to activate the drop target.
;     UseHelper         -  Use the shell helper object if available (True/False).
; Return value:
;     New IDropTarget instance on success; in case of parameter errors, False.
; Remarks:
;     The interface permits up to 4 user-defined functions which will be called from the related methods:
;        IDropTargetOnEnter      Optional, called from IDropTarget.DragEnter()
;        IDropTargetOnOver       Optional, called from IDropTarget.DragOver()
;        IDropTargetOnLeave      Optional, called from IDropTarget.DragLeave()
;        IDropTargetOnDrop       Mandatory, called from IDropTarget.Drop()
;     The suffix passed in UserFuncSuffix which will be appended to this names to identify the instance specific functions.
;
;     Function parameters:
;        IDropTargetOnDrop and IDropTargetOnEnter must accept at least 6 parameters:
;           TargetObject   -  This instance.
;           pDataObj       -  A pointer to the IDataObject interface on the data object being dropped.
;           KeyState       -  The current state of the mouse buttons and keyboard modifier keys.
;           X              -  The current X coordinate of the cursor in screen coordinates.
;           Y              -  The current Y coordinate of the cursor in screen coordinates.
;           DropEffect     -  The drop effect determined by the Drop() method.
;        IDropTargetOnOver must accept at least 5 parameters:
;           TargetObject   -  This instance.
;           KeyState       -  The current state of the mouse buttons and keyboard modifier keys.
;           X              -  The current X coordinate of the cursor in screen coordinates.
;           Y              -  The current Y coordinate of the cursor in screen coordinates.
;           DropEffect     -  The drop effect determined by the Drop() method.
;        IDropTargetOnLeave must accept at least 1 parameter:
;           TargetObject   -  This instance.
;
;     What the functions must return:
;        The return value of IDropTargetOnDrop, IDropTargetOnEnter, and IDropTargetOnOver is used as the drop effect reported
;        as the result of the drop operation. In the easiest case the function returns the value passed in DropEffect.
;        Otherwise, it must return one of the following values:
;           0 (DROPEFFECT_NONE)
;           1 (DROPEFFECT_COPY)
;           2 (DROPEFFECT_MOVE)
;        The return value of IDropTargetOnLeave is not used.
;
;     As is the interface supports only left-dragging and permits DROPEFFECT_COPY and DROPEFFECT_MOVE. The default effect is
;     DROPEFFECT_COPY. It will be switched to DROPEFFECT_MOVE if either Ctrl or Shift is pressed. You can overwrite the default
;     from the IDropTargetOnEnter user function.
;
;     The dropped data have to be processed completely by the IDropTargetOnDrop user function.
; ==================================================================================================================================
IDropTarget_Create(HWND, UserFuncSuffix, RequiredFormats := "", Register := True, UseHelper := True) {
   Return New IDropTarget(HWND, UserFuncSuffix, RequiredFormats, Register, UseHelper)
}
; ==================================================================================================================================
Class IDropTarget {
   __New(HWND, UserFuncSuffix, RequiredFormats := "", Register := True, UseHelper := True) {
      Static Methods := ["QueryInterface", "AddRef", "Release", "DragEnter", "DragOver", "DragLeave", "Drop"]
      Static Params := (A_PtrSize = 8 ? [3, 1, 1, 5, 4, 1, 5] : [3, 1, 1, 6, 5, 1, 6])
      Static DefaultFormat := 15 ; CF_HDROP
      Static DropFunc := "IDropTargetOnDrop"
      Static EnterFunc := "IDropTargetOnEnter"
      Static OverFunc := "IDropTargetOnOver"
      Static LeaveFunc := "IDropTargetOnLeave"
      Static CLSID_IDTH := "{4657278A-411B-11D2-839A-00C04FD918D0}" ; CLSID_DragDropHelper
      Static IID_IDTH := "{4657278B-411B-11D2-839A-00C04FD918D0}"   ; IID_IDropTargetHelper
      If This.Base.HasKey("Ptr")
         Return False
      UserFunc := DropFunc . UserFuncSuffix
      If !IsFunc(UserFunc) || (Func(UserFunc).MinParams < 6)
         Return False
      This.DropUserFunc := Func(UserFunc)
      UserFunc := EnterFunc . UserFuncSuffix
      If (IsFunc(UserFunc) && (Func(UserFunc).MinParams > 5))
         This.EnterUserFunc := Func(UserFunc)
      UserFunc := OverFunc . UserFuncSuffix
      If (IsFunc(UserFunc) && (Func(UserFunc).MinParams > 4))
         This.OverUserFunc := Func(UserFunc)
      UserFunc := LeaveFunc . UserFuncSuffix
      If (IsFunc(UserFunc) && (Func(UserFunc).MinParams > 0))
         This.LeaveUserFunc := Func(UserFunc)
      This.HWND := HWND
      This.Registered := False
      If IsObject(RequiredFormats)
         This.Required := RequiredFormats
      Else
         This.Required := [DefaultFormat]
      This.PreferredDropEffect := 0
      SizeOfVTBL := (Methods.Length() + 2) * A_PtrSize
      This.SetCapacity("VTBL", SizeOfVTBL)
      This.Ptr := This.GetAddress("VTBL")
      DllCall("RtlZeroMemory", "Ptr", This.Ptr, "Ptr", SizeOfVTBL)
      NumPut(This.Ptr + A_PtrSize, This.Ptr + 0, "UPtr")
      For Index, Method In Methods {
         CB := RegisterCallback("IDropTarget." . Method, "", Params[Index], &This)
         NumPut(CB, This.Ptr + 0, A_Index * A_PtrSize, "UPtr")
      }
      This.Helper := ComObjCreate(CLSID_IDTH, IID_IDTH)
      If (Register)
         If !This.RegisterDragDrop()
            Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Registers window/control as a drop target.
   ; -------------------------------------------------------------------------------------------------------------------------------
   RegisterDragDrop() {
      If !(This.Registered)
         If DllCall("Ole32.dll\RegisterDragDrop", "Ptr", This.HWND, "Ptr", This.Ptr, "Int")
            Return False
      Return (This.Registered := True)
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Revokes registering of the window/control as a drop target.
   ; This method should be called before the window/control will be destroyed.
   ; -------------------------------------------------------------------------------------------------------------------------------
   RevokeDragDrop() {
      If (This.Registered)
         DllCall("Ole32.dll\RevokeDragDrop", "Ptr", This.HWND)
      Return !(This.Registered := False)
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Notifies the drag-image manager, if used, to show or hide the drag image.
   ; Parameter:
   ;     Show  -  If true, the drag image will be shown; otherwise it will be hidden.
   ; -------------------------------------------------------------------------------------------------------------------------------
   HelperShow(Show := True) {
      Static HelperShow := A_PtrSize * 7
      If (This.Helper) {
         pVTBL := NumGet(This.Helper + 0, "UPtr")
         , DllCall(NumGet(pVTBL + HelperShow, "UPtr"), "Ptr", This.Helper, "UInt", !!Show)
         Return True
      }
      Return False
   }
   ; ===============================================================================================================================
   ; The following methods must not be called directly, they are reserved for internal and system use.
   ; ===============================================================================================================================
   __Delete() {
      This.RevokeDragDrop()
      While (CB := NumGet(This.Ptr + (A_PtrSize * A_Index), "Ptr"))
         DllCall("GlobalFree", "Ptr", CB)
      If (This.Helper)
         ObjRelease(This.Helper)
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   QueryInterface(RIID, PPV) {
      ; IUnknown -> msdn.microsoft.com/en-us/library/ms682521(v=vs.85).aspx
      Static IID := "{00000122-0000-0000-C000-000000000046}"
      VarSetCapacity(QID, 80, 0)
      QIDLen := DllCall("Ole32.dll\StringFromGUID2", "Ptr", RIID, "Ptr", &QID, "Int", 40, "Int")
      If (StrGet(&QID, QIDLen, "UTF-16") = IID) {
         NumPut(This, PPV + 0, "Ptr")
         Return 0 ; S_OK
      }
      Else {
         NumPut(0, PPV + 0, "Ptr")
         Return 0x80004002 ; E_NOINTERFACE
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   AddRef() {
      ; IUnknown -> msdn.microsoft.com/en-us/library/ms691379(v=vs.85).aspx
      ; Reference counting is not needed in this case.
      Return 1
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   Release() {
      ; IUnknown -> msdn.microsoft.com/en-us/library/ms682317(v=vs.85).aspx
      ; Reference counting is not needed in this case.
      Return 0
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   DragEnter(pDataObj, grfKeyState, P3 := "", P4 := "", P5 := "") {
      ; DragEnter -> msdn.microsoft.com/en-us/library/ms680106(v=vs.85).aspx
      ; Params 32: IDataObject *pDataObj, DWORD grfKeyState, LONG x, LONG y, DWORD *pdwEffect
      ; Params 64: IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect
      Static HelperEnter := A_PtrSize * 3
      Instance := Object(A_EventInfo)
      If (A_PtrSize = 8)
         X := P2 & 0xFFFFFFFF, Y := P2 >> 32
      Else
         X := P2, Y := P3
      Effect := 0
      If !(grfKeyState & 0x02) { ; right-drag isn't supported by default
         For Each, Format In Instance.Required {
            IDataObject_CreateFormatEtc(FORMATETC, Format)
            If (Effect := IDataObject_QueryGetData(pDataObj, FORMATETC))
               Break
         }
      }
      If (Effect) && (Instance.EnterUserFunc)
         Effect := Instance.EnterUserFunc.Call(Instance, pDataObj, grfKeyState, X, Y, Effect)
      Instance.PreferredDropEffect := Effect
      ; If Ctrl and/or Shift is pressed swap the effect
      Effect ^= grfKeyState & 0x0C ? 3 : 0
      ; Call IDropTargetHelper, if created
      If (Instance.Helper) {
         VarSetCapacity(PT, 8, 0)
         , NumPut(X, PT, 0, "Int")
         , NumPut(Y, PT, 0, "Int")
         , pVTBL := NumGet(Instance.Helper + 0, "UPtr")
         , DllCall(NumGet(pVTBL + HelperEnter, "UPtr")
                 , "Ptr", Instance.Helper, "Ptr", Instance.HWND, "Ptr", pDataObj, "Ptr", &PT, "UInt", Effect, "Int")
      }
      NumPut(Effect, (A_PtrSize = 8 ? P4 : P5) + 0, "UInt")
      Return 0 ; S_OK
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   DragOver(grfKeyState, P2 := "", P3 := "", P4 := "") {
      ; DragOver -> msdn.microsoft.com/en-us/library/ms680129(v=vs.85).aspx
      ; Params 32: DWORD grfKeyState, LONG x, LONG y, DWORD *pdwEffect
      ; Params 64: DWORD grfKeyState, POINTL pt, DWORD *pdwEffect
      Static HelperOver := A_PtrSize * 5
      Instance := Object(A_EventInfo)
      If (A_PtrSize = 8)
         X := P2 & 0xFFFFFFFF, Y := P2 >> 32
      Else
         X := P2, Y := P3
      ; If Ctrl and/or Shift is pressed swap the effect
      Effect := Instance.PreferredDropEffect ^ (grfKeyState & 0x0C ? 3 : 0)
      If (Effect) && (Instance.OverUserFunc)
         Effect := Instance.OverUserFunc.Call(Instance, grfKeyState, X, Y, Effect)
      If (Instance.Helper) {
         VarSetCapacity(PT, 8, 0)
         , NumPut(X, PT, 0, "Int")
         , NumPut(Y, PT, 0, "Int")
         , pVTBL := NumGet(Instance.Helper + 0, "UPtr")
         , DllCall(NumGet(pVTBL + HelperOver, "UPtr"), "Ptr", Instance.Helper, "Ptr", &PT, "UInt", Effect, "Int")
      }
      NumPut(Effect, (A_PtrSize = 8 ? P3 : P4) + 0, "UInt")
      Return 0 ; S_OK
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   DragLeave() {
      ; DragLeave -> msdn.microsoft.com/en-us/library/ms680110(v=vs.85).aspx
      Static HelperLeave := A_PtrSize * 4
      Instance := Object(A_EventInfo)
      Instance.PreferredDropEffect := 0
      If (Instance.LeaveUserFunc)
         Instance.LeaveUserFunc.Call(Instance)
      If (Instance.Helper) {
         pVTBL := NumGet(Instance.Helper + 0, "UPtr"), DllCall(NumGet(pVTBL + HelperLeave, "UPtr"), "Ptr", Instance.Helper)
      }
      Return 0 ; S_OK
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   Drop(pDataObj, grfKeyState, P3 := "", P4 := "", P5 := "") {
      ; Drop -> msdn.microsoft.com/en-us/library/ms687242(v=vs.85).aspx
      ; Params 32: IDataObject *pDataObj, DWORD grfKeyState, LONG x, LONG y, DWORD *pdwEffect
      ; Params 64: IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect
      Static HelperDrop := A_PtrSize * 6
      Instance := Object(A_EventInfo)
      If (A_PtrSize = 8)
         X := P3 & 0xFFFFFFFF, Y := P3 >> 32
      Else
         X := P3, Y := P4
      Effect := Instance.PreferredDropEffect ^ (grfKeyState & 0x0C ? 3 : 0)
      Effect := Instance.DropUserFunc.Call(Instance, pDataObj, grfKeyState, X, Y, Effect)
      NumPut(Effect, (A_PtrSize = 8 ? P4 : P5) + 0, "UInt")
      If (Instance.Helper) {
         VarSetCapacity(PT, 8, 0)
         , NumPut(X, PT, 0, "Int")
         , NumPut(Y, PT, 0, "Int")
         , pVTBL := NumGet(Instance.Helper + 0, "UPtr")
         , DllCall(NumGet(pVTBL + HelperDrop, "UPtr"), "Ptr", Instance.Helper, "Ptr", pDataObj, "Ptr", &PT, "UInt", Effect, "Int")
      }
      ObjRelease(pDataObj)
      Return 0 ; S_OK
   }
}
; ==================================================================================================================================
#Include *i %A_ScriptDir%\IDataObject.ahk
; ==================================================================================================================================

Code: Select all

; ==================================================================================================================================
; IDataObject interface -> msdn.microsoft.com/en-us/library/ms688421(v=vs.85).aspx
; Partial implementation.
; Requires: IEnumFORMATETC.ahk
; ==================================================================================================================================
IDataObject_EnumFormatEtc(pDataObj) {
   ; EnumFormatEtc -> msdn.microsoft.com/en-us/library/ms683979(v=vs.85).aspx
   ; DATADIR_GET = 1
   Static EnumFormatEtc := A_PtrSize * 8
   pVTBL := NumGet(pDataObj + 0, "UPtr")
   If !DllCall(NumGet(pVTBL + EnumFormatEtc, "UPtr"), "Ptr", pDataObj, "UInt", 1, "PtrP", ppenumFormatEtc, "Int")
      Return ppenumFormatEtc
   Return False
}
; ==================================================================================================================================
IDataObject_GetData(pDataObj, ByRef FORMATETC, ByRef Size, ByRef Data) {
   ; GetData -> msdn.microsoft.com/en-us/library/ms678431(v=vs.85).aspx
   Static GetData := A_PtrSize * 3
   Data := ""
   , Size := -1
   , VarSetCapacity(STGMEDIUM, 24, 0) ; 64-bit
   , pVTBL := NumGet(pDataObj + 0, "UPtr")
   If !DllCall(NumGet(pVTBL + GetData, "UPtr"), "Ptr", pDataObj, "Ptr", &FORMATETC, "Ptr", &STGMEDIUM, "Int") {
      If (NumGet(STGMEDIUM, "UInt") = 1) { ; TYMED_HGLOBAL
         hGlobal := NumGet(STGMEDIUM, A_PtrSize, "UPtr")
         , pGlobal := DllCall("GlobalLock", "Ptr", hGlobal, "Uptr")
         , Size := DllCall("GlobalSize", "Ptr", hGlobal, "UPtr")
         , VarSetCapacity(Data, Size, 0)
         , DllCall("RtlMoveMemory", "Ptr", &Data, "Ptr", pGlobal, "Ptr", Size)
         , DllCall("GlobalUnlock", "Ptr", hGlobal)
         , DllCall("Ole32.dll\ReleaseStgMedium", "Ptr", &STGMEDIUM)
         Return True
      }
      DllCall("Ole32.dll\ReleaseStgMedium", "Ptr", &STGMEDIUM)
   }
   Return False
}
; ==================================================================================================================================
IDataObject_QueryGetData(pDataObj, ByRef FORMATETC) {
   ; QueryGetData -> msdn.microsoft.com/en-us/library/ms680637(v=vs.85).aspx
   Static QueryGetData := A_PtrSize * 5
   pVTBL := NumGet(pDataObj + 0, "UPtr")
   Return !DllCall(NumGet(pVTBL + QueryGetData, "UPtr"), "Ptr", pDataObj, "Ptr", &FORMATETC, "Int")
}
; ==================================================================================================================================
IDataObject_SetData(pDataObj, ByRef FORMATETC, ByRef STGMEDIUM) {
   ; SetData -> msdn.microsoft.com/en-us/library/ms686626(v=vs.85).aspx
   Static SetData := A_PtrSize * 7
   pVTBL := NumGet(pDataObj + 0, "UPtr")
   Return !DllCall(NumGet(pVTBL + SetData, "UPtr"), "Ptr", pDataObj, "Ptr", &FORMATETC, "Ptr", &STGMEDIUM, "Int", True, "Int")
}
; ==================================================================================================================================
; Auxiliary functions to get/set data of the data object.
; ==================================================================================================================================
; FORMATETC structure -> msdn.microsoft.com/en-us/library/ms682242(v=vs.85).aspx
; ==================================================================================================================================
IDataObject_CreateFormatEtc(ByRef FORMATETC, Format, Aspect := 1, Index := -1, Tymed := 1) {
   ; DVASPECT_CONTENT = 1, Index all data = -1, TYMED_HGLOBAL = 1
   VarSetCapacity(FORMATETC, 32, 0) ; 64-bit
   , NumPut(Format, FORMATETC, 0, "Ushort")
   , NumPut(Aspect, FORMATETC, A_PtrSize = 8 ? 16 : 8 , "UInt")
   , NumPut(Index, FORMATETC, A_PtrSIze = 8 ? 20 : 12, "Int")
   , NumPut(Tymed, FORMATETC, A_PtrSize = 8 ? 24 : 16, "UInt")
   Return &FORMATETC
}
; ==================================================================================================================================
IDataObject_ReadFormatEtc(ByRef FORMATETC, ByRef Format, ByRef Device, ByRef Aspect, ByRef Index, ByRef Tymed) {
   Format := NumGet(FORMATETC, OffSet := 0, "UShort")
   , Device := NumGet(FORMATETC, Offset += A_PtrSize, "UPtr")
   , Aspect := NumGet(FORMATETC, Offset += A_PtrSize, "UInt")
   , Index  := NumGet(FORMATETC, Offset += 4, "Int")
   , Tymed  := NumGet(FORMATETC, Offset += 4, "UInt")
}
; ==================================================================================================================================
; Get/Set format data.
; ==================================================================================================================================
IDataObject_GetDroppedFiles(pDataObj, ByRef DroppedFiles) {
   ; msdn.microsoft.com/en-us/library/bb773269(v=vs.85).aspx
   IDataObject_CreateFormatEtc(FORMATETC, 15) ; CF_HDROP
   DroppedFiles := []
   If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
      Offset := NumGet(Data, 0, "UInt")
      CP := NumGet(Data, 16, "UInt") ? "UTF-16" : "CP0"
      Shift := (CP = "UTF-16")
      While (File := StrGet(&Data + Offset, CP)) {
         DroppedFiles.Push(File)
         Offset += (StrLen(File) + 1) << Shift
      }
   }
   Return DroppedFiles.Length()
}
; ==================================================================================================================================
IDataObject_GetLogicalDropEffect(pDataObj, ByRef DropEffect) {
   Static LogicalDropEffect := DllCall("RegisterClipboardFormat", "Str", "Logical Performed DropEffect")
   IDataObject_CreateFormatEtc(FORMATETC, LogicalDropEffect)
   DropEffect := ""
   If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
      DropEffect := NumGet(Data, "UChar")
      Return True
   }
   Return False
}
; ==================================================================================================================================
IDataObject_GetPerformedDropEffect(pDataObj, ByRef DropEffect) {
   Static PerformedDropEffect := DllCall("RegisterClipboardFormat", "Str", "Performed DropEffect")
   IDataObject_CreateFormatEtc(FORMATETC, PerformedDropEffect)
   DropEffect := ""
   If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
      DropEffect := NumGet(Data, "UChar")
      Return True
   }
   Return False
}
; ==================================================================================================================================
IDataObject_GetText(pDataObj, ByRef Txt) {
   Static CF_NATIVE := A_IsUnicode ? 13 : 1 ; CF_UNICODETEXT : CF_TEXT
   IDataObject_CreateFormatEtc(FORMATETC, CF_NATIVE)
   Txt := ""
   If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
      Txt := StrGet(Data, Size >> !!A_IsUnicode)
      Return True
   }
   Return False
}
; ==================================================================================================================================
IDataObject_SetLogicalDropEffect(pDataObj, DropEffect) {
   Static LogicalDropEffect := DllCall("RegisterClipboardFormat", "Str", "Logical Performed DropEffect")
   IDataObject_CreateFormatEtc(FORMATETC, LogicalDropEffect)
   , VarSetCapacity(STGMEDIUM, 24, 0) ; 64-bit
   , NumPut(1, STGMEDIUM, "UInt") ; TYMED_HGLOBAL
   ; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
   , hMem := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 4, "UPtr")
   , pMem := DllCall("GlobalLock", "Ptr", hMem)
   , NumPut(DropEffect, pMem + 0, "UChar")
   , DllCall("GlobalUnlock", "Ptr", hMem)
   , NumPut(hMem, STGMEDIUM, A_PtrSize, "UPtr")
   Return IDataObject_SetData(pDataObj, FORMATETC, STGMEDIUM)
}
; ==================================================================================================================================
IDataObject_SetPerformedDropEffect(pDataObj, DropEffect) {
   Static PerformedDropEffect := DllCall("RegisterClipboardFormat", "Str", "Performed DropEffect")
   IDataObject_CreateFormatEtc(FORMATETC, PerformedDropEffect)
   , VarSetCapacity(STGMEDIUM, 24, 0) ; 64-bit
   , NumPut(1, STGMEDIUM, "UInt") ; TYMED_HGLOBAL
   ; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
   , hMem := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 4, "UPtr")
   , pMem := DllCall("GlobalLock", "Ptr", hMem)
   , NumPut(DropEffect, pMem + 0, "UChar")
   , DllCall("GlobalUnlock", "Ptr", hMem)
   , NumPut(hMem, STGMEDIUM, A_PtrSize, "UPtr")
   Return IDataObject_SetData(pDataObj, FORMATETC, STGMEDIUM)
}
; ==================================================================================================================================
IDataObject_SHFileOperation(pDataObj, TargetPath, Operation, HWND := 0) {
   ; SHFileOperation -> msdn.microsoft.com/en-us/library/bb762164(v=vs.85).aspx
   If Operation Not In 1,2
      Return False
   IDataObject_CreateFormatEtc(FORMATETC, 15) ; CF_HDROP
   If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
      Offset := NumGet(Data, 0, "UInt") ; offset of the file list
      IsUnicode := NumGet(Data, 16, "UInt") ; 1: Unicode, 0: ANSI
      TargetLen := StrPut(TargetPath, IsUnicode ? "UTF-16" : "CP0") + 2
      VarSetCapacity(Target, TargetLen << !!IsUnicode, 0)
      StrPut(TargetPath, &Target, IsUnicode ? "UTF-16" : "CP0")
      SHFOSLen := A_PtrSize * (A_PtrSize = 8 ? 7 : 8)
      VarSetCapacity(SHFOS, SHFOSLen, 0) ; SHFILEOPSTRUCT
      NumPut(HWND, SHFOS, 0, "UPtr")
      NumPut(Operation, SHFOS, A_PtrSize, "UInt") ; FO_MOVE = 1, FO_COPY = 2, so we have to swap the DropEffect
      NumPut(&Data + Offset, SHFOS, A_PtrSize * 2, "UPtr")
      NumPut(&Target, SHFOS, A_PtrSize * 3, "UPtr")
      NumPut(0x0200, SHFOS, A_PtrSize * 4, "UInt") ; FOF_NOCONFIRMMKDIR
      If (IsUnicode)
         Return DllCall("Shell32.dll\SHFileOperationW", "Ptr", &SHFOS, "Int")
      Else
         Return DllCall("Shell32.dll\SHFileOperationA", "Ptr", &SHFOS, "Int")
   }
}
; ==================================================================================================================================
#Include *i %A_ScriptDir%\IEnumFORMATETC.ahk
; ==================================================================================================================================

Code: Select all

; ==================================================================================================================================
; IEnumFORMATETC interface -> msdn.microsoft.com/en-us/library/ms682337(v=vs.85).aspx
; Partial implementation, 'Clone' method is missing.
; ==================================================================================================================================
IEnumFORMATETC_Next(pEnumObj, ByRef FORMATETC) {
   ; Next -> msdn.microsoft.com/en-us/library/dd542673(v=vs.85).aspx
   Static Next := A_PtrSize * 3
   VarSetCapacity(FORMATETC, A_PtrSize = 8 ? 32 : 20, 0)
   , pVTBL := NumGet(pEnumObj + 0, "UPtr")
   Return !DllCall(NumGet(pVTBL + Next, "UPtr"), "Ptr", pEnumObj, "UInt", 1, "Ptr", &FORMATETC, "Ptr", 0, "Int")
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumFORMATETC_Reset(pEnumObj) {
   ; Reset -> msdn.microsoft.com/en-us/library/dd542674(v=vs.85).aspx
   Static Reset := A_PtrSize * 5
   pVTBL := NumGet(pEnumObj + 0, "UPtr")
   Return !DllCall(NumGet(pVTBL + Reset, "UPtr"), "Ptr", pEnumObj, "Int")
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumFORMATETC_Skip(pEnumObj, ItemCount) {
   ; Skip -> msdn.microsoft.com/en-us/library/dd542674(v=vs.85).aspx
   Static Skip := A_PtrSize * 4
   pVTBL := NumGet(pEnumObj + 0, "UPtr")
   Return !DllCall(NumGet(pVTBL + Skip, "UPtr"), "Ptr", pEnumObj, "UInt", ItemCount, "Int")
}
; ==================================================================================================================================
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

16 Aug 2015, 16:44

I've only spent a few minutes looking at it but so far, it looks pretty cool. You're doing a few things that I had never seen before. For example, keeping the original source drag icon definitely adds to the user experience.

Good work! You should publish this in the main script forum. A few "real life" examples wouldn't hurt. Thanks for sharing.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

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

17 Aug 2015, 01:33

I found a few articles about the IDragSourceHelper and IDropTargetHelper interfaces: It was somewhat easy to get the target helper working, but I had no luck with the source helper as yet.

The IDropTarget part seems to work stable now. But I still want to get the IDragSourceHelper working, too.

This is what I tried so far:

Code: Select all

; ==================================================================================================================================
; DoDragDrop -> msdn.microsoft.com/en-us/library/ms678486(v=vs.85).aspx
; Requires: IDataObject.ahk, IDropSource.ahk
; ==================================================================================================================================
; Carries out an OLE drag and drop operation using the current contents of the clipboard.
; Return values:
;     If the data have been dropped successfully, the functions returns the performed drop operation (i.e. 1 for
;     DROPEFFECT_COPY or 2 for DROPEFFECT_MOVE). In all other cases the function returns 0.
;     If DROPEFFECT_MOVE is returned, the drag source should remove the data.
; ==================================================================================================================================
DoDragDrop() {
   ; DRAGDROP_S_DROP = 0x40100
   Static DropEffects := 0x03 ; DROPEFFECT_COPY | DROPEFFECT_MOVE
   IDS := IDropSource_Create()
   If !DllCall("Ole32.dll\OleGetClipboard", "PtrP", pDataObj, "UInt") {
      HBITMAP := IDragSourceHelper_LoadImage(A_ScriptDir . "\Test.bmp", 64, 64)
      IDSH := IDragSourceHelper_CreateFromBitmap(pDataObj, HBITMAP, 64, 64)
;       IDSH := IDragSourceHelper_CreateFromWindow(pDataObj, TVHWND)
      RC := DllCall("Ole32.dll\DoDragDrop","Ptr", pDataObj, "Ptr", IDS, "UInt", DropEffects, "PtrP", Effect, "Int")
      If IDataObject_GetPerformedDropEffect(pDataObj, PerformedDropEffect)
         Effect := PerformedDropEffect
      ObjRelease(pDataObj)
      ObjRelease(IDSH)
      DllCall("Gdi32.dll\DeleteObject", "Ptr", HBITMAP)
   }
   IDropSource_Free(IDS)
   Return (RC = 0x40100 ? Effect : 0)
}
; ==================================================================================================================================
#Include *i %A_ScriptDir%\IDataObject.ahk
#Include *i %A_ScriptDir%\IDropSource.ahk
; ==================================================================================================================================

Code: Select all

; ==================================================================================================================================
; IDragSourceHelper interface -> msdn.microsoft.com/en-us/library/bb762034(v=vs.85).aspx
; CLSID_DragDropHelper     "{4657278A-411B-11D2-839A-00C04FD918D0}"
; IID_IDropSourcetHelper   "{DE5BF786-477A-11D2-839D-00C04FD918D0}"
; ==================================================================================================================================
; Initializes the drag-image manager for a windowless control.
; ==================================================================================================================================
IDragSourceHelper_CreateFromBitmap(pDataObj, HBITMAP, Width, Height, ColorKey := 0x00FFFFFF, OffCX := 0, OffCY := 0) {
   Static InitializeFromBitmap := A_PtrSize * 3
   VarSetCapacity(SHDI, 32, 0) ; SHDRAGIMAGE structure, 64-bit size
   NumPut(Width, SHDI, 0, "Int")
   NumPut(Height, SHDI, 4, "Int")
   NumPut(OffCX, SHDI, 8, "Int")
   NumPut(OffCY, SHDI, 12, "Int")
   NumPut(20, SHDI, 8, "Int")
   NumPut(20, SHDI, 12, "Int")
   NumPut(HBITMAP, SHDI, 16, "UPtr")
   NumPut(ColorKey, SHDI, 16 + A_PtrSize, "UInt")
   If (pIDSH := ComObjCreate("{4657278A-411B-11D2-839A-00C04FD918D0}", "{DE5BF786-477A-11D2-839D-00C04FD918D0}")) {
      pVTBL := NumGet(pIDSH + 0, "UPtr")
      ToolTip, % DllCall(NumGet(pVTBL + 0, InitializeFromBitmap, "UPtr"), "Ptr", pIDSH, "Ptr", &SHDI, "Ptr", pDataObj, "Int")
      Return pIDSH
   }
   Return False
}
; ==================================================================================================================================
; Initializes the drag-image manager for a control with a window.
; ==================================================================================================================================
IDragSourceHelper_CreateFromWindow(pDataObj, HWND, OffCX := 0, OffCY := 0) {
   Static InitializeFromWindow := A_PtrSize * 4
   If (pIDSH := ComObjCreate("{4657278A-411B-11D2-839A-00C04FD918D0}", "{DE5BF786-477A-11D2-839D-00C04FD918D0}")) {
      pVTBL := NumGet(pIDSH + 0, "UPtr")
      ToolTip, % DllCall(NumGet(pVTBL + 0, InitializeFromWindow, "UPtr"), "Ptr", pIDSH, "Ptr", HWND, "Ptr", 0, "Ptr", pDataObj, "Int")
      Return pIDSH
   }
   Return False
}
; ==================================================================================================================================
; Auxiliary functions:
; ==================================================================================================================================
IDragSourceHelper_LoadImage(ImagePath, W := 0, H := 0) {
   HBITMAP := 0
   GDIPModule := DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
   VarSetCapacity(SI, 24, 0)
   NumPut(1, SI, 0, "UInt")
   DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", GDIPToken, "Ptr", &SI, "Ptr", 0)
   DllCall("Gdiplus.dll\GdipCreateBitmapFromFile", "WStr", ImagePath, "PtrP", GDIPBitmap)
	DllCall("Gdiplus.dll\GdipGetImageWidth", "Ptr", GDIPBitmap, "UIntP", PW)
	DllCall("Gdiplus.dll\GdipGetImageHeight", "Ptr", GDIPBitmap, "UIntP", PH)
   DllCall("Gdiplus.dll\GdipCreateHBITMAPFromBitmap", "Ptr", GDIPBitmap, "PtrP", HBITMAP, "UInt", 0xFFFFFFFF)
   DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", GDIPBitmap)
   DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GDIPToken)
   DllCall("FreeLibrary", "Ptr", GDIPModule)
   If (W = 0)
      W := PW
   If (H = 0)
      H := PH
   If (W <> PW) || (H <> PH)
      HBITMAP := DllCall("CopyImage", "Ptr", HBITMAP, "UInt", 0, "Int", W, "Int", H, "UInt", 0x800A, "UPtr") ; 0x200A
   Return HBITMAP
}
InitializeFromWindow is sometimes working somehow. But InitializeFromBitmap always retrieves the meaningful error E_FAIL. Due to my very limited knowledge about COM I don't have any idea, what else I could try. Maybe someone more experienced could jump in and help.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

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

23 Aug 2015, 02:30

Nobody can contribute?
User avatar
jballi
Posts: 723
Joined: 29 Sep 2013, 17:34

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

24 Aug 2015, 00:59

just me wrote:Nobody can contribute?
To be fair, coding for COM is tough. Also, hosting this in the "Ask For Help" forum will probably not provide the right kind of traffic for this topic. Not sure where to move it though.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

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

24 Aug 2015, 02:37

jballi wrote:To be fair, coding for COM is tough.
Couldn't have said it better myself! It's like treading on very thin ice for me. ;)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], masheen, mikeyww and 148 guests